Ruby scratchpad
From WhyNotWiki
Maybe make a Ruby / Examples "namespace" ?
can tack on an unless even to the end of a big old block!
case method
when :capture
`#{command}`
when :exec
#Kernel.exec *args
Kernel.exec command
when :popen
# To do: rather than `...`, which waits until command is finished before you get any output, maybe do popen and output the output in realtime!
IO.popen(command, 'r') do |pipe|
pipe.read
end
else
raise ArgumentError.new(":method option must be one of #{valid_options.inspect}")
end unless Subversion.dry_run
begin/end example
can substitute an *expression* anytime you have a statement...
even if it's somewhere you wouldn't ordinarily think you can put multiple commands... for example in an argument to a method call
you can still put them there...!
just wrap your commands in a begin/end "block"(?)
for example, this is completely legal:
blah.meth1 arg1, arg2, begin a += 3; a*=9; a end, arg 4
# a will be modified by that call -- i.e., the method call has "side-effects"
change strlen example to -> Enum#max ?
pretty cool
define_method block or eval block rather than
eval *string*
see http://asqyzeron.wordpress.com/2007/01/14/toying-with-the-dynamic-features-of-ruby-adding-methods-at-run-time/
the power of closures...
local
blah {
local
}
splat operator example
can make a wrapper method that passes along all args (even if the method you are wrapping accepts a variable number of them)
def st(*args); status(*args); end
iterators usually return sane values which lets you string them together in reasonable ways
irb -> {:a=>1,:b=>2}.each_key { |key| key }
=> {:a=>1, :b=>2}
irb -> {:a=>1,:b=>2}.each_key { |key| puts key }.each_key { |key| puts key.to_s.capitalize }
a
b
A
B
=> {:a=>1, :b=>2}
procs give you pretty much all the power that C++ gives you with pointers ... (only procs are safer!)
example:
Hash.new lets you specify what the default value is if you try to reference an element of the hash that doesn't exist
normally you'd pass an object/value to it
h = Hash.new("Go Fish")
h["c"] #=> "Go Fish"
but -- get this -- you can pass a *proc* -- (which, of course, can be bound to any binding that you want)
and instead of just returning what the value was at the time you called Hash.new, with a proc, you can have it actually go and *look up* what the value should be...
http://ruby-doc.org/core/classes/Hash.html#M002873
If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block‘s responsibility to store the value in the hash if required.
you could even have it use *the current value* of some variable (as opposed to the value that it had when Hash.new was called), just by wrapping that variable in a closure...
irb -> x = 3
=> 3
irb -> h = Hash.new(x)
=> {}
irb -> h[nil]
=> 3
irb -> x = 4
=> 4
irb -> h[nil]
=> 3 # not 4
irb -> # But!
h = Hash.new { |hash, key| x }
=> {}
irb -> h['foo']
=> 4
irb -> x = 19
=> 19
irb -> h['bar']
=> 19
So you can see that blocks/closures let you pass around *more* than just behavior -- you can also pass around an [execution context / binding]!!
demonstration showing that Ruby's variables are by-reference, they reference an object -- and multiple things can provide a reference to that object (allowing that object to be modified in perhaps unforeseen ways! -- danger!)
http://ruby-doc.org/core/classes/Hash.html#M002873
# The following alters the single default object
h["c"].upcase! #=> "GO FISH"
h["d"] #=> "GO FISH"
h.keys #=> ["a", "b"]
side effect of "" being treated as true and of nil.to_s being "" (true) (and false.to_s being "false" (true))
not usually, but in this particular I wanted "" to be treated as false
@subcommand = @subcommand_aliases[@subcommand.to_sym].to_s || @subcommand
So if @subcommand.to_sym can't be found in @subcommand_aliases, I want it to fall back / cancel that assignment and just keep its existing value (assign it to itself)
That doesn't work because if it's not found, it returns nil (the default default value when instantiated with {})...
which by itself would have worked, but I also wanted to convert it to a string...
(woludn't have helped to instantiate hash with Hash.new(false), because false.to_s also evaluates to a true value ("false" specifically))
This works, though, so I can't complain...
@subcommand = (@subcommand_aliases[@subcommand.to_sym] || @subcommand).to_s
Other possible solutions considered:
(not as pretty, does key lookup ([] and has_key?) twice -- duplication!)
@subcommand = @subcommand_aliases[@subcommand.to_sym].to_s if @subcommand_aliases.has_key? @subcommand.to_sym
(not as pretty, uses a temporary variable):
@subcommand = a = @subcommand_aliases[@subcommand.to_sym] && a.to_s || @subcommand.to_s
There's a difference between a method that accepts/expects an *array* to be passed in and a method that accepts/expects a variable number of args (which is still ''received'' as an array via *args splat, but the method call looks a bit different)...
Comparison:
When you have a short number of args and you're actually enumerating them, then this:
Subversion.add('file1', 'file2') is nicer than
Subversion.add(['file1', 'file2'])
That must have been Lance's premise when he wrote the original Subversion module.
But when you have another method that calls that method, it means you have to remember to "unsplat"(?) the args like this
Subversion.add(*args) instead of just
Subversion.add(args)
If you forget to unsplat, you can get some pretty subtle but very incorrect bugs...
irb -> def foo(*args); p args; "Doing something with our first argument, #{args[0]}" end
irb -> foo([1, 2, 3])
[[1, 2, 3]]
=> "Doing something with our first argument, 123"
irb -> foo(*[1, 2, 3])
[1, 2, 3]
=> "Doing something with our first argument, 1"
Problem: this won't end up with options = {}
@as ||= nil
Subversion.externalize(repo_path, @as)
self.externalize(repo_path, options = {})
Solution: duplicate default values in your user class...
@as ||= {} # duplication
Subversion.externalize(repo_path, @as)
Solution: new language construct/core class ?
@as ||= DefaultValue
Subversion.externalize(repo_path, @as)
will cause method to use default of options = {}
ruby conventions : whitespace
assert_equal [
"svn propget svn:ignore gemables/calculator/lib",
"svn propset svn:ignore 'calculator.rb' gemables/calculator/lib"
], Subversion.executed
(yes)
or
assert_equal [
"svn propget svn:ignore gemables/calculator/lib",
"svn propset svn:ignore 'calculator.rb' gemables/calculator/lib"
], Subversion.executed
getting user input
irb -> $stdin.getc.chr
w => "w"
irb -> $stdin.gets
asnuth
=> "asnuth\n"
locals first set from inside a block are not available outside of the block
def test_1
simulate_input('foo') { input_received = $stdin.getc.chr }
assert_equal 'f', input_received
end
NameError: undefined local variable or method `input_received' for #<TheTest:0xb7ec6154>
/home/tyler/code/gemables/our_extensions/lib/simulate_input.rb:39:in `test_1'
but
def test_1
input_received = nil
simulate_input('foo') { input_received = $stdin.getc.chr }
assert_equal 'f', input_received
end
even loops and break statements return values!
irb -> loop do
break 3
end
=> 3
break, like return, accepts an argument
Strange behavior: ret_val seems to live in the scope of the if statement unless we initialize it outside.
irb -> i = 0
=> 0
irb -> loop do
i += 1
puts i
if i < 3
ret_val = i
else
break ret_val
end
end
1
2
3
=> nil # not expected!
irb -> i, ret_val = 0, 0
=> [0, 0]
irb -> loop do
i += 1
puts i
if i < 3
ret_val = i
else
break ret_val
end
end
1
2
3
=> 2 # expected!
irb -> loop do i += 1; ret_val ||= 0; puts i; if i < 3; ret_val = i; else; break ret_val; end; end
1
2
3
=> 0 # not expected!
equivalent/implementation of to_b:
def to_b
!!self
Facets does provide this
http://facets.rubyforge.org/src/doc/rdoc/core/classes/Object.html#M000666
require 'facets/core/kernel/to_b'
Take your pick:
show_lines = !!$*.delete('--lines') || false
or
show_lines = $*.delete('--lines').to_b
irb -> nil ==false
=> false
irb -> "yep" if nil
=> nil
irb -> "yep" if !nil
=> "yep"
irb -> "yep" if nil == false
=> nil
In most languages, if something isn't true, then it must be false... not so in Ruby!!!!!!!!!!!!!
Use !! to explicitly convert something to true/false...
irb -> "yep" if !!nil == false
=> "yep"
String.split with Regexp
http://www.rubycentral.com/book/tut_stdtypes.html
file, length, name, title = line.chomp.split(/\s*\|\s*/)
mins, secs = length.split(/:/)
file, length, name, title = line.chomp.split(/\s*\|\s*/)
gsub can accept blocks! powerful!!!
http://www.rubycentral.com/book/tut_stdtypes.html
a = "the quick brown fox"
a.sub(/^./) { $&.upcase } » "The quick brown fox"
a.gsub(/[aeiou]/) { $&.upcase } » "thE qUIck brOwn fOx"
before_externals, *externals = input.split(/^Performing status on external item at.*$/)
Caveat!! () are necessary more often than you think!
This does *not* do what you expect:
output.puts (' ' + path + ' ').black_on_white.bold.underline
in addition to giving a warning ( warning: don't put space before argument parentheses ), it results in a NoMethodError
because output.puts (' ' + path + ' ') results in nil !
not the order of evaluation/associativity you were expecting, huh?!
(puts '1'; puts '2') if true
equivalent to
begin
puts '1'
puts '2'
end if true
?
If so, it's a lot conciser!
dsl idea for file searching
Find(*.rb).include.contains(':todo:').exclude.filename(/all/).exclude.contains('Took care of?: yes')
Example of: [Iterators (category)] [returning command (category)] [Enumerable#max (category)], Ruby syntax (braces, do/end, multi-line statements), style (breaking long statements/expressions into multiple lines), ...
def externals_items(directory = "./")
longest_path_name = 25
externals_structs = Subversion.externals_containers(directory).map do |external|
returning(
external.entries_structs.map do |entry|
Struct.new(:path, :repository_path).new(
File.join(external.container_dir, entry.name).relativize_path,
entry.repository_path
)
end
) do |entries_structs|
longest_path_name =
[
longest_path_name,
entries_structs.map { |entry|
entry.path.size
}.max
].max
end
end
puts '(Use the -o/--omit-repository-path option if you just want the external paths/names without the repository paths)' unless @omit_repository_path
puts externals_structs.map { |entries_structs|
entries_structs.map { |entry|
entry.path.ljust(longest_path_name + 1) +
(@omit_repository_path ? '' : entry.repository_path)
}
}
end
ways to namespace:
modules
MyNamespace.meth1
prefix
my_namespace_meth1
Facets namespace "method" method
my_namespace.meth1
annoyance:
when scripts rescue errors and print out the error message but don't give a backtrace so if you want to debug it you have no
idea where to look
and if you want to see the backtrace, you have no idea where the exception is being rescued so that you can stop rescuing it
conventions:
Getting the line number right...
module_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{name}( *args, &block )
super(
*fu_merge_option(args,
#{default_options.join(', ')}
), &block)
end
EOS
conventions:
def meth(*cmd, &block)
if Hash === cmd.last then
options = cmd.pop
else
options = {}
end
A way to avoid duplication...
./vendor/rails/actionpack/lib/action_controller/rescue.rb
def local_request?
[request.remote_addr, request.remote_ip] == ["127.0.0.1"] * 2
end
since we can't do this...
def local_request?
request.remote_addr == request.remote_ip == "127.0.0.1"
end
and we don't want to duplicate, like this:
request.remote_addr == "127.0.0.1" and request.remote_ip == "127.0.0.1"
maybe this would be even better?
[request.remote_addr, request.remote_ip, "127.0.0.1"].all_equal?
Ruby provides ways / makes it easier to eliminate duplication!
One of the ways it makes this possible is with the fact that all statements (including if/else) return a value, and you can call methods directly on the result of the if statement...
Instead of doing this (terrible duplication, with .sort.map(&:to_sym) being called in two places):
def methods_with_sorting_and_include_super(include_super = true)
if include_super
methods_without_sorting_and_include_super.sort.map(&:to_sym)
else
((methods_without_sorting_and_include_super - Object.methods_without_sorting_and_include_super)).sort.map(&:to_sym)
end
end
or this (better, but we still have some duplication with the temporary variable assignment in each block of the if statement) (the best you can do in most languages):
def methods_with_sorting_and_include_super(include_super = true)
if include_super
return_value = methods_without_sorting_and_include_super
else
return_value = (methods_without_sorting_and_include_super - Object.methods_without_sorting_and_include_super)
end
return return_value.sort.map(&:to_sym)
end
, I was able to just do this:
def methods_with_sorting_and_include_super(include_super = true)
if include_super
methods_without_sorting_and_include_super
else
(methods_without_sorting_and_include_super - Object.methods_without_sorting_and_include_super)
end.sort.map(&:to_sym)
end
(~/code/gemables/qualitysmith_extensions/lib/qualitysmith_extensions/object/methods.rb)
