hotcell 0.2.0 → 0.3.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.
@@ -46,15 +46,15 @@ prechigh
46
46
  left AND
47
47
  left OR
48
48
  nonassoc RANGE
49
- right TERNARY
49
+ right QUESTION
50
50
  right ASSIGN
51
51
  nonassoc COMMA COLON
52
52
  left SEMICOLON NEWLINE
53
53
  preclow
54
54
  start document
55
55
  rule
56
- document: document document_unit { pospoppush(2); val[0].children.push(val[1]) }
57
- | document_unit { result = build Joiner, :JOINER, val[0], position: pospoppush(1) }
56
+ document: document document_unit { pospoppush(2); val[0].push(val[1]) }
57
+ | document_unit { result = [val[0]] }
58
58
  document_unit: template | tag | block_tag | command_tag
59
59
 
60
60
  template: TEMPLATE { result = val[0] }
@@ -155,6 +155,7 @@ rule
155
155
  | expr EQUAL expr { result = build Expression, :EQUAL, val[0], val[2], position: pospoppush(3) }
156
156
  | expr INEQUAL expr { result = build Expression, :INEQUAL, val[0], val[2], position: pospoppush(3) }
157
157
  | NOT expr { result = build Expression, :NOT, val[1], position: pospoppush(2) }
158
+ | expr QUESTION expr COLON expr { result = build Expression, :TERNARY, val[0], val[2], val[4], position: pospoppush(5) }
158
159
  | IDENTIFER ASSIGN expr { result = build Assigner, val[0], val[2], position: pospoppush(3) }
159
160
  | expr PERIOD method { pospoppush(3); val[2].children[0] = val[0]; result = val[2] }
160
161
  | expr AOPEN arguments ACLOSE {
@@ -162,21 +163,37 @@ rule
162
163
  }
163
164
  | POPEN PCLOSE { pospoppush(2); result = nil }
164
165
  | POPEN sequence PCLOSE {
165
- position = pospoppush(3)
166
166
  result = case val[1].size
167
167
  when 1
168
168
  val[1][0]
169
169
  else
170
- build Sequencer, :SEQUENCE, *val[1].flatten, position: position
170
+ build Sequencer, :SEQUENCE, *val[1].flatten, position: pospoppush(3)
171
171
  end
172
172
  }
173
173
  | value
174
174
 
175
- value: const | number | string | range | array | hash | method
175
+ value: const | number | string | dstring | regexp | range | array | hash | method
176
176
 
177
177
  const: NIL | TRUE | FALSE
178
178
  number: INTEGER | FLOAT
179
- string: STRING | REGEXP
179
+ string: STRING
180
+ regexp: REGEXP
181
+
182
+ dstring: DOPEN dstring_content DCLOSE { result = build Expression, :DSTRING, *val[1], position: pospoppush(3) }
183
+ | DOPEN DCLOSE { pospoppush(2); result = '' }
184
+ dstring_content: STRING { result = [val[0]] }
185
+ | dstring_interpolation { result = [val[0]] }
186
+ | dstring_content STRING { pospoppush(2); val[0].push(val[1]); result = val[0] }
187
+ | dstring_content dstring_interpolation { pospoppush(2); val[0].push(val[1]); result = val[0] }
188
+ dstring_interpolation: IOPEN sequence ICLOSE {
189
+ result = case val[1].size
190
+ when 1
191
+ val[1][0]
192
+ else
193
+ build Sequencer, :SEQUENCE, *val[1].flatten, position: pospoppush(3)
194
+ end
195
+ }
196
+ | IOPEN ICLOSE { pospoppush(2); result = nil }
180
197
 
