Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fun with JRuby (and monkey patching)

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →
Avatar for vivo vivo
April 29, 2012

Fun with JRuby (and monkey patching)

Avatar for vivo

vivo

April 29, 2012

Other Decks in Technology

Transcript

  1. MONKEY P A T C H I N G FUN

    WITH (J)RUBY (c) 2011 Victor Volle ([email protected])
  2. "WHY PROGRAM BY HAND IN FIVE DAYS WHAT YOU CAN

    SPEND FIVE YEARS OF YOUR LIFE AUTOMATING?" TERENCE PARR (c) 2011 Victor Volle ([email protected])
  3. 2011-08-15 10:53:07,179 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,191 [||94] INFO

    tools.subsys.Handler ... 2011-08-15 10:53:07,201 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,212 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,214 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,218 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,226 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,227 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,229 [||94] INFO translator.bar.Hrglbrmft ... HOW TO GET FROM HERE TO THERE ... 1. Separate different invocations/threads 2. Determine which log file lines belong to which layer 3. Calculate length of each invocation 4. Visualize ✔ ✔ ✔ (c) 2011 Victor Volle ([email protected])
  4. #!/usr/bin/perl use strict; my %currentDialogs = {}; my($dialogCount) = 0;

    my($reuseThread) = 0; while (<>) { my($line) = $_; my($newLine); if ( $line =~ m/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2},\d{3}) \[([5-8])\] \[(\d*)\| (.*)\|([0-9.]*)\] (.*)/) { my($date) = $1; my($time) = $2; my($server) = $3; my($userId) = $4; my($dialogId) = $5; my($threadId) = $6; my($rest) = $7; $time =~ m/(\d{2}):(\d{2}):(\d{2}),(\d{3})/; my($hour) = $1; my($minute) = $2; my($second) = $3; my($millisecond) = $4; $threadId =~ /(\d*)(\.\d*)?/; my($baseThreadId) = $1; my($subThreadId) = $2; my($threadOnlyKey) = "$server-$baseThreadId"; my($dialogIdKey) = "$dialogId"; if ( $rest =~ /INFO translator.bar.Hrglbrmft - > \[/ ) { $dialogCount++; if ( $dialogCount > 2000 ) { my($key, $value); my(@allInterestingGVOs) = (...); while(($key, $value) = each(%currentDialogs)) { foreach(@allInterestingGVOs) { my($anInterestingGVO) = $_; if ( $value =~ /\[($anInterestingGVO)/s ) { mkdir "$anInterestingGVO"; my($filename) = "$anInterestingGVO/$key.txt"; open (MYFILE, "> $filename") || die "cannot open $filename\n"; print MYFILE $value || die "cannot write $filename\n"; close(MYFILE); } } } exit; } if ( $currentDialogs{$threadOnlyKey} ne "" ) { $reuseThread++; if ( $reuseThread > 1 ) { exit; } } $currentDialogs{$threadOnlyKey} = $line; } elsif ( $dialogCount > 0 ) { if ( $dialogId ne "" ) { if ( $currentDialogs{$dialogIdKey} ) { $currentDialogs{$dialogIdKey} = "$currentDialogs{$dialogIdKey}$line"; } elsif ( $currentDialogs{$threadOnlyKey} ) { $currentDialogs{$dialogIdKey} = "$currentDialogs{$threadOnlyKey}$line"; $currentDialogs{$threadOnlyKey} = $dialogIdKey; } } elsif ( $threadId ne "" ) { if ( $rest =~ /INFO tools.subsys.Handler - Context\[([^]][^]]*)\]/ ) { $dialogId = $1; $dialogIdKey = "$dialogId"; if ( $currentDialogs{$threadOnlyKey} ne $dialogIdKey ) { $currentDialogs{$dialogIdKey} = "$currentDialogs{$dialogIdKey}$currentDialogs{$threadOnlyKey}$line"; $currentDialogs{$threadOnlyKey} = $dialogIdKey; } else { $currentDialogs{$dialogIdKey} = "$currentDialogs{$dialogIdKey} $line"; } } else { my($potDialogIdKey) = $currentDialogs{$threadOnlyKey}; if ( $potDialogIdKey =~ /^.*%$/ ) { $currentDialogs{$potDialogIdKey} = "$currentDialogs{$potDialogIdKey}$line"; } else { if ( $currentDialogs{$threadOnlyKey} ) { $currentDialogs{$threadOnlyKey} = "$currentDialogs{$threadOnlyKey}$line"; } } } } } } } ✔ ✔ ✔ (c) 2011 Victor Volle ([email protected])
  5. 2011-08-15 10:53:07,179 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,191 [||94] INFO

    tools.subsys.Handler ... 2011-08-15 10:53:07,201 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,212 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,214 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,218 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,226 [||94] INFO tools.subsys.Handler ... 2011-08-15 10:53:07,227 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,228 [||94] INFO translator.bar.Hrglbrmft ... 2011-08-15 10:53:07,229 [||94] INFO translator.bar.Hrglbrmft ... HOW TO GET FROM HERE TO THERE ... 1. Separate different invocations/threads 2. Determine which log file lines belong to which layer 3. Calculate length of each invocation 4.Visualize? ✔ ✔ ✔ (c) 2011 Victor Volle ([email protected])
  6. • JFreeChart • better for charts • GnuPlot • better

    for charts (and scientific data) as well • GraphViz • better, if you need an automatic layout • Plain Old Java • Cumbersome and boring (yes, that is an important criteria) CANDIDATES (c) 2011 Victor Volle ([email protected])
  7. FURTHER REQUIREMENTS what if we could do something like ...

    foo.left = rectangle1.right foo.width = 200 and foo.right would be calculated (or vice versa) and if we could do something like ... foo.below(rectangle1, 60) foo 60px (c) 2011 Victor Volle ([email protected])
  8. CANDIDATES • Groovy 㱺 very Java-like, interesting. Tried it, works,

    boring. • Clojure 㱺 never did grok Lisp • Ruby 㱺 aah, always wanted to really learn Ruby! • Scala flatMap [B, That] (f: (A) ⇒ Traversable[B])(implicit bf: CanBuildFrom[List[A], B, That]) : That 㱺 this is a method signature, no thanks, next time perhaps (c) 2011 Victor Volle ([email protected])
  9. HELLO WORLD def hallo(name) puts "Hello #{name}" end hello_world.rb method

    declaration method name parameters (“Look Ma: No Types!”) System.out.println... variable extrapolation (c) 2011 Victor Volle ([email protected])
  10. CLASSES class GraphObject :attr_accessor :name def initialize(name) @name = name

    @id = ... end def begin_x ... end def end_x ... end def width ... end end graph_object.rb class name file name generate getter&setter public methods instance variable (no declaration necessary) ”constructor” (somewhat) (c) 2011 Victor Volle ([email protected])
  11. OBJECTS go = GraphObject.new("foo") puts go ==> #<FooBar:0x1996e136> allocates memory

    and calls initialize("foo") no to_s method yet, just print the object id (c) 2011 Victor Volle ([email protected])
  12. require "test/unit" require "shoulda-context" require "graph_object" class GraphObjectTest < Test::Unit::TestCase

    should "be able to calculate bottom and center, if top and height are given" do go = GraphObject.new() go.top_y = 10 go.height = 30 assert_equal(40, go.bottom_y) assert_equal(25, go.center_y) end end TESTS graph_object_test.rb “import” “extends” namespace method invocation first parameter (String) second parameter (a Block) (c) 2011 Victor Volle ([email protected])
  13. class Foo puts "here" def begin_x ... end end A

    CLASS IS CODE foo.rb $> ruby foo.rb here (c) 2011 Victor Volle ([email protected])
  14. File.readlines(file).each do |line| puts line end BLOCKS a block “parameter”

    gets invoked for each line in the file (c) 2011 Victor Volle ([email protected])
  15. File.readlines(file).each do |line| puts line end BLOCKS map = {

    "a" => 100, "b" => 200 } map.each do |key, value| puts "#{key} is #{value}" end a block “parameter” gets invoked for each line in the file multiple parameter are possible (c) 2011 Victor Volle ([email protected])
  16. class Array def find for i in 0...size value =

    self[i] return value if yield(value) end return nil end end [1, 3, 5, 7, 9].find {|v| v*v > 30 } ==> 7 BLOCKS invoke the block (c) 2011 Victor Volle ([email protected])
  17. import java.awt.*; ... public class ShowMe extends Component { private

    ApplicationFrame frame; public ShowMe(int width, int height) { setPreferredSize(new Dimension(width, height)); frame = new ApplicationFrame("ShowOff v1.0"); frame.setLayout(new BorderLayout()); frame.add(this, BorderLayout.CENTER); frame.pack(); frame.center(); frame.setResizable(false); frame.setVisible(true); } } FROM JAVA TO ... (c) 2011 Victor Volle ([email protected])
  18. require "java" include_class "java.awt.Component" class ShowMe2 < Component def initialize(width,

    height) super() dimension = Dimension.new(width, height) # preferred_size = dimension self.preferred_size dimension @frame = ApplicationFrame.new("ShowMe") @frame.layout = BorderLayout.new() @frame.add(self, BorderLayout::CENTER) @frame.pack @frame.center @frame.resizable = false @frame.visible = true end end .. JRUBY make “Java” available import java classes problem with deprecated method leftover from JDK 1.1... Java constant! (c) 2011 Victor Volle ([email protected])
  19. DEFINE PROPERTIES it is sufficient to “know” any two properties

    to calculate the other two begin_x end_x center_x width (c) 2011 Victor Volle ([email protected])
  20. if defined? @begin_x if defined? @width @end_x = @begin_x +

    @width ... elsif defined? @center_x ... end end FIRST IDEA: PROPERTIES check which properties are defined calculate the other properties (c) 2011 Victor Volle ([email protected])
  21. b begin_x = a.end_x + 50 a end_x 50px DEPENDENT

    PROPERTIES what if “a.end_x” is not known (yet)? (c) 2011 Victor Volle ([email protected])
  22. IF WE EXACTLY KNOW HOW THE FINAL DIAGRAM SHALL LOOK

    LIKE, ... what if “a.end_x” is not known (yet)? ... WE WILL ALWAYS BE ABLE TO BUILD EACH ELEMENT’S POSITION ON KNOWN POSITIONS OF OTHER ELEMENTS ... (c) 2011 Victor Volle ([email protected])
  23. b begin_x = a.end_x + 50 a end_x 50px LAMBDA

    TO THE RESCUE b.begin_x = lambda { a.end_x + 50 } (c) 2011 Victor Volle ([email protected])
  24. a = Rectangle.new("a") b = Rectangle.new("b") b.width = $defaultWidth b.height

    = $defaultHeight b.top_y = 0 b.begin_x = lambda { a.end_x + 50 } a.width = $defaultWidth a.height = $defaultHeight a.top_y = 0 a.begin_x = 0 ShowGraph.new() A FIRST GRAPH two_rectangles.rb (c) 2011 Victor Volle ([email protected])
  25. a = Rectangle.new("a") b = Rectangle.new("b") b.width = $defaultWidth b.height

    = $defaultHeight b.top_y = 0 b.begin_x = lambda { a.end_x + 50 } a.width = $defaultWidth a.height = $defaultHeight a.top_y = 0 a.begin_x = 0 ShowGraph.new() LAMBDA ... AND INSTANCE VARS two_rectangles.rb b.height = lambda { width * 2 } (c) 2011 Victor Volle ([email protected])
  26. a = Rectangle.new("a") b = Rectangle.new("b") b.width = $defaultWidth b.height

    = $defaultHeight b.top_y = 0 b.begin_x = lambda { a.end_x + 50 } a.width = $defaultWidth a.height = $defaultHeight a.top_y = 0 a.begin_x = 0 ShowGraph.new() LAMBDA ... AND INSTANCE VARS two_rectangles.rb b.height = lambda { width * 2 } NameError: undefined local variable or method `width' for main:Object (root) at /Users/vivo/workspaces/any/vgraph2/vgraph2-base/ src/main/ruby/two_rectangles.rb:16 call at org/jruby/RubyProc.java:274 call at org/jruby/RubyProc.java:229 height at ./graph_object.rb:105 call at org/jruby/RubyProc.java:274 _bottom_y at ./graph_object.rb:217 send at org/jruby/RubyKernel.java:2092 method_missing at ./graph_object.rb:122 bounds at ./graph.rb:22 each at org/jruby/RubyArray.java:1603 bounds at ./graph.rb:20 initialize at ./show_graph.rb:19 (root) at /Users/vivo/workspaces/any/vgraph2/vgraph2-base/ src/main/ruby/two_rectangles.rb:20 load at org/jruby/RubyKernel.java:1063 (root) at -e:1 (c) 2011 Victor Volle ([email protected])
  27. a = Rectangle.new("a") b = Rectangle.new("b") b.width = $defaultWidth b.height

    = $defaultHeight b.top_y = 0 b.begin_x = lambda { a.end_x + 50 } a.width = $defaultWidth a.height = $defaultHeight a.top_y = 0 a.begin_x = 0 ShowGraph.new() LAMBDA ... AND INSTANCE VARS two_rectangles.rb b.height = lambda { width * 2 } width is an instance variable: not in scope! (c) 2011 Victor Volle ([email protected])
  28. ok, we could simply use full references or we need

    to get into the scope of that object ... SECOND IDEA b.height = lambda { width * 2 } b.height = lambda { b.width * 2 } (c) 2011 Victor Volle ([email protected])
  29. ok, we could simply use full references or we need

    to get into the scope of that object ... SECOND IDEA b.height = lambda { width * 2 } b.height = lambda { b.width * 2 } class << b def height; width * 2 end end the class of object ‘b’ (c) 2011 Victor Volle ([email protected])
  30. CLASS/OBJECT MODEL a class class height() Graph- Object @name b

    a Class class class height() Graph- Object @name b class (c) 2011 Victor Volle ([email protected])
  31. CLASS/OBJECT MODEL a class class height() Graph- Object @name b

    a Class class class height() Graph- Object @name b class a Object Class class class height() Graph- Object @name b class super- class (c) 2011 Victor Volle ([email protected])
  32. CLASS/OBJECT MODEL a class class height() Graph- Object @name b

    a Class class class height() Graph- Object @name b class a Object Class class class height() Graph- Object @name b class super- class a Object Class Module class class height() Graph- Object @name b class super- class super- class (c) 2011 Victor Volle ([email protected])
  33. CLASS/OBJECT MODEL a class class height() Graph- Object @name b

    a Class class class height() Graph- Object @name b class a Object Class class class height() Graph- Object @name b class super- class a Object Class Module class class height() Graph- Object @name b class super- class super- class a Object Class Module class class height() Graph- Object @name b class super- class super- class class super- class class (c) 2011 Victor Volle ([email protected])
  34. CLASS/OBJECT MODEL a Object Class Module class class height() Graph-

    Object @name b class super- class super- class class super- class class height() << b super- class (c) 2011 Victor Volle ([email protected])
  35. CLASS/OBJECT MODEL a Object Class Module class class height() Graph-

    Object @name b class super- class super- class class super- class class height() << b super- class a “singleton” class, or “eigenclass” (c) 2011 Victor Volle ([email protected])
  36. ok, we could simply use full references or we need

    to get into the scope of that object ... SECOND IDEA b.height = lambda { width * 2 } b.height = lambda { b.width * 2 } class << b def height; @width * 2 end end the class of object ‘b’ but now ‘height’ is a method (c) 2011 Victor Volle ([email protected])
  37. if defined? @begin_x if defined? @width @end_x = @begin_x +

    @width ... elsif defined? @center_x ... end end FIRST IDEA: PROPERTIES (c) 2011 Victor Volle ([email protected])
  38. def end_x if self.respond_to? :begin_x if self.respond_to? :width begin_x +

    width elsif ... else raise InsufficientDefinitionError ... end ... end end SECOND IDEA: METHODS is the method defined? return value (a symbol) throw ... (c) 2011 Victor Volle ([email protected])
  39. def end_x if self.respond_to? :begin_x if self.respond_to? :width begin_x +

    width elsif ... else raise InsufficientDefinitionError ... end ... end end SECOND IDEA: METHODS but if we define ‘end_x’ ... (c) 2011 Victor Volle ([email protected])
  40. def end_x if self.respond_to? :begin_x if self.respond_to? :width begin_x +

    width elsif ... else raise InsufficientDefinitionError ... end ... end end SECOND IDEA: METHODS (c) 2011 Victor Volle ([email protected])
  41. def begin_x if self.respond_to? :end_x if self.respond_to? :width end_x -

    width elsif ... else raise InsufficientDefinitionError ... end ... end end SECOND IDEA: METHODS this will be true (c) 2011 Victor Volle ([email protected])
  42. def begin_x if self.respond_to? :end_x if self.respond_to? :width end_x -

    width elsif ... else raise InsufficientDefinitionError ... end ... end end SECOND IDEA: METHODS this will be true D’oh! (c) 2011 Victor Volle ([email protected])
  43. we need to define “begin_x”, “end_x” etc. without defining it

    ... ... method_missing SECOND IDEA+ def _begin_x ... def _end_x ... def method_missing(method_symbol, *args, &block) method_name = method_symbol.to_s internal_meth = "_#{method_name}" send internal_meth end method_missing gets invoked whenever a method is called, that is not defined (remember, we use a dynamic language) (c) 2011 Victor Volle ([email protected])
  44. but what about ... SECOND IDEA++ go2.begin_x = lambda {

    go.end_x + $x } go2.width = 100 if width and begin_x are now methods (kinda), can we assign values to them? but width= and begin_x= are methods (c) 2011 Victor Volle ([email protected])
  45. SECOND IDEA++ def begin_x=(value) if value.respond_to? :call self.eigenclass.send(:define_method, “begin_x”) do

    value.call() end else self.eigenclass.send(:define_method, “begin_x”) do value end end end setter a lambda (or something very much alike) creates a new method (c) 2011 Victor Volle ([email protected])
  46. def begin_x=(value) if value.respond_to? :call self.eigenclass.send(:define_method, “begin_x”) do value.call() end

    else self.eigenclass.send(:define_method, “begin_x”) do value end end end SECOND IDEA++ (c) 2011 Victor Volle ([email protected])
  47. def end_x=(value) if value.respond_to? :call self.eigenclass.send(:define_method, “end_x”) do value.call() end

    else self.eigenclass.send(:define_method, “end_x”) do value end end end SECOND IDEA++ since all setters are nearly alike, we again use method_missing (c) 2011 Victor Volle ([email protected])
  48. we define all “properties” as methods like “_begin_x”: WRAP UP

    (1) def _begin_x if self.respond_to? :end_x if self.respond_to? :width ... if “begin_x” is called (and not defined), method_missing will be called: def method_missing(method_symbol, *args, &block) # method_name = method_symbol.to_s ... internal_meth = "_#{method_name}" send internal_meth (c) 2011 Victor Volle ([email protected])
  49. if we ‘set’ a ‘property’: WRAP UP (2) b.begin_x =

    lambda { a.end_x + $x } b.width = 100 if “begin_x” is called (and not defined), method_missing will be called: if value.respond_to? :call self.eigenclass.send(:define_method, getter_name) do value.call() end else # self.eigenclass.send(:define_method, getter_name) do value end end (c) 2011 Victor Volle ([email protected])
  50. MONKEY PATCHING • We can add/replace methods on classes on

    the fly • We can add/replace methods on each objects • We can have methods like ‘findByNameAndAddress” that are implemented on the fly by using “method_missing” • classes are objects, functions are objects • all these are very powerful and dangerous mechanisms (lets replace “wait()” on class “Object” in Java while multiple threads are running ...) (c) 2011 Victor Volle ([email protected])