otaku 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ === 0.4.0 2010-09-15
2
+
3
+ = Features
4
+ * Use SerializableProc instead of handrolled MagicProc & friends, thus doing away
5
+ with the awkward need for explicitely passing in contextual references [#ngty]
6
+
1
7
  === 0.3.0 2010-08-05
2
8
 
3
9
  = Features
@@ -5,7 +5,7 @@ Dead simple server/client service built using eventmachine.
5
5
  == Introduction
6
6
 
7
7
  Otaku's original intent is to support testing of cross-process stubbing in
8
- cross-stub (http://github.com/ngty/cross-stub). It's usefulness in other
8
+ CrossStub (http://github.com/ngty/cross-stub). It's usefulness in other
9
9
  aspects of my hacking life prompts me to extract it out, & package it as a
10
10
  generic solution. Its primary intent is to be dead simple to use & easy to
11
11
  customize, of course, both criteria subjected to very my own tastes.
@@ -14,7 +14,7 @@ customize, of course, both criteria subjected to very my own tastes.
14
14
 
15
15
  It's hosted on rubygems.org:
16
16
 
17
- $ sudo gem install otaku
17
+ $ gem install otaku
18
18
 
19
19
  == Using It
20
20
 
@@ -33,32 +33,29 @@ It's hosted on rubygems.org:
33
33
  Otaku.process('hello')
34
34
  # >> '~ hello ~'
35
35
 
36
- === Unfortunately ...
36
+ === Wait a minute, how abt contextual references ??
37
37
 
38
- Most of the times, we won't have anything as simple as above, the following
39
- illustrates the problem of contextual reference:
38
+ Previously, when there is references to local variables outside the proc, we need
39
+ to start Otaku in a very awkward manner:
40
40
 
41
- mark = '*'
42
- Otaku.start do |data|
41
+ Otaku.start(:mark => '*') do |data|
43
42
  '%s %s %s' % [mark, data, mark]
44
43
  end
45
44
 
46
- Otaku.process('hello') # failure !!
45
+ Otaku.process('hello')
46
+ # >> '* hello *'
47
47
 
48
- The reason is that the proc that we passed to Otaku.start is being marshalled
49
- while being passed to the server as a handler, in the process, the contextual
50
- references are lost. A workaround for this problem is:
48
+ This is no longer needed from release-0.4.0 onwards, thanks to the help with
49
+ SerializableProc (http://github.com/ngty/serializable_proc), contextual references
50
+ to global, class, instance & local variables are automatically taken care of:
51
51
 
52
- Otaku.start(:mark => '*') do |data|
53
- '%s %s %s' % [mark, data, mark]
52
+ x, @x, @@x, $x = 'lx', 'ix', 'cx', 'gx'
53
+ Otaku.start do |data|
54
+ [x, @x, @@x, $x].join(data)
54
55
  end
55
56
 
56
- Otaku.process('hello') # >> '* hello *'
57
-
58
- Anything that can be marshalled can be passed as a context. The exception is
59
- proc, which cannot be marshalled, yet it can still be passed in. However, in
60
- the process, all contextual references within the proc are lost, so u may
61
- want to exercise care when playing with proc.
57
+ Otaku.process(' & ')
58
+ # >> 'lx & ix & cx & gx'
62
59
 
63
60
  == Configuraing It
64
61
 
@@ -90,10 +87,6 @@ Configuring can be done via:
90
87
 
91
88
  Otaku.init_wait_time = 10
92
89
 
93
- == TODO
94
-
95
- 1. Currently, only integration testing is done ...
96
-
97
90
  == Note on Patches/Pull Requests
98
91
 
99
92
  * Fork the project.
data/Rakefile CHANGED
@@ -4,6 +4,7 @@ require 'rake'
4
4
  begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
7
8
  gem.name = "otaku"
8
9
  gem.summary = %Q{Dead simple service framework built on eventmachine}
9
10
  gem.description = %Q{}
@@ -12,9 +13,7 @@ begin
12
13
  gem.authors = ["NgTzeYang"]
13
14
  gem.add_development_dependency "bacon", ">= 0"
14
15
  gem.add_dependency "eventmachine", ">= 0.12.10"
15
- gem.add_dependency "ruby2ruby", ">= 1.2.4"
16
- gem.add_dependency "ruby_parser", ">= 2.0.4"
17
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ gem.add_dependency "serializable_proc", ">= 0.4.0"
18
17
  end
19
18
  Jeweler::GemcutterTasks.new
20
19
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -3,8 +3,7 @@ require 'forwardable'
3
3
  require 'eventmachine'
4
4
  require 'logger'
5
5
  require 'base64'
6
- require 'ruby2ruby'
7
- require 'ruby_parser'
6
+ require 'serializable_proc'
8
7
 
9
8
  require 'otaku/encoder'
10
9
  require 'otaku/handler'
@@ -21,7 +20,7 @@ module Otaku
21
20
  :address => '127.0.0.1',
22
21
  :port => 10999,
23
22
  :log_file => '/tmp/otaku.log',
24
- :init_wait_time => 2 * (RUBY_PLATFORM =~ /java/i ? 3 : 1)
23
+ :init_wait_time => 2 * (RUBY_PLATFORM =~ /java/i ? 5 : 1)
25
24
  }
26
25
 
27
26
  class << self
@@ -43,9 +42,9 @@ module Otaku
43
42
  File.expand_path(File.dirname(__FILE__))
44
43
  end
45
44
 
46
- def start(context = {}, &handler)
45
+ def start(&handler)
47
46
  raise HandlerNotDefinedError unless block_given?
48
- Server.handler = Handler.new(context, handler)
47
+ Server.handler = Handler.new(handler)
49
48
  Server.start
50
49
  end
51
50
 
@@ -1,32 +1,30 @@
1
- require 'otaku/handler/magic_proc'
2
- require 'otaku/handler/context'
3
- require 'otaku/handler/processor'
4
-
5
1
  module Otaku
6
2
  class Handler
7
3
 
8
- attr_reader :context, :processor
9
-
10
- def initialize(context, handler)
11
- @context = Context.new(context)
12
- @processor = Processor.new(handler)
4
+ def initialize(handler)
5
+ @proc = SerializableProc.new(&handler)
13
6
  end
14
7
 
15
8
  def process(data)
16
- @context.eval!.instance_exec(data, &@processor.eval!)
9
+ @proc.call(data)
17
10
  end
18
11
 
19
12
  def root
20
- File.dirname(@processor.file)
13
+ @proc.file
21
14
  end
22
15
 
23
16
  def marshal_dump
24
- [@context, @processor]
17
+ @proc
25
18
  end
26
19
 
27
20
  def marshal_load(data)
28
- @context, @processor = data
21
+ @proc = data
29
22
  end
30
23
 
31
24
  end
25
+
26
+ class ::SerializableProc
27
+ attr_reader :file
28
+ end
29
+
32
30
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{otaku}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["NgTzeYang"]
12
- s.date = %q{2010-08-05}
12
+ s.date = %q{2010-09-15}
13
13
  s.description = %q{}
14
14
  s.email = %q{ngty77@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -28,12 +28,8 @@ Gem::Specification.new do |s|
28
28
  "lib/otaku/client.rb",
29
29
  "lib/otaku/encoder.rb",
30
30
  "lib/otaku/handler.rb",
31
- "lib/otaku/handler/context.rb",
32
- "lib/otaku/handler/magic_proc.rb",
33
- "lib/otaku/handler/processor.rb",
34
31
  "lib/otaku/server.rb",
35
32
  "otaku.gemspec",
36
- "spec/handler_spec.rb",
37
33
  "spec/integration_spec.rb",
38
34
  "spec/spec_helper.rb"
39
35
  ]
@@ -44,7 +40,6 @@ Gem::Specification.new do |s|
44
40
  s.summary = %q{Dead simple service framework built on eventmachine}
45
41
  s.test_files = [
46
42
  "spec/integration_spec.rb",
47
- "spec/handler_spec.rb",
48
43
  "spec/spec_helper.rb",
49
44
  "examples/unittest/client.rb",
50
45
  "examples/unittest/tests/b_test.rb",
@@ -60,19 +55,16 @@ Gem::Specification.new do |s|
60
55
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
61
56
  s.add_development_dependency(%q<bacon>, [">= 0"])
62
57
  s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.10"])
63
- s.add_runtime_dependency(%q<ruby2ruby>, [">= 1.2.4"])
64
- s.add_runtime_dependency(%q<ruby_parser>, [">= 2.0.4"])
58
+ s.add_runtime_dependency(%q<serializable_proc>, [">= 0.4.0"])
65
59
  else
66
60
  s.add_dependency(%q<bacon>, [">= 0"])
67
61
  s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
68
- s.add_dependency(%q<ruby2ruby>, [">= 1.2.4"])
69
- s.add_dependency(%q<ruby_parser>, [">= 2.0.4"])
62
+ s.add_dependency(%q<serializable_proc>, [">= 0.4.0"])
70
63
  end
71
64
  else
72
65
  s.add_dependency(%q<bacon>, [">= 0"])
73
66
  s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
74
- s.add_dependency(%q<ruby2ruby>, [">= 1.2.4"])
75
- s.add_dependency(%q<ruby_parser>, [">= 2.0.4"])
67
+ s.add_dependency(%q<serializable_proc>, [">= 0.4.0"])
76
68
  end
77
69
  end
78
70
 
@@ -25,40 +25,35 @@ describe "Otaku Service" do
25
25
  Otaku.process('hello').should.equal '~ hello ~'
26
26
  end
27
27
 
28
- should 'raise Otaku::DataProcessError w proc that has contextual reference yet has no specified context' do
28
+ should 'succeed w proc that has contextual reference' do
29
29
  mark = '*'
30
- Otaku.start{|data| '%s %s %s' % [mark, data, mark] }
31
- lambda { Otaku.process('hello') }.should.raise(Otaku::DataProcessError).
32
- message.should.match(/#<NameError: undefined local variable or method `mark' for /)
33
- end
34
-
35
- should 'succeed w proc that has non-proc contextual reference & has context specified' do
36
- Otaku.start(:mark => '*') {|data| '%s %s %s' % [mark, data, mark] }
37
- Otaku.process('hello').should.equal('* hello *')
38
- end
39
-
40
- should 'succeed w proc that has proc contextual reference & has context specified' do
41
- Otaku.start(:mark => lambda { '*' }) {|data| '%s %s %s' % [mark.call, data, mark.call] }
30
+ Otaku.start {|data| '%s %s %s' % [mark, data, mark] }
42
31
  Otaku.process('hello').should.equal('* hello *')
43
32
  end
44
33
 
45
34
  should 'reflect __FILE__ as captured when declaring proc' do
46
- Otaku.start{|data| __FILE__ }
47
- Otaku.process(:watever_data).should.equal(File.expand_path(__FILE__))
35
+ Otaku.start {|data| __FILE__ }
36
+ Otaku.process(:watever_data).should.equal(__FILE__)
48
37
  end
49
38
 
50
39
  should 'reflect __LINE__ as captured when declaring proc' do
51
40
  Otaku.start{|data| __LINE__ }
52
- Otaku.process(:watever_data).should.equal(__LINE__.pred)
41
+ Otaku.process(:watever_data).should.equal(__LINE__ - 1)
53
42
  end
54
43
 
55
44
  should 'have $LOAD_PATH include Otaku.root' do
56
- Otaku.start{|data| $LOAD_PATH }
45
+ Otaku.start do |data|
46
+ @@_not_isolated_vars = :global
47
+ $LOAD_PATH
48
+ end
57
49
  Otaku.process(:watever_data).should.include(Otaku.root)
58
50
  end
59
51
 
60
52
  should 'have $LOAD_PATH include Otaku::Server#handler.root' do
61
- Otaku.start{|data| $LOAD_PATH }
53
+ Otaku.start do |data|
54
+ @@_not_isolated_vars = :global
55
+ $LOAD_PATH
56
+ end
62
57
  Otaku.process(:watever_data).should.include(Otaku::Server.handler.root)
63
58
  end
64
59
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: otaku
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - NgTzeYang
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-05 00:00:00 +08:00
18
+ date: 2010-09-15 00:00:00 +08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,37 +49,21 @@ dependencies:
49
49
  type: :runtime
50
50
  version_requirements: *id002
51
51
  - !ruby/object:Gem::Dependency
52
- name: ruby2ruby
52
+ name: serializable_proc
53
53
  prerelease: false
54
54
  requirement: &id003 !ruby/object:Gem::Requirement
55
55
  none: false
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- hash: 23
59
+ hash: 15
60
60
  segments:
61
- - 1
62
- - 2
63
- - 4
64
- version: 1.2.4
65
- type: :runtime
66
- version_requirements: *id003
67
- - !ruby/object:Gem::Dependency
68
- name: ruby_parser
69
- prerelease: false
70
- requirement: &id004 !ruby/object:Gem::Requirement
71
- none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- hash: 7
76
- segments:
77
- - 2
78
61
  - 0
79
62
  - 4
80
- version: 2.0.4
63
+ - 0
64
+ version: 0.4.0
81
65
  type: :runtime
82
- version_requirements: *id004
66
+ version_requirements: *id003
83
67
  description: ""
84
68
  email: ngty77@gmail.com
85
69
  executables: []
@@ -101,12 +85,8 @@ files:
101
85
  - lib/otaku/client.rb
102
86
  - lib/otaku/encoder.rb
103
87
  - lib/otaku/handler.rb
104
- - lib/otaku/handler/context.rb
105
- - lib/otaku/handler/magic_proc.rb
106
- - lib/otaku/handler/processor.rb
107
88
  - lib/otaku/server.rb
108
89
  - otaku.gemspec
109
- - spec/handler_spec.rb
110
90
  - spec/integration_spec.rb
111
91
  - spec/spec_helper.rb
112
92
  - examples/unittest/client.rb
@@ -150,7 +130,6 @@ specification_version: 3
150
130
  summary: Dead simple service framework built on eventmachine
151
131
  test_files:
152
132
  - spec/integration_spec.rb
153
- - spec/handler_spec.rb
154
133
  - spec/spec_helper.rb
155
134
  - examples/unittest/client.rb
156
135
  - examples/unittest/tests/b_test.rb
@@ -1,43 +0,0 @@
1
- module Otaku
2
- class Handler
3
- class Context #:nodoc:
4
-
5
- attr_reader :code
6
-
7
- def initialize(methods_hash)
8
- @code = 'Class.new{ %s }.new' %
9
- methods_hash.map do |method, val|
10
- 'def %s; %s; end' % [method, method_body(val)]
11
- end.sort.join('; ')
12
- end
13
-
14
- def eval!
15
- eval(@code, nil, '(generated class)', 1)
16
- end
17
-
18
- def marshal_dump
19
- [@code]
20
- end
21
-
22
- def marshal_load(data)
23
- @code, _ = data
24
- end
25
-
26
- private
27
-
28
- def method_body(val)
29
- if val.is_a?(Proc)
30
- "Encoder.decode(%|#{Encoder.encode(magic_proc(val)).gsub('|','\|')}|).eval!"
31
- else
32
- "Encoder.decode(%|#{Encoder.encode(val).gsub('|','\|')}|)"
33
- end
34
- end
35
-
36
- def magic_proc(block)
37
- MagicProc.new(block)
38
- end
39
-
40
- end
41
- end
42
- end
43
-
@@ -1,116 +0,0 @@
1
- module Otaku
2
- class Handler
3
- class MagicProc #:nodoc:
4
-
5
- RUBY_PARSER = RubyParser.new
6
- RUBY_2_RUBY = Ruby2Ruby.new
7
-
8
- attr_reader :file, :line, :code
9
-
10
- def initialize(block)
11
- @cache = {:block => block}
12
- extract_file_and_line_and_code
13
- @cache = nil
14
- end
15
-
16
- def eval!
17
- eval(@code, nil, @file, @line)
18
- end
19
-
20
- def marshal_dump
21
- [@code, @file, @line]
22
- end
23
-
24
- def marshal_load(data)
25
- @code, @file, @line = data
26
- end
27
-
28
- private
29
-
30
- def extract_file_and_line_and_code
31
- code, remaining = code_fragments
32
-
33
- while frag = remaining[frag_regexp,1]
34
- begin
35
- sexp = RUBY_PARSER.parse(replace_magic_vars(code += frag))
36
- if sexp.inspect =~ sexp_regexp
37
- @code = revert_magic_vars(RUBY_2_RUBY.process(sexp)).sub('proc {','lambda {')
38
- break
39
- end
40
- rescue SyntaxError, Racc::ParseError, NoMethodError
41
- remaining.sub!(frag,'')
42
- end
43
- end
44
- end
45
-
46
- def code_fragments
47
- ignore, start_marker, arg =
48
- [:ignore, :start_marker, :arg].map{|key| code_match_args[key] }
49
- [
50
- arg ? "proc #{start_marker} |#{arg}|" : "proc #{start_marker}",
51
- source_code.sub(ignore, '')
52
- ]
53
- end
54
-
55
- def sexp_regexp
56
- @cache[:sexp_regexp] ||= (
57
- Regexp.new([
58
- Regexp.quote("s(:iter, s(:call, nil, :"),
59
- "(proc|lambda)",
60
- Regexp.quote(", s(:arglist)), "),
61
- '(%s|%s|%s)' % [
62
- Regexp.quote('s(:masgn, s(:array, s('),
63
- Regexp.quote('s(:lasgn, :'),
64
- Regexp.quote('nil, s(')
65
- ]
66
- ].join)
67
- )
68
- end
69
-
70
- def frag_regexp
71
- @cache[:frag_regexp] ||= (
72
- end_marker = {'do' => 'end', '{' => '\}'}[code_match_args[:start_marker]]
73
- /^(.*?\W#{end_marker})/m
74
- )
75
- end
76
-
77
- def code_regexp
78
- @cache[:code_regexp] ||=
79
- /^(.*?(lambda|proc|Proc\.new)?\s*(do|\{)\s*(\|(.*?)\|\s*)?)/m
80
- end
81
-
82
- def code_match_args
83
- @cache[:code_match_args] ||= (
84
- args = source_code.match(code_regexp)
85
- {
86
- :ignore => args[1],
87
- :start_marker => args[3],
88
- :arg => args[5]
89
- }
90
- )
91
- end
92
-
93
- def source_code
94
- @cache[:source_code] ||= (
95
- file, line = /^#<Proc:0x[0-9A-Fa-f]+@(.+):(\d+).*?>$/.match(@cache[:block].inspect)[1..2]
96
- @file = File.expand_path(file)
97
- @line = line.to_i
98
- File.readlines(@file)[@line.pred .. -1].join
99
- )
100
- end
101
-
102
- def replace_magic_vars(code)
103
- %w{__FILE__ __LINE__}.inject(code) do |code, var|
104
- code.gsub(var, "%|((#{var}))|")
105
- end
106
- end
107
-
108
- def revert_magic_vars(code)
109
- %w{__FILE__ __LINE__}.inject(code) do |code, var|
110
- code.gsub(%|"((#{var}))"|, var)
111
- end
112
- end
113
-
114
- end
115
- end
116
- end