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

Fun with JRuby (and monkey patching)

Avatar for vivo vivo
April 29, 2012

Fun with JRuby (and monkey patching)

Avatar for vivo

vivo

April 29, 2012
Tweet

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])