moosex 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +114 -2
- data/lib/moosex.rb +48 -23
- data/lib/moosex/event.rb +1 -2
- data/lib/moosex/version.rb +1 -1
- data/samples/events.rb +40 -0
- data/spec/coerce_spec.rb +1 -1
- data/spec/event_spec.rb +1 -1
- data/spec/hooks_spec.rb +95 -1
- data/spec/proxy_spec.rb +66 -5
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcfa29200c1f61890442923acc7dc15d025634f0
|
4
|
+
data.tar.gz: fa879cea6cd2145c67216366c944278f09510ac6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 258f22f909577942823e51962d8191c9827ed2cf4194bee1cb034a87cf056c0e83f55ce2738bd5769d4b4f021e53dd7855e623a953fe38672927aab3fa9d7833
|
7
|
+
data.tar.gz: 729fe046482be2b464dfe0f2709167737fbb88f1b28afe0b8f2215b8ce06b82fa6b38f793ac3df3a360ec11413ae37e2ba94ec8ade4b52df6599c38d454f8a10
|
data/Changelog
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -167,12 +167,18 @@ Optional.
|
|
167
167
|
|
168
168
|
### coerce
|
169
169
|
|
170
|
-
You can try to coerce the attribute value by a lambda before the type check phase. For example you can do
|
170
|
+
You can try to coerce the attribute value by a lambda/method before the type check phase. For example you can do
|
171
171
|
|
172
172
|
```ruby
|
173
173
|
coerce: lambda{ |new_value| new_value.to_i },
|
174
174
|
```
|
175
175
|
|
176
|
+
or just
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
coerce: :to_i,
|
180
|
+
```
|
181
|
+
|
176
182
|
to force a convertion to integer. Or flatten one array, convert to symbol, etc. Optional.
|
177
183
|
|
178
184
|
### handles
|
@@ -200,6 +206,63 @@ If you need rename the method, you can specify a Hash:
|
|
200
206
|
```
|
201
207
|
|
202
208
|
Optional.
|
209
|
+
#### Currying
|
210
|
+
|
211
|
+
It is possible curry constant values declaring a pair/hash and set one or more constant values / lambdas
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
handles: {
|
215
|
+
my_method_1: {
|
216
|
+
method1: 1
|
217
|
+
}
|
218
|
+
},
|
219
|
+
```
|
220
|
+
|
221
|
+
this will curry the constant 1 to the argument list. In other words:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
obj.target.method1(1,2,3) # OR
|
225
|
+
obj.my_method_1(2,3)
|
226
|
+
```
|
227
|
+
|
228
|
+
are equivalent. You can curry as many arguments as you can.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
handles: {
|
232
|
+
my_method_2: {
|
233
|
+
method2: [1, lambda{ 2 } ]
|
234
|
+
}
|
235
|
+
},
|
236
|
+
```
|
237
|
+
|
238
|
+
will generate
|
239
|
+
```ruby
|
240
|
+
obj.target.method1(1,2,3) # OR
|
241
|
+
obj.my_method_2(3)
|
242
|
+
```
|
243
|
+
|
244
|
+
are equivalent. if we find one lambda we will call on runtime.
|
245
|
+
|
246
|
+
Important: if you need do something more complex ( like manipulate the argument list, etc ) consider use the hook 'around'.
|
247
|
+
|
248
|
+
###### But how we can curry arrays?
|
249
|
+
|
250
|
+
Use Double arrays
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
handles: {
|
254
|
+
my_method_1: {
|
255
|
+
method1: [ [1,2,3] ]
|
256
|
+
}
|
257
|
+
},
|
258
|
+
```
|
259
|
+
this will curry the array [1,2,3] to the argument list. In other words:
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
obj.target.method1([1,2,3],2,3) # OR
|
263
|
+
obj.my_method_1(2,3)
|
264
|
+
```
|
265
|
+
are equivalent.
|
203
266
|
|
204
267
|
### trigger
|
205
268
|
|
@@ -732,7 +795,7 @@ ex3 = BuildArgsExample2.new() # x == 4, y == 8
|
|
732
795
|
|
733
796
|
## EVENTS
|
734
797
|
|
735
|
-
MooseX has a built-in event system, and it should be useful if you want to avoid after/before hooks ( depends of what is
|
798
|
+
MooseX has a built-in event system, and it should be useful if you want to avoid after/before hooks ( depends of what is your problem ).
|
736
799
|
|
737
800
|
```ruby
|
738
801
|
require 'moosex'
|
@@ -779,6 +842,55 @@ e.emit(:error, "...") # will no longer log nothing
|
|
779
842
|
|
780
843
|
If you want to restrict how many different events you can handle, you should overload the `has_events` method and return one array of valid events. If you want accept all, you should return nil (default).
|
781
844
|
|
845
|
+
For example, your method should emit many events, in many points, and you can add/remove listeners easily. And Much More!
|
846
|
+
|
847
|
+
### Events + Handles / Currying
|
848
|
+
|
849
|
+
Look this good example:
|
850
|
+
|
851
|
+
```ruby
|
852
|
+
require 'moosex'
|
853
|
+
require 'moosex/event'
|
854
|
+
|
855
|
+
class EventHandler
|
856
|
+
include MooseX
|
857
|
+
include MooseX::Event
|
858
|
+
|
859
|
+
def has_events ; [ :pinged, :ponged ]; end
|
860
|
+
end
|
861
|
+
|
862
|
+
class EventProcessor
|
863
|
+
include MooseX
|
864
|
+
|
865
|
+
has event_handler: {
|
866
|
+
is: :ro,
|
867
|
+
isa: EventHandler,
|
868
|
+
default: lambda{ EventHandler.new }, # EventProcessor HAS ONE EventHandler
|
869
|
+
handles: { # Now, lets start to delegate and currying:
|
870
|
+
ping: { emit: :pinged }, # ping() is the same of event_handler.emit(:pinged)
|
871
|
+
pong: { emit: :ponged }, # pong(x) is the same of event_handler.emit(:pinged,x)
|
872
|
+
on_ping: { on: :pinged }, #
|
873
|
+
on_pong: { on: :ponged }, # same thing for on_ping / on_pong
|
874
|
+
},
|
875
|
+
}
|
876
|
+
end
|
877
|
+
|
878
|
+
ep = EventProcessor.new()
|
879
|
+
|
880
|
+
ep.on_ping do |obj|
|
881
|
+
puts "receive ping!"
|
882
|
+
end
|
883
|
+
|
884
|
+
ep.on_pong do |obj, message|
|
885
|
+
puts "receive pong with #{message}!"
|
886
|
+
end
|
887
|
+
|
888
|
+
ep.ping # will print "receive ping!"
|
889
|
+
ep.pong 1 # will print "receive pong with 1!"
|
890
|
+
```
|
891
|
+
|
892
|
+
Now, imagine what you can do with a Parametrized Role: we can create all handles based on event names!
|
893
|
+
|
782
894
|
## IMPORTANT
|
783
895
|
|
784
896
|
This module is experimental. I should test more and more to be possible consider this "production ready". If you find some issue/bug please add here: https://github.com/peczenyj/MooseX/issues
|
data/lib/moosex.rb
CHANGED
@@ -175,18 +175,18 @@ module MooseX
|
|
175
175
|
after = @after[method_name]
|
176
176
|
around = @around[method_name]
|
177
177
|
|
178
|
-
klass.__meta_define_method(method_name) do |*args|
|
179
|
-
before.each{|b| b.call(self,*args)}
|
178
|
+
klass.__meta_define_method(method_name) do |*args, &proc|
|
179
|
+
before.each{|b| b.call(self,*args, &proc)}
|
180
180
|
|
181
|
-
original = lambda do |object, *args|
|
182
|
-
method.bind(object).call(*args)
|
181
|
+
original = lambda do |object, *args, &proc|
|
182
|
+
method.bind(object).call(*args, &proc)
|
183
183
|
end
|
184
184
|
|
185
185
|
result = around.inject(original) do |lambda1, lambda2|
|
186
186
|
lambda2.curry[lambda1]
|
187
|
-
end.call(self, *args)
|
187
|
+
end.call(self, *args, &proc)
|
188
188
|
|
189
|
-
after.each{|b| b.call(self,*args)}
|
189
|
+
after.each{|b| b.call(self,*args, &proc)}
|
190
190
|
|
191
191
|
result
|
192
192
|
end
|
@@ -217,9 +217,9 @@ module MooseX
|
|
217
217
|
begin
|
218
218
|
method = instance_method method_name
|
219
219
|
|
220
|
-
define_method method_name do |*args|
|
221
|
-
result = method.bind(self).call(*args)
|
222
|
-
block.call(self,*args)
|
220
|
+
define_method method_name do |*args, &proc|
|
221
|
+
result = method.bind(self).call(*args, &proc)
|
222
|
+
block.call(self,*args,&proc)
|
223
223
|
result
|
224
224
|
end
|
225
225
|
rescue => e
|
@@ -234,9 +234,9 @@ module MooseX
|
|
234
234
|
begin
|
235
235
|
method = instance_method method_name
|
236
236
|
|
237
|
-
define_method method_name do |*args|
|
238
|
-
block.call(self,*args)
|
239
|
-
method.bind(self).call(*args)
|
237
|
+
define_method method_name do |*args, &proc|
|
238
|
+
block.call(self,*args, &proc)
|
239
|
+
method.bind(self).call(*args, &proc)
|
240
240
|
end
|
241
241
|
rescue => e
|
242
242
|
MooseX.warn "unable to apply hook before in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
|
@@ -248,17 +248,17 @@ module MooseX
|
|
248
248
|
def around(*methods_name, &block)
|
249
249
|
methods_name.each do |method_name|
|
250
250
|
begin
|
251
|
+
|
251
252
|
method = instance_method method_name
|
252
253
|
|
253
|
-
code = Proc.new do | o, *a|
|
254
|
-
method.bind(o).call(*a)
|
254
|
+
code = Proc.new do | o, *a, &proc|
|
255
|
+
method.bind(o).call(*a,&proc)
|
255
256
|
end
|
256
257
|
|
257
|
-
define_method method_name do |*args|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
end
|
258
|
+
define_method method_name do |*args, &proc|
|
259
|
+
block.call(code, self,*args, &proc)
|
260
|
+
end
|
261
|
+
|
262
262
|
rescue => e
|
263
263
|
MooseX.warn "unable to apply hook around in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
|
264
264
|
__meta.add_around(method_name, block)
|
@@ -411,7 +411,15 @@ module MooseX
|
|
411
411
|
end
|
412
412
|
|
413
413
|
handles.map do |key,value|
|
414
|
-
|
414
|
+
if value.is_a? Hash
|
415
|
+
raise "ops! Handle should accept only one map / currying" unless value.count == 1
|
416
|
+
|
417
|
+
original, currying = value.shift
|
418
|
+
|
419
|
+
{ key.to_sym => [original.to_sym, currying] }
|
420
|
+
else
|
421
|
+
{ key.to_sym => value.to_sym }
|
422
|
+
end
|
415
423
|
end.reduce({}) do |hash,e|
|
416
424
|
hash.merge(e)
|
417
425
|
end
|
@@ -537,9 +545,26 @@ module MooseX
|
|
537
545
|
|
538
546
|
attr_symbol = @attr_symbol
|
539
547
|
@handles.each_pair do | method, target_method |
|
540
|
-
|
541
|
-
|
542
|
-
|
548
|
+
if target_method.is_a? Array
|
549
|
+
original, currying = target_method
|
550
|
+
|
551
|
+
@methods[method] = Proc.new do |*args, &proc|
|
552
|
+
|
553
|
+
a1 = [ currying ]
|
554
|
+
|
555
|
+
if currying.is_a?Proc
|
556
|
+
a1 = currying.call()
|
557
|
+
elsif currying.is_a? Array
|
558
|
+
a1 = currying.map{|c| (c.is_a?(Proc)) ? c.call : c }
|
559
|
+
end
|
560
|
+
|
561
|
+
self.send(attr_symbol).send(original, *a1, *args, &proc)
|
562
|
+
end
|
563
|
+
else
|
564
|
+
@methods[method] = Proc.new do |*args, &proc|
|
565
|
+
self.send(attr_symbol).send(target_method, *args, &proc)
|
566
|
+
end
|
567
|
+
end
|
543
568
|
end
|
544
569
|
end
|
545
570
|
|
data/lib/moosex/event.rb
CHANGED
@@ -59,8 +59,7 @@ module MooseX
|
|
59
59
|
listener
|
60
60
|
end
|
61
61
|
|
62
|
-
before(:
|
63
|
-
|
62
|
+
before(:on, :once, :emit) do |obj, event, *rest, &proc|
|
64
63
|
if ! obj.has_events.nil? && ! [ obj.has_events ].flatten.include?(event)
|
65
64
|
|
66
65
|
raise EventException, "Event '#{event.inspect}' not supported, this class only allow: #{obj.has_events.inspect}",caller
|
data/lib/moosex/version.rb
CHANGED
data/samples/events.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
require 'moosex/event'
|
3
|
+
|
4
|
+
class EventHandler
|
5
|
+
include MooseX
|
6
|
+
include MooseX::Event
|
7
|
+
|
8
|
+
def has_events
|
9
|
+
[ :pinged, :ponged ]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class EventProcessor
|
14
|
+
include MooseX
|
15
|
+
|
16
|
+
has event_handler: {
|
17
|
+
is: :ro,
|
18
|
+
isa: EventHandler,
|
19
|
+
default: lambda{ EventHandler.new },
|
20
|
+
handles: {
|
21
|
+
ping: { emit: :pinged },
|
22
|
+
pong: { emit: :ponged },
|
23
|
+
on_ping: { on: :pinged },
|
24
|
+
on_pong: { on: :ponged },
|
25
|
+
},
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
ep = EventProcessor.new()
|
30
|
+
|
31
|
+
ep.on_ping do |x|
|
32
|
+
puts "receive ping!"
|
33
|
+
end
|
34
|
+
|
35
|
+
ep.on_pong do |obj, message|
|
36
|
+
puts "receive pong with #{message}!"
|
37
|
+
end
|
38
|
+
|
39
|
+
ep.ping # will print "receive ping!"
|
40
|
+
ep.pong 1 # will print "receive pong!"
|
data/spec/coerce_spec.rb
CHANGED
data/spec/event_spec.rb
CHANGED
data/spec/hooks_spec.rb
CHANGED
@@ -120,4 +120,98 @@ describe "OtherPoint4D" do
|
|
120
120
|
p.z.should == 0
|
121
121
|
p.t.should == 0
|
122
122
|
end
|
123
|
-
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module ModuleHWB
|
126
|
+
include MooseX
|
127
|
+
|
128
|
+
requires :info
|
129
|
+
|
130
|
+
around(:call_with) do |original, object,x,&proc|
|
131
|
+
object.info(1,x)
|
132
|
+
|
133
|
+
result= original.call(object,x,&proc)
|
134
|
+
|
135
|
+
object.info(2,result)
|
136
|
+
|
137
|
+
result
|
138
|
+
end
|
139
|
+
|
140
|
+
before(:call_with) do |object,x, &k|
|
141
|
+
object.info(3, x)
|
142
|
+
end
|
143
|
+
|
144
|
+
after(:call_with) do |object,x,&k|
|
145
|
+
object.info(4, x)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class HooksWithBlocks
|
150
|
+
include MooseX
|
151
|
+
|
152
|
+
has logger: { is: :rw, handles: :info }
|
153
|
+
|
154
|
+
def call_with(x)
|
155
|
+
yield(x)
|
156
|
+
#proc.call(x)
|
157
|
+
end
|
158
|
+
|
159
|
+
include ModuleHWB
|
160
|
+
end
|
161
|
+
|
162
|
+
class HooksWithBlocks2
|
163
|
+
include MooseX
|
164
|
+
|
165
|
+
has logger: { is: :rw, handles: :info }
|
166
|
+
|
167
|
+
def call_with(x, &proc)
|
168
|
+
#yield(x)
|
169
|
+
proc.call(x)
|
170
|
+
end
|
171
|
+
|
172
|
+
around(:call_with) do |original, object,x,&proc|
|
173
|
+
object.info(1,x)
|
174
|
+
|
175
|
+
result= original.call(object,x,&proc)
|
176
|
+
|
177
|
+
object.info(2,result)
|
178
|
+
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
before(:call_with) do |object,x, &k|
|
183
|
+
object.info(3, x)
|
184
|
+
end
|
185
|
+
|
186
|
+
after(:call_with) do |object,x,&k|
|
187
|
+
object.info(4, x)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe HooksWithBlocks do
|
192
|
+
it "should call all hooks" do
|
193
|
+
logger = double
|
194
|
+
logger.should_receive(:info).with(3,7).once()
|
195
|
+
logger.should_receive(:info).with(1,7).once()
|
196
|
+
logger.should_receive(:info).with(2,8).once()
|
197
|
+
logger.should_receive(:info).with(4,7).once()
|
198
|
+
|
199
|
+
x = HooksWithBlocks.new(logger: logger)
|
200
|
+
x.call_with(7){|x| x+1}
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe HooksWithBlocks2 do
|
205
|
+
it "should call all hooks" do
|
206
|
+
logger = double
|
207
|
+
logger.should_receive(:info).with(3,7).once()
|
208
|
+
logger.should_receive(:info).with(1,7).once()
|
209
|
+
logger.should_receive(:info).with(2,8).once()
|
210
|
+
logger.should_receive(:info).with(4,7).once()
|
211
|
+
|
212
|
+
x = HooksWithBlocks2.new(logger: logger)
|
213
|
+
x.call_with(7) do |x|
|
214
|
+
x+1
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/spec/proxy_spec.rb
CHANGED
@@ -5,17 +5,37 @@ class ProxyToTarget
|
|
5
5
|
|
6
6
|
has target: {
|
7
7
|
is: :ro,
|
8
|
-
default: lambda { Target.new },
|
9
|
-
handles: {
|
10
|
-
my_method_x: :method_x,
|
11
|
-
my_method_y: :method_y,
|
12
|
-
|
8
|
+
default: lambda { Target.new }, # default, new instace of Target
|
9
|
+
handles: { # handles is for delegation,
|
10
|
+
my_method_x: :method_x, # inject methods with new names
|
11
|
+
my_method_y: :method_y, # old => obj.target.method_x , now => obj.my_method_x
|
12
|
+
my_method_y_with_1: { # currying!!!
|
13
|
+
method_y: 1, # call obj.mymethod_z(2,3) is the equivalent to
|
14
|
+
}, # call obj.target.method_z(1,2,3)
|
15
|
+
my_method_y_with_lambda: { # currying!!!
|
16
|
+
method_y: lambda{ 1 }, # call obj.mymethod_z(2,3) is the equivalent to
|
17
|
+
}, # call obj.target.method_z(1,2,3)
|
18
|
+
my_method_z_with_array: {
|
19
|
+
method_z: [1,lambda{ 2 } ,3]
|
20
|
+
},
|
21
|
+
my_method_k_with_literal_array: {
|
22
|
+
method_k: [[1,2,3]]
|
23
|
+
},
|
24
|
+
my_method_k_with_literal_array2: {
|
25
|
+
method_k: [ lambda{ [1,2,3] } ]
|
26
|
+
},
|
27
|
+
my_method_k_with_literal_array3: {
|
28
|
+
method_k: lambda{ [[1,2,3]] }
|
29
|
+
}
|
30
|
+
}
|
13
31
|
}
|
14
32
|
end
|
15
33
|
|
16
34
|
module TargetModule
|
17
35
|
def method_x; 1024; end # works with simple methods
|
18
36
|
def method_y(a,b,c); a + b + c; end # or methods with arguments
|
37
|
+
def method_z(*args); args.reduce(:+); end
|
38
|
+
def method_k(array); array.count; end
|
19
39
|
end
|
20
40
|
|
21
41
|
class Target
|
@@ -36,7 +56,48 @@ describe "ProxyToTarget" do
|
|
36
56
|
p.target.method_y(1,2,3).should == 6
|
37
57
|
p.my_method_y(1,2,3).should == 6
|
38
58
|
end
|
59
|
+
|
60
|
+
it "should delegate method_y to the target with currying" do
|
61
|
+
p = ProxyToTarget.new
|
62
|
+
|
63
|
+
p.target.method_y(1,2,3).should == 6
|
64
|
+
p.my_method_y_with_1(2,3).should == 6
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should delegate method_y to the target with currying as lambda" do
|
68
|
+
p = ProxyToTarget.new
|
69
|
+
|
70
|
+
p.target.method_y(1,2,3).should == 6
|
71
|
+
p.my_method_y_with_lambda(2,3).should == 6
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should delegate method_z to the target with currying (many args) as lambda" do
|
75
|
+
p = ProxyToTarget.new
|
76
|
+
|
77
|
+
p.target.method_z(1,2,3,4).should == 10
|
78
|
+
p.my_method_z_with_array(4).should == 10
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should delegate method_k to the target with currying (many args)" do
|
82
|
+
p = ProxyToTarget.new
|
83
|
+
|
84
|
+
p.target.method_k([1,2,3]).should == 3
|
85
|
+
p.my_method_k_with_literal_array().should == 3
|
86
|
+
end
|
39
87
|
|
88
|
+
it "should delegate method_k to the target with currying (many args) as lambda" do
|
89
|
+
p = ProxyToTarget.new
|
90
|
+
|
91
|
+
p.target.method_k([1,2,3]).should == 3
|
92
|
+
p.my_method_k_with_literal_array2().should == 3
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should delegate method_k to the target with currying (many args) as lambda (2)" do
|
96
|
+
p = ProxyToTarget.new
|
97
|
+
|
98
|
+
p.target.method_k([1,2,3]).should == 3
|
99
|
+
p.my_method_k_with_literal_array3().should == 3
|
100
|
+
end
|
40
101
|
it "should inject method_y" do
|
41
102
|
p = ProxyToTarget.new
|
42
103
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moosex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Peczenyj
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- lib/moosex/types.rb
|
92
92
|
- lib/moosex/version.rb
|
93
93
|
- moosex.gemspec
|
94
|
+
- samples/events.rb
|
94
95
|
- samples/point.rb
|
95
96
|
- samples/roles.rb
|
96
97
|
- spec/baserole_spec.rb
|