otaku 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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