Hidden Gems of Ruby 1.9

  • 8,616 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • This is a noob as i have not used FFI.
    why Fiddle when we have ruby-ffi?
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
8,616
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
86
Comments
1
Likes
12

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. HELLO!!!
  • 2. ZOMG HAPPY SATURDAY!
  • 3. PEW PEW PEW~!!!
  • 4. Aaron Patterson
  • 5. @tenderlove
  • 6. google 'tenderlove' Might be NSFW
  • 7. AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.
  • 8. Enterprise Developer http://github.com/tenderlove/enterprise
  • 9. ruby core committer
  • 10. rails core committer
  • 11. rails committer
  • 12. My Failures
  • 13. rails core committer
  • 14. rails committer
  • 15. Presenting Last
  • 16. I like Ryan Davis
  • 17. My Slides Suck (Sorry Shane)
  • 18. minitest
  • 19. RFC 2119 common.rspec
  • 20. No Parens on Method Definitions
  • 21. def foo bar, baz ... end
  • 22. Ruby + JavaScript
  • 23. Ruby + C
  • 24. My Failures (as a presenter)
  • 25. Fun
  • 26. Practical
  • 27. I am a nerd
  • 28. I like boring things
  • 29. 2 Presentations
  • 30. Practical
  • 31. Fun!
  • 32. The Fun
  • 33. Your Guide to Presentation Popularity!
  • 34. Your Guide to Presentation Notoriety!
  • 35. •Provocative Title • Risqué Photos •Ruby Code?
  • 36. Provocative Title:
  • 37. Use Ruby 1.9 like an Engineer
  • 38. Use Ruby 1.9 like a SEXY Engineer
  • 39. Risqué Photos
  • 40. America's Next Top Model
  • 41. America's Next Top Engineer
  • 42. Confident
  • 43. Elegant
  • 44. Sultry
  • 45. Sexy
  • 46. Thoughtful
  • 47. Fierce
  • 48. Playful
  • 49. Powerful
  • 50. Provocative
  • 51. Ruby Code?
  • 52. protected def method_missing(method, *args, &block) if Array.method_defined?(method) to_a.send(method, *args, &block) elsif @klass.scopes[method] merge(@klass.send(method, *args, &block)) elsif @klass.respond_to?(method) scoping { @klass.send(method, *args, &block) } elsif arel.respond_to?(method) arel.send(method, *args, &block) elsif match = DynamicFinderMatch.match(method) attributes = match.attribute_names super unless @klass.send(:all_attributes_exists?, attributes) if match.finder? find_by_attributes(match, attributes, *args) elsif match.instantiator? find_or_instantiator_by_attributes(match, attributes, *args, &block) end else super end end private def references_eager_loaded_tables? # always convert table names to downcase as in Oracle quoted table names are in uppercase joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq (tables_in_string(to_sql) - joined_tables).any? end
  • 53. TL;DR
  • 54. The Practical
  • 55. Hidden Gems of Ruby 1.9
  • 56. Ruby 1.9 PSA
  • 57. minitest
  • 58. require 'minitest/autorun' class FooTest < MiniTest::Unit::TestCase WIN32 = true def test_foo assert_equal 'foo', 'foo' end def test_refutation refute_equal 'foo', 'bar' end def test_skip return skip if WIN32 assert_equal 'fun!', 'fun!' end end
  • 59. require 'minitest/autorun'
  • 60. Test::Unit::TestCase => MiniTest::Unit::TestCase
  • 61. class FooTest < MiniTest::Unit::TestCase end
  • 62. assert_not_* => refute_*
  • 63. def test_refutation refute_equal 'foo', 'bar' end
  • 64. skip
  • 65. class FooTest < MiniTest::Unit::TestCase WIN32 = true def test_skip skip if WIN32 assert_equal 'fun!', 'fun!' end end
  • 66. Loaded suite footest Started .S. Finished in 0.000682 seconds. 1) Skipped: test_skip(FooTest) [footest.rb:15]: Skipped, no message given 3 tests, 2 assertions, 0 failures, 0 errors, 1 skips
  • 67. randomization
  • 68. class FailTest < MiniTest::Unit::TestCase @@foos = %w{ hello } def test_equality assert_equal @@foos, %w{ hello } end def test_append @@foos << "world" assert_equal @@foos, %w{ hello world } end end
  • 69. Test run options: --seed 31149 Loaded suite failtest Started .. Finished in 0.000604 seconds. 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 31149
  • 70. Test run options: --seed 29650 Loaded suite failtest Started .F Finished in 0.000637 seconds. 1) Failure: test_equality(FailTest) [failtest.rb:7]: Expected ["hello", "world"], not ["hello"]. 2 tests, 2 assertions, 1 failures, 0 errors, 0 skips Test run options: --seed 29650
  • 71. Test run options: --seed 29650 Loaded suite failtest Started .F Finished in 0.000637 seconds. 1) Failure: test_equality(FailTest) [failtest.rb:7]: Expected ["hello", "world"], not ["hello"]. 2 tests, 2 assertions, 1 failures, 0 errors, 0 skips Test run options: --seed 29650
  • 72. --seed 29650
  • 73. -v
  • 74. ruby failtest.rb --seed 29650 -v
  • 75. Test run options: --seed 29650 --verbose Loaded suite failtest Started FailTest#test_append: 0.00 s: . FailTest#test_equality: 0.00 s: F Finished in 0.000735 seconds. 1) Failure: test_equality(FailTest) [failtest.rb:7]: Expected ["hello", "world"], not ["hello"]. 2 tests, 2 assertions, 1 failures, 0 errors, 0 skips Test run options: --seed 29650 --verbose
  • 76. Test run options: --seed 29650 --verbose Loaded suite failtest Started FailTest#test_append: 0.00 s: . FailTest#test_equality: 0.00 s: F Finished in 0.000735 seconds. 1) Failure: test_equality(FailTest) [failtest.rb:7]: Expected ["hello", "world"], not ["hello"]. 2 tests, 2 assertions, 1 failures, 0 errors, 0 skips Test run options: --seed 29650 --verbose
  • 77. Test Performance
  • 78. class FooTest < MiniTest::Unit::TestCase def test_foo assert_equal 'foo', 'foo' end def test_refutation refute_equal 'foo', 'bar' end def test_slow sleep 10 end end
  • 79. class FooTest < MiniTest::Unit::TestCase def test_foo assert_equal 'foo', 'foo' end def test_refutation refute_equal 'foo', 'bar' end def test_slow sleep 10 end end
  • 80. Test run options: --seed 33095 --verbose Loaded suite footest Started FooTest#test_slow: 10.00 s: . FooTest#test_refutation: 0.00 s: . FooTest#test_foo: 0.00 s: . Finished in 10.001114 seconds. 3 tests, 2 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 33095 --verbose
  • 81. Test run options: --seed 33095 --verbose Loaded suite footest Started FooTest#test_slow: 10.00 s: . FooTest#test_refutation: 0.00 s: . FooTest#test_foo: 0.00 s: . Finished in 10.001114 seconds. 3 tests, 2 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 33095 --verbose
  • 82. With Rake:
  • 83. rake test TESTSOPTS='-v'
  • 84. rspec
  • 85. describe 'Awesome' do describe 'Class' do it 'discovers something AMAZING' do (10 + 10).must_equal 20 end it 'matches something AMAZING' do "vuvuzela".must_match /vuvu/ end it 'raises something AMAZING' do lambda { raise }.must_raise(RuntimeError) end end end
  • 86. rspec
  • 87. minitest/spec
  • 88. require 'minitest/spec' require 'minitest/autorun'
  • 89. require 'minitest/spec' require 'minitest/autorun' describe 'Awesome' do describe 'Class' do it 'discovers something AMAZING' do (10 + 10).must_equal 20 end it 'matches something AMAZING' do "vuvuzela".must_match /vuvu/ end it 'must raise something' do lambda { raise }.must_raise(RuntimeError) end end end
  • 90. ObjectSpace
  • 91. ObjectSpace.each_object do |obj| p obj end
  • 92. require 'objspace' • count_objects_size • memsize_of • count_nodes • count_tdata_objects
  • 93. count_object_size require 'objspace' hash = {} ObjectSpace.count_objects_size(hash) p hash # => {:T_CLASS=>291520, :T_MODULE=>42512, :T_STRING=>26133, :T_REGEXP=>11501, :T_ARRAY=>5896, :T_HASH=>1088, :T_FILE=>9056, :T_DATA=>1144348, :TOTAL=>1532054}
  • 94. count_object_size require 'objspace' hash = {} ObjectSpace.count_objects_size(hash) p hash # => {:T_CLASS=>291520, :T_MODULE=>42512, :T_STRING=>26133, :T_REGEXP=>11501, :T_ARRAY=>5896, :T_HASH=>1088, :T_FILE=>9056, :T_DATA=>1144348, :TOTAL=>1532054}
  • 95. memsize_of require 'objspace' require 'fiddle' cl = Fiddle::Closure.new(0, [1]) p ObjectSpace.memsize_of(cl) # => 232
  • 96. memsize_of require 'objspace' require 'fiddle' cl = Fiddle::Closure.new(0, [1]) p ObjectSpace.memsize_of(cl) # => 232
  • 97. Implementation
  • 98. struct rb_data_type_struct struct rb_data_type_struct { const char *wrap_struct_name; struct { void (*dmark)(void*); void (*dfree)(void*); size_t (*dsize)(const void *); void *reserved[2]; /* For future extension. This array *must* be filled with ZERO. */ } function; const rb_data_type_t *parent; void *data; /* This area can be used for any purpose by a programmer who define the type. */ };
  • 99. struct rb_data_type_struct struct rb_data_type_struct { const char *wrap_struct_name; struct { void (*dmark)(void*); void (*dfree)(void*); size_t (*dsize)(const void *); void *reserved[2]; /* For future extension. This array *must* be filled with ZERO. */ } function; const rb_data_type_t *parent; void *data; /* This area can be used for any purpose by a programmer who define the type. */ };
  • 100. static size_t my_memsize(const void *p) { return 10; } const rb_data_type_t my_data_type = { "my_extension", {NULL, NULL, my_memsize,}, }; static VALUE allocate(VALUE klass) { struct something * cif; return TypedData_Make_Struct( klass, something, &my_data_type, cif); }
  • 101. count_nodes require 'objspace' p ObjectSpace.count_nodes #=> {:NODE_SCOPE=>50, :NODE_BLOCK=>168, :NODE_IF=>27, :NODE_ITER=>7, ... }
  • 102. count_nodes require 'objspace' p ObjectSpace.count_nodes #=> {:NODE_SCOPE=>50, :NODE_BLOCK=>168, :NODE_IF=>27, :NODE_ITER=>7, ... }
  • 103. count_nodes require 'objspace' 10.times do p ObjectSpace.count_nodes[:NODE_IF] eval 'if true; end' end
  • 104. $ ruby objectspace.rb 27 28 29 30 31 32 33 34 35 36 $
  • 105. count_tdata_objects require 'objspace' p ObjectSpace.count_tdata_objects # => {RubyVM::InstructionSequence=>64, false=>13, ... }
  • 106. count_tdata_objects require 'objspace' p ObjectSpace.count_tdata_objects # => {RubyVM::InstructionSequence=>64, false=>13, ... }
  • 107. count_tdata_objects require 'objspace' require 'fiddle' 10.times do Fiddle::Closure.new(0, [1]) p ObjectSpace.count_tdata_objects[Fiddle::Closure] end
  • 108. $ ruby objectspace.rb 1 2 3 4 5 6 7 8 9 10 $
  • 109. Fiddle
  • 110. libffi wrapper
  • 111. fiddle + dl
  • 112. Fiddle • Function calls • Closure allocation
  • 113. DL • dlopen() wrapper • memory management
  • 114. Calling Functions
  • 115. • Open dynamic library • Locate function pointer • Wrap function pointer • Call function
  • 116. Wrapping "sin" require 'fiddle' libm = DL.dlopen('libm.dylib') function = Fiddle::Function.new( libm['sin'], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts function.call(90 * Math::PI / 180)
  • 117. Wrapping "sin" require 'fiddle' libm = DL.dlopen('libm.dylib') function = Fiddle::Function.new( libm['sin'], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts function.call(90 * Math::PI / 180)
  • 118. Wrapping "sin" require 'fiddle' libm = DL.dlopen('libm.dylib') function = Fiddle::Function.new( libm['sin'], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts function.call(90 * Math::PI / 180)
  • 119. Wrapping "sin" require 'fiddle' libm = DL.dlopen('libm.dylib') function = Fiddle::Function.new( libm['sin'], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts function.call(90 * Math::PI / 180)
  • 120. Wrapping "sin" require 'fiddle' libm = DL.dlopen('libm.dylib') function = Fiddle::Function.new( libm['sin'], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts function.call(90 * Math::PI / 180)
  • 121. Creating Closures
  • 122. double (func *)(double)
  • 123. require 'fiddle' class MySin < Fiddle::Closure def call number Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) puts function.call(90 * Math::PI / 180)
  • 124. require 'fiddle' class MySin < Fiddle::Closure def call(number) Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) puts function.call(90 * Math::PI / 180)
  • 125. require 'fiddle' class MySin < Fiddle::Closure def call(number) Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) puts function.call(90 * Math::PI / 180)
  • 126. require 'fiddle' class MySin < Fiddle::Closure def call(number) Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) puts function.call(90 * Math::PI / 180)
  • 127. Using our Closure
  • 128. class MySin < Fiddle::Closure def call number Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) cfunc = Fiddle::Function.new( function, [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts cfunc.call(90 * Math::PI / 180)
  • 129. class MySin < Fiddle::Closure def call number Math.sin(number) end end function = MySin.new( Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE] ) cfunc = Fiddle::Function.new( function, [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE ) puts cfunc.call(90 * Math::PI / 180)
  • 130. Fiddle Masquerade
  • 131. ruby-ffi code module Tidy extend FFI::Library ffi_lib "libtidy.dylib" attach_function :tidyFileExists, [:string], :int attach_function :tidyCreate, [], :pointer attach_function :tidyParseString, [:pointer, :string], :int attach_function :tidySaveStdout, [:pointer], :int end tdoc = Tidy.tidyCreate Tidy.tidyParseString tdoc, "<title>Foo</title" Tidy.tidySaveStdout tdoc
  • 132. In terms of Fiddle
  • 133. module FFI module Library TYPE_MAP = { :string => DL::TYPE_VOIDP, :pointer => DL::TYPE_VOIDP, } DL.constants.each do |const| next unless const.to_s =~ /^TYPE_/ name = const.to_s.split('_', 2).last.downcase.to_sym TYPE_MAP[name] = DL.const_get(const) end def ffi_lib(lib) @lib = DL::Handle.new lib end def attach_function(name, args, ret) func = Fiddle::Function.new( @lib[name.to_s], args.map { |x| TYPE_MAP[x] }, TYPE_MAP[ret] ) define_singleton_method(name) { |*args| func.call(*args) } end end end
  • 134. ruby-ffi code module Tidy extend FFI::Library ffi_lib "libtidy.dylib" attach_function :tidyFileExists, [:string], :int attach_function :tidyCreate, [], :pointer attach_function :tidyParseString, [:pointer, :string], :int attach_function :tidySaveStdout, [:pointer], :int end tdoc = Tidy.tidyCreate Tidy.tidyParseString tdoc, "<title>Foo</title" Tidy.tidySaveStdout tdoc
  • 135. Fiddle code module Tidy extend FFI::Library ffi_lib "libtidy.dylib" attach_function :tidyFileExists, [:string], :int attach_function :tidyCreate, [], :pointer attach_function :tidyParseString, [:pointer, :string], :int attach_function :tidySaveStdout, [:pointer], :int end tdoc = Tidy.tidyCreate Tidy.tidyParseString tdoc, "<title>Foo</title" Tidy.tidySaveStdout tdoc
  • 136. Psych
  • 137. YAML Parser • 1.9.2 and up • Wraps libyaml • Replaces Syck • Opt-in
  • 138. Opt-in Process $ irb irb(main):001:0> require 'yaml' => true irb(main):002:0> YAML::ENGINE.syck? => true irb(main):003:0> YAML::ENGINE.yamler = 'psych' => "psych" irb(main):004:0> YAML::ENGINE.syck? => false irb(main):005:0>
  • 139. require 'psych'
  • 140. Parsing & Dumping require 'psych' Psych.load('--- hello world!') # => 'hello world!' Psych.dump('hello world!') # => '--- hello world!' 'hello world!'.to_yaml # => '--- hello world!'
  • 141. JSON Psych.load("['hello', 'world!']n") # => ["hello", "world!"] Psych.to_json(%w{ hello world! }) # => "['hello', 'world!']n"
  • 142. JSON Disclaimer
  • 143. Evented Parsing
  • 144. Evented Parsing class MyHandler < Psych::Handler def start_sequence(*args) puts "open [" end def end_sequence(*args) puts "close ]" end def scalar(value, anchor, tag, plain, quoted, style) puts value end end
  • 145. Evented Parsing parser = Psych::Parser.new(MyHandler.new) parser.parse(StringIO.new("['foo', 'bar']"))
  • 146. Evented Parsing $ ruby yml.rb open [ foo bar close ] $
  • 147. Psych::Parser#parse(io_or_string)
  • 148. Evented Emitting
  • 149. (the hard way)
  • 150. Psych::Emitter emitter = Psych::Emitter.new($stdout) emitter.start_stream(Psych::Parser::UTF8) emitter.start_document([], [], false) emitter.start_sequence(nil, nil, false, 1) 10.times { emitter.scalar('hello world', nil, nil, false, true, 1) } emitter.end_sequence emitter.end_document true emitter.end_stream
  • 151. --- - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world' - 'hello world'
  • 152. Read Psych::Handler
  • 153. Streamed Emitting
  • 154. (the easy way)
  • 155. Psych::Stream emitter = Psych::Stream.new($stdout) emitter.start emitter.push %w{ one two } emitter.push %w{ three four } emitter.finish
  • 156. Psych::Stream emitter = Psych::Stream.new($stdout) emitter.start emitter.push %w{ one two } emitter.push %w{ three four } emitter.finish
  • 157. Psych::Stream $ ruby yml.rb --- - one - two ... --- - three - four ...
  • 158. Problem?
  • 159. Streaming JSON
  • 160. Psych::Stream emitter = Psych::Stream.new($stdout) emitter.start emitter.push %w{ one two } emitter.push %w{ three four } emitter.finish
  • 161. Psych::JSON::Stream emitter = Psych::JSON::Stream.new($stdout) emitter.start emitter.push %w{ one two } emitter.push %w{ three four } emitter.finish
  • 162. Psych::JSON::Stream emitter = Psych::JSON::Stream.new($stdout) emitter.start emitter.push %w{ one two } emitter.push %w{ three four } emitter.finish
  • 163. Psych::JSON::Stream --- ['one', 'two'] ... --- ['three', 'four'] ...
  • 164. Uses?
  • 165. More Info
  • 166. THE END
  • 167. Questions?
  • 168. Coverage
  • 169. Methods • Coverage.start • Coverage.result
  • 170. a.rb ### # It's my class! class Foo def initialize @thought = 0 10.times { @thought += 1 # thinking } end def rested? if @thought > 8 false else true end end end Foo.new.rested?
  • 171. a.rb ### # It's my class! class Foo def initialize @thought = 0 10.times { @thought += 1 # thinking } end def rested? if @thought > 8 false else true end end end Foo.new.rested?
  • 172. a.rb ### # It's my class! class Foo def initialize @thought = 0 10.times { @thought += 1 # thinking } end def rested? if @thought > 8 false else true end end end Foo.new.rested?
  • 173. a.rb ### # It's my class! class Foo def initialize @thought = 0 10.times { @thought += 1 # thinking } end def rested? if @thought > 8 false else true end end end Foo.new.rested?
  • 174. require 'coverage' Coverage.start require 'a' p Coverage.result
  • 175. require 'coverage' Coverage.start require 'a' p Coverage.result
  • 176. Coverage.result {"/Users/apatterson/git/code/a.rb"=>[nil, nil, 1, 1, 1, 1, 10, nil, nil, nil, 1, 1, 1, nil, 0, nil, nil, nil, nil, 1]}
  • 177. We've Learned
  • 178. We've Learned • Lines executed
  • 179. We've Learned • Lines executed • # times a line was executed
  • 180. We've Learned • Lines executed • # times a line was executed • Lines that can't be executed
  • 181. We can deduce
  • 182. We can deduce • Coverage
  • 183. We can deduce • Coverage • Hotspots (code heatmap)
  • 184. SimpleCov http://github.com/colszowka/simplecov
  • 185. Read More Here! http://bit.ly/19coverage