181
198
  range: expr RANGE expr {
182
199
  result = build Expression, val[1] == '..' ? :RANGE : :ERANGE,
@@ -260,11 +277,8 @@ rule
260
277
  end
261
278
 
262
279
  def parse
263
- if @tokens.size == 0
264
- build Joiner, :JOINER, position: 0
265
- else
266
- do_parse
267
- end
280
+ children = @tokens.size.zero? ? [] : do_parse
281
+ build Joiner, :JOINER, *children, position: 0
268
282
  end
269
283
 
270
284
  def next_token
@@ -0,0 +1,54 @@
1
+ module Hotcell
2
+ class Tong
3
+ module Mixin
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :tong_methods, instance_writter: false
8
+ self.tong_methods = Set.new
9
+
10
+ def self.manipulate *methods
11
+ self.tong_methods = Set.new(tong_methods.to_a + methods.flatten.map(&:to_s))
12
+ end
13
+ end
14
+
15
+ def to_tong
16
+ self
17
+ end
18
+
19
+ def tong_invoke method, *arguments
20
+ if method == '[]'
21
+ tong_invoke_brackets *arguments
22
+ elsif tong_invokable? method
23
+ send(method, *arguments)
24
+ else
25
+ tong_missing(method, *arguments)
26
+ end
27
+ end
28
+
29
+ def tong_missing method, *arguments
30
+ end
31
+
32
+ private
33
+
34
+ def tong_invokable? method
35
+ tong_methods.include? method
36
+ end
37
+
38
+ def tong_invoke_brackets *arguments
39
+ if respond_to? :[]
40
+ self[*arguments]
41
+ else
42
+ tong_invoke *arguments
43
+ end
44
+ end
45
+ end
46
+
47
+ include Mixin
48
+
49
+ def tong_methods
50
+ @tong_methods ||= Set.new((self.class.public_instance_methods -
51
+ Hotcell::Tong.public_instance_methods).map(&:to_s))
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Hotcell
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
File without changes
@@ -27,7 +27,7 @@ describe Hotcell::Commands::Include do
27
27
 
28
28
  describe '#render' do
29
29
  specify { parse("{{ include 'template0' }}").render(render_options).should =~ /Template not found/ }
30
- specify { parse("{{ include 'template1' }}").render(render_options).should == 'Hello' }
30
+ specify { parse("{{ include 'template1' }}").render!(render_options).should == 'Hello' }
31
31
  specify { parse("{{ include 'template2' }}").render(render_options).should == 'Hello, ' }
32
32
  specify { parse(
33
33
  "{{ include 'template2', name: 'Pyrosha' }}"
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Hotcell::Context do
4
4
  its('scope.scope') { should == [{}] }
5
- its(:helpers) { should be_a Hotcell::Manipulator }
5
+ its(:helpers) { should be_a Hotcell::Tong }
6
6
 
7
7
  describe '#normalize_options' do
8
8
  def result options = {}
@@ -69,7 +69,7 @@ describe Hotcell::Context do
69
69
  end
70
70
  end
71
71
 
72
- describe '#manipulator_invoke' do
72
+ describe '#tong_invoke' do
73
73
  subject { described_class.new(
74
74
  variables: { foo: 42, 'bar' => 'baz' }, environment: { 'baz' => 'moo' },
75
75
  helpers: Module.new do
@@ -82,12 +82,12 @@ describe Hotcell::Context do
82
82
  end
83
83
  end
84
84
  ) }
85
- specify { subject.manipulator_invoke('foo').should == 42 }
86
- specify { subject.manipulator_invoke('moo').should be_nil }
87
- specify { subject.manipulator_invoke('baz').should be_nil }
88
- specify { subject.manipulator_invoke('bar').should == 'baz' }
89
- specify { expect { subject.manipulator_invoke('bar', 42) }.to raise_error ArgumentError }
90
- specify { expect { subject.manipulator_invoke('strip') }.to raise_error ArgumentError }
91
- specify { subject.manipulator_invoke('strip', ' hello ').should == 'hello' }
85
+ specify { subject.tong_invoke('foo').should == 42 }
86
+ specify { subject.tong_invoke('moo').should be_nil }
87
+ specify { subject.tong_invoke('baz').should be_nil }
88
+ specify { subject.tong_invoke('bar').should == 'baz' }
89
+ specify { expect { subject.tong_invoke('bar', 42) }.to raise_error ArgumentError }
90
+ specify { expect { subject.tong_invoke('strip') }.to raise_error ArgumentError }
91
+ specify { subject.tong_invoke('strip', ' hello ').should == 'hello' }
92
92
  end
93
93
  end
@@ -105,13 +105,13 @@ describe Hotcell::Lexer do
105
105
 
106
106
  context 'constants' do
107
107
  specify { expression('nil').should == [[:NIL, nil]] }
108
- specify { expression('"nil"').should == [[:STRING, 'nil']] }
108
+ specify { expression('"nil"').should == [[:DOPEN, '"'], [:STRING, 'nil'], [:DCLOSE, '"']] }
109
109
  specify { expression('null').should == [[:NIL, nil]] }
110
- specify { expression('"null"').should == [[:STRING, 'null']] }
110
+ specify { expression('"null"').should == [[:DOPEN, '"'], [:STRING, 'null'], [:DCLOSE, '"']] }
111
111
  specify { expression('false').should == [[:FALSE, false]] }
112
- specify { expression('"false"').should == [[:STRING, 'false']] }
112
+ specify { expression('"false"').should == [[:DOPEN, '"'], [:STRING, 'false'], [:DCLOSE, '"']] }
113
113
  specify { expression('true').should == [[:TRUE, true]] }
114
- specify { expression('"true"').should == [[:STRING, 'true']] }
114
+ specify { expression('"true"').should == [[:DOPEN, '"'], [:STRING, 'true'], [:DCLOSE, '"']] }
115
115
  end
116
116
  end
117
117
 
@@ -130,7 +130,7 @@ describe Hotcell::Lexer do
130
130
  specify { expression(%q{'при\вет'}).should == [[:STRING, 'при\вет']] }
131
131
 
132
132
  context do
133
- let(:strings) { data 'sstrings' }
133
+ let(:strings) { data 'strings' }
134
134
 
135
135
  specify { expression(strings).delete_if { |token| token.first == :NEWLINE }.should == [
136
136
  [:STRING, 'fo\'o'], [:STRING, 'fo\o'], [:STRING, 'fo\\o'],
@@ -141,28 +141,64 @@ describe Hotcell::Lexer do
141
141
 
142
142
  context 'double quoted' do
143
143
  specify { expression(%q{""}).should == [[:STRING, ""]] }
144
- specify { expression(%q{"foo"}).should == [[:STRING, "foo"]] }
145
- specify { expression(%q{"fo'o"}).should == [[:STRING, "fo'o"]] }
146
- specify { expression(%q{"fo\o"}).should == [[:STRING, "fo\o"]] }
147
- specify { expression(%q{"fo\"o"}).should == [[:STRING, "fo\"o"]] }
148
- specify { expression(%q{"fo\'o"}).should == [[:STRING, "fo\'o"]] }
149
- specify { expression(%q{"fo\no"}).should == [[:STRING, "fo\no"]] }
150
- specify { expression(%q{"fo\mo"}).should == [[:STRING, "fo\mo"]] }
151
- specify { expression(%q{"foo42"}).should == [[:STRING, "foo42"]] }
152
- specify { expression(%q{"привет"}).should == [[:STRING, "привет"]] }
144
+ specify { expression(%q{"foo"}).should == [[:DOPEN, '"'], [:STRING, "foo"], [:DCLOSE, '"']] }
145
+ specify { expression(%q{"fo'o"}).should == [[:DOPEN, '"'], [:STRING, "fo'o"], [:DCLOSE, '"']] }
146
+ specify { expression(%q{"fo\o"}).should == [[:DOPEN, '"'], [:STRING, "fo\o"], [:DCLOSE, '"']] }
147
+ specify { expression(%q{"fo\"o"}).should == [[:DOPEN, '"'], [:STRING, "fo\"o"], [:DCLOSE, '"']] }
148
+ specify { expression(%q{"fo\'o"}).should == [[:DOPEN, '"'], [:STRING, "fo\'o"], [:DCLOSE, '"']] }
149
+ specify { expression(%q{"fo\no"}).should == [[:DOPEN, '"'], [:STRING, "fo\no"], [:DCLOSE, '"']] }
150
+ specify { expression(%q{"fo\mo"}).should == [[:DOPEN, '"'], [:STRING, "fo\mo"], [:DCLOSE, '"']] }
151
+ specify { expression(%q{"foo42"}).should == [[:DOPEN, '"'], [:STRING, "foo42"], [:DCLOSE, '"']] }
152
+ specify { expression(%q{"привет"}).should == [[:DOPEN, '"'], [:STRING, "привет"], [:DCLOSE, '"']] }
153
153
  # RBX can not handle this
154
154
  # specify { expression(%q{"при\вет"}).should == [[:STRING, "при\вет"]] }
155
155
 
156
156
  context do
157
157
  let(:strings) { data 'dstrings' }
158
158
 
159
- specify { expression(strings).delete_if { |token| token.first == :NEWLINE }.should == [
159
+ specify { expression(strings).delete_if { |token|
160
+ [:NEWLINE, :DOPEN, :DCLOSE].include?(token.first)
161
+ }.should == [
160
162
  [:STRING, "fo\"o"], [:STRING, "fo\o"], [:STRING, "fo\\o"],
161
163
  [:STRING, "fo\no"], [:STRING, "fo\mo"], [:STRING, "fo\to"],
162
164
  [:STRING, "foo\nbar"]
163
165
  ] }
164
166
  end
165
167
  end
168
+
169
+ context 'interpolation' do
170
+ specify { expression('"{ }"').should == [[:DOPEN, '"'], [:STRING, '{ }'], [:DCLOSE, '"']] }
171
+ specify { expression('"#"').should == [[:DOPEN, '"'], [:STRING, '#'], [:DCLOSE, '"']] }
172
+ specify { expression('"# "').should == [[:DOPEN, '"'], [:STRING, '# '], [:DCLOSE, '"']] }
173
+ specify { expression('" #"').should == [[:DOPEN, '"'], [:STRING, ' #'], [:DCLOSE, '"']] }
174
+ specify { expression('"#{ var }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:IDENTIFER, "var"],
175
+ [:ICLOSE, "}"], [:DCLOSE, '"']] }
176
+ specify { expression('"#{ } world"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:ICLOSE, "}"],
177
+ [:STRING, " world"], [:DCLOSE, '"']] }
178
+ specify { expression('"hello #{ }"').should == [[:DOPEN, '"'], [:STRING, "hello "], [:IOPEN, "\#{"],
179
+ [:ICLOSE, "}"], [:DCLOSE, '"']] }
180
+ specify { expression('"hello#{ } world"').should == [[:DOPEN, '"'], [:STRING, "hello"], [:IOPEN, "\#{"],
181
+ [:ICLOSE, "}"], [:STRING, " world"], [:DCLOSE, '"']] }
182
+ specify { expression('"hello #{ 2 + 3 }world"').should == [[:DOPEN, '"'], [:STRING, "hello "],
183
+ [:IOPEN, "\#{"], [:INTEGER, 2], [:PLUS, "+"], [:INTEGER, 3],
184
+ [:ICLOSE, "}"], [:STRING, "world"], [:DCLOSE, '"']] }
185
+ specify { expression('"\#{ var }"').should == [[:DOPEN, '"'], [:STRING, '#{ var }'], [:DCLOSE, '"']] }
186
+ specify { expression('"#{ } }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:ICLOSE, "}"],
187
+ [:STRING, ' }'], [:DCLOSE, '"']] }
188
+ specify { expression('"#{ { } }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:HOPEN, "{"],
189
+ [:HCLOSE, "}"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
190
+ specify { expression('"#{ [ ] }"').should == [[:DOPEN, '"'], [:IOPEN, "\#{"], [:AOPEN, "["],
191
+ [:ACLOSE, "]"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
192
+ specify { expression('"#\{ }"').should == [[:DOPEN, '"'], [:STRING, '#{ }'], [:DCLOSE, '"']] }
193
+ specify { expression('"##{ }"').should == [[:DOPEN, '"'], [:STRING, '#'], [:IOPEN, "\#{"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
194
+ specify { expression('"###{ } #{ }"').should == [[:DOPEN, '"'], [:STRING, '##'], [:IOPEN, "\#{"],
195
+ [:ICLOSE, "}"], [:STRING, ' '], [:IOPEN, "\#{"], [:ICLOSE, "}"], [:DCLOSE, '"']] }
196
+
197
+ context 'exceptions' do
198
+ specify { expect { expression('"#{"') }.to raise_error Hotcell::UnterminatedString, /`" }}`.*1:7/ }
199
+ specify { expect { expression('"#{ { }"') }.to raise_error Hotcell::UnterminatedString, /`" }}`.*1:11/ }
200
+ end
201
+ end
166
202
  end
167
203
 
168
204
  context 'regexp' do
@@ -204,7 +240,8 @@ describe Hotcell::Lexer do
204
240
  [:INTEGER, 42], [:SEMICOLON, ";"], [:REGEXP, /regexp/]
205
241
  ] }
206
242
  specify { expression('"hello" /regexp/').should == [
207
- [:STRING, "hello"], [:DIVIDE, "/"], [:IDENTIFER, "regexp"], [:DIVIDE, "/"]
243
+ [:DOPEN, '"'], [:STRING, "hello"], [:DCLOSE, '"'],
244
+ [:DIVIDE, "/"], [:IDENTIFER, "regexp"], [:DIVIDE, "/"]
208
245
  ] }
209
246
  end
210
247
  end
@@ -274,11 +311,13 @@ describe Hotcell::Lexer do
274
311
  specify { expression("foo(36.6);\n a = \"привет\"").should == [
275
312
  [:IDENTIFER, "foo"], [:POPEN, "("], [:FLOAT, 36.6],
276
313
  [:PCLOSE, ")"], [:SEMICOLON, ";"], [:NEWLINE, "\n"],
277
- [:IDENTIFER, "a"], [:ASSIGN, "="], [:STRING, "привет"]
314
+ [:IDENTIFER, "a"], [:ASSIGN, "="], [:DOPEN, '"'],
315
+ [:STRING, "привет"], [:DCLOSE, '"']
278
316
  ] }
279
317
  specify { expression("'foo'.match(\"^foo$\")").should == [
280
318
  [:STRING, "foo"], [:PERIOD, "."], [:IDENTIFER, "match"],
281
- [:POPEN, "("], [:STRING, "^foo$"], [:PCLOSE, ")"]
319
+ [:POPEN, "("], [:DOPEN, '"'], [:STRING, "^foo$"],
320
+ [:DCLOSE, '"'], [:PCLOSE, ")"]
282
321
  ] }
283
322
  end
284
323
 
@@ -218,6 +218,54 @@ describe Hotcell::Parser do
218
218
  end
219
219
  end
220
220
 
221
+ context 'ternary operator' do
222
+ specify { parse('{{ true ? 42 : 43 }}').should be_equal_node_to JOINER(TAG(42, mode: :normal)) }
223
+ specify { parse('{{ false ? 42 : 43 }}').should be_equal_node_to JOINER(TAG(43, mode: :normal)) }
224
+ specify { parse('{{ cond ? branch1 : branch2 }}').should be_equal_node_to JOINER(
225
+ TAG(TERNARY(METHOD('cond'), METHOD('branch1'), METHOD('branch2')), mode: :normal)
226
+ ) }
227
+ specify { parse('{{ cond1 ? branch1 : cond2 ? branch2 : branch3 }}').should be_equal_node_to JOINER(
228
+ TAG(TERNARY(
229
+ METHOD('cond1'), METHOD('branch1'),
230
+ TERNARY(METHOD('cond2'), METHOD('branch2'), METHOD('branch3'))
231
+ ), mode: :normal)
232
+ ) }
233
+ specify { parse('{{ (cond1 ? branch1 : cond2) ? branch2 : branch3 }}').should be_equal_node_to JOINER(
234
+ TAG(TERNARY(
235
+ TERNARY(METHOD('cond1'), METHOD('branch1'), METHOD('cond2')),
236
+ METHOD('branch2'), METHOD('branch3')
237
+ ), mode: :normal)
238
+ ) }
239
+ specify { parse('{{ cond1 ? cond2 ? branch1 : branch2 : branch3 }}').should be_equal_node_to JOINER(
240
+ TAG(TERNARY(
241
+ METHOD('cond1'),
242
+ TERNARY(METHOD('cond2'), METHOD('branch1'), METHOD('branch2')),
243
+ METHOD('branch3')
244
+ ), mode: :normal)
245
+ ) }
246
+ specify { parse('{{ res = cond ? branch1 + 3 : branch2 && true }}').should be_equal_node_to JOINER(
247
+ TAG(ASSIGN('res',
248
+ TERNARY(METHOD('cond'), PLUS(METHOD('branch1'), 3), AND(METHOD('branch2'), true)
249
+ )), mode: :normal)
250
+ ) }
251
+ specify { expect { parse('{{ (1 ? 2) : 3 }}') }.to raise_error Hotcell::UnexpectedLexem, "Unexpected PCLOSE `)` at 1:10"}
252
+ end
253
+
254
+ context 'interpolation' do
255
+ specify { parse('{{ "hello" }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
256
+ specify { parse('{{ \'#{}\' }}').should be_equal_node_to JOINER(TAG('#{}', mode: :normal)) }
257
+ specify { parse('{{ "#{}" }}').should be_equal_node_to JOINER(TAG('', mode: :normal)) }
258
+ specify { parse('{{ "#{ 3 }" }}').should be_equal_node_to JOINER(TAG('3', mode: :normal)) }
259
+ specify { parse('{{ "#{ var }" }}').should be_equal_node_to JOINER(TAG(DSTRING(METHOD('var')), mode: :normal)) }
260
+ specify { parse('{{ "#{ var }world" }}').should be_equal_node_to JOINER(TAG(DSTRING(METHOD('var'), 'world'), mode: :normal)) }
261
+ specify { parse('{{ "hello#{}" }}').should be_equal_node_to JOINER(TAG('hello', mode: :normal)) }
262
+ specify { parse('{{ "hello#{}world" }}').should be_equal_node_to JOINER(TAG('helloworld', mode: :normal)) }
263
+ specify { parse('{{ "hello#{ 6 * 7 }world" }}').should be_equal_node_to JOINER(TAG('hello42world', mode: :normal)) }
264
+ specify { parse('{{ "hello#{ var = 6 * 7; var + 1 }world" }}').should be_equal_node_to JOINER(
265
+ TAG(DSTRING('hello', SEQUENCE(ASSIGN('var', 42), PLUS(METHOD('var'), 1)), 'world'), mode: :normal)
266
+ ) }
267
+ end
268
+
221
269
  context 'arrays' do
222
270
  specify { parse('{{ [] }}').should be_equal_node_to JOINER(TAG(ARRAY(), mode: :normal)) }
223
271
  specify { parse('{{ [ 2 ] }}').should be_equal_node_to JOINER(TAG(ARRAY(2), mode: :normal)) }
@@ -67,11 +67,11 @@ describe Hotcell::Template do
67
67
  specify { described_class.parse(<<-SOURCE
68
68
  {{ for i, in: [1, 2, 3, 4] }}
69
69
  {{ if i % 2 == 1 }}
70
- {{ i }}
70
+ {{ "number\#{i}" }}
71
71
  {{ end if }}
72
72
  {{ end for }}
73
73
  SOURCE
74
- ).render.gsub(/[\s\n]+/, ' ').strip.should == '1 3' }
74
+ ).render.gsub(/[\s\n]+/, ' ').strip.should == 'number1 number3' }
75
75
 
76
76
  specify { described_class.parse(<<-SOURCE
77
77
  {{ for i, in: [1, 2, 3, 4] }}
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hotcell::Tong do
4
+ context 'mixed in' do
5
+ context do
6
+ let(:klass) do
7
+ Class.new(Numeric) do
8
+ include Hotcell::Tong::Mixin
9
+
10
+ def foo; end
11
+ end
12
+ end
13
+ subject { klass.new }
14
+
15
+ its(:tong_methods) { should be_a Set }
16
+ its(:tong_methods) { should be_empty }
17
+ its(:to_tong) { should === subject }
18
+ specify { subject.tong_invoke('foo').should be_nil }
19
+ specify { subject.tong_invoke(:foo).should be_nil }
20
+ specify { subject.tong_invoke('foo', 42, :arg).should be_nil }
21
+ end
22
+
23
+ context do
24
+ let(:klass) do
25
+ Class.new(String) do
26
+ include Hotcell::Tong::Mixin
27
+
28
+ manipulate :foo, :bar
29
+
30
+ alias_method :foo, :split
31
+ alias_method :bar, :size
32
+ end
33
+ end
34
+ subject { klass.new('hello world') }
35
+
36
+ its(:to_tong) { should === subject }
37
+ specify { (subject.tong_methods.to_a & %w(foo bar)).should =~ %w(foo bar) }
38
+ specify { subject.tong_invoke('baz').should be_nil }
39
+ specify { subject.tong_invoke('baz', 42, :arg).should be_nil }
40
+ specify { subject.tong_invoke(:foo).should be_nil }
41
+ specify { subject.tong_invoke('foo').should == %w(hello world) }
42
+ specify { subject.tong_invoke('bar').should == 11 }
43
+ specify { expect { subject.tong_invoke('bar', 42) }.to raise_error ArgumentError }
44
+ end
45
+ end
46
+
47
+ context 'inherited' do
48
+ let(:klass) do
49
+ Class.new(described_class) do
50
+ def foo
51
+ 'foo'
52
+ end
53
+
54
+ def bar arg1, arg2
55
+ arg1 + arg2
56
+ end
57
+ end
58
+ end
59
+ subject { klass.new }
60
+
61
+ its('tong_methods.to_a') { should =~ %w(foo bar) }
62
+ its(:to_tong) { should === subject }
63
+ specify { subject.tong_invoke('send').should be_nil }
64
+ specify { subject.tong_invoke(:bar).should be_nil }
65
+ specify { subject.tong_invoke('foo').should == 'foo' }
66
+ specify { subject.tong_invoke('bar', 5, 8).should == 13 }
67
+ specify { expect { subject.tong_invoke('bar') }.to raise_error ArgumentError }
68
+ end
69
+
70
+ describe '#tong_missing' do
71
+ let(:klass) do
72
+ Class.new(described_class) do
73
+ def tong_missing method, *args
74
+ "missing #{method} with #{args}"
75
+ end
76
+ end
77
+ end
78
+ subject { klass.new }
79
+
80
+ specify { subject.tong_invoke('foo').should == 'missing foo with []' }
81
+ specify { subject.tong_invoke('bar', 42).should == 'missing bar with [42]' }
82
+ end
83
+ end