moosex 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +285 -173
- data/lib/moosex.rb +56 -47
- data/lib/moosex/version.rb +1 -1
- data/moosex.gemspec +2 -2
- data/samples/point.rb +59 -0
- data/spec/baz_spec.rb +85 -0
- data/spec/build_spec.rb +23 -0
- data/spec/buildargs_spec.rb +106 -0
- data/spec/coerce_spec.rb +51 -0
- data/spec/foo_spec.rb +40 -0
- data/spec/lazy_spec.rb +126 -0
- data/spec/lol_spec.rb +42 -0
- data/spec/moosex_spec.rb +7 -746
- data/spec/point_spec.rb +201 -0
- data/spec/proxy_spec.rb +145 -0
- data/spec/trigger_spec.rb +83 -0
- metadata +26 -5
data/lib/moosex.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# Module MooseX
|
2
|
-
# A postmodern object
|
2
|
+
# A postmodern object DSL for Ruby
|
3
|
+
#
|
4
|
+
# Author:: Tiago Peczenyj (mailto:tiago.peczenyj@gmail.com)
|
5
|
+
# Copyright:: Copyright (c) 2014 Tiago Peczenyj
|
6
|
+
# License:: MIT
|
3
7
|
#
|
4
|
-
# MooseX is an extension of Ruby object system. The main goal of MooseX is to make Ruby Object Oriented programming easier, more consistent, and less tedious. With MooseX you can think more about what you want to do and less about the mechanics of OOP. It is a port of Moose/Moo from Perl to Ruby world.
|
5
|
-
|
6
8
|
require "moosex/version"
|
7
9
|
|
8
10
|
module MooseX
|
@@ -17,16 +19,16 @@ module MooseX
|
|
17
19
|
define_singleton_method(:__meta) { meta }
|
18
20
|
end
|
19
21
|
|
20
|
-
def initialize(args
|
21
|
-
args = BUILDARGS(args)
|
22
|
-
|
23
|
-
self.class.__meta().init(self, args)
|
22
|
+
def initialize(*args)
|
23
|
+
args = BUILDARGS(*args)
|
24
|
+
|
25
|
+
self.class.__meta().init(self, args || {})
|
24
26
|
|
25
27
|
BUILD()
|
26
28
|
end
|
27
29
|
|
28
|
-
def BUILDARGS(args)
|
29
|
-
args
|
30
|
+
def BUILDARGS(*args)
|
31
|
+
args[0]
|
30
32
|
end
|
31
33
|
|
32
34
|
def BUILD
|
@@ -74,20 +76,15 @@ module MooseX
|
|
74
76
|
|
75
77
|
attr = MooseX::Attribute.new(attr_name, attr_options)
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
when :rw
|
85
|
-
define_method attr.writter, &s
|
86
|
-
|
87
|
-
when :rwp
|
88
|
-
define_method attr.writter, &s
|
89
|
-
|
79
|
+
attr.methods.each_pair do |method, proc|
|
80
|
+
define_method method, &proc
|
81
|
+
end
|
82
|
+
|
83
|
+
if attr.is.eql?(:rwp)
|
84
|
+
private attr.writter
|
85
|
+
elsif attr.is.eql?(:private)
|
90
86
|
private attr.writter
|
87
|
+
private attr.reader
|
91
88
|
end
|
92
89
|
|
93
90
|
__meta.add(attr)
|
@@ -97,7 +94,7 @@ module MooseX
|
|
97
94
|
|
98
95
|
class Attribute
|
99
96
|
|
100
|
-
attr_reader :attr_symbol, :is, :reader, :writter, :lazy, :builder
|
97
|
+
attr_reader :attr_symbol, :is, :reader, :writter, :lazy, :builder, :methods
|
101
98
|
DEFAULTS= {
|
102
99
|
lazy: false,
|
103
100
|
clearer: false,
|
@@ -113,8 +110,8 @@ module MooseX
|
|
113
110
|
|
114
111
|
VALIDATE = {
|
115
112
|
is: lambda do |is, field_name|
|
116
|
-
unless [:rw, :rwp, :ro, :lazy].include?(is)
|
117
|
-
raise "invalid value for field '#{field_name}' is '#{is}', must be one of :rw, :rwp, :ro or :lazy"
|
113
|
+
unless [:rw, :rwp, :ro, :lazy, :private].include?(is)
|
114
|
+
raise "invalid value for field '#{field_name}' is '#{is}', must be one of :private, :rw, :rwp, :ro or :lazy"
|
118
115
|
end
|
119
116
|
end,
|
120
117
|
};
|
@@ -161,7 +158,7 @@ module MooseX
|
|
161
158
|
if ! clearer
|
162
159
|
return false
|
163
160
|
elsif clearer.is_a? TrueClass
|
164
|
-
return "
|
161
|
+
return "clear_#{field_name}!".to_sym
|
165
162
|
end
|
166
163
|
|
167
164
|
begin
|
@@ -252,6 +249,7 @@ module MooseX
|
|
252
249
|
};
|
253
250
|
|
254
251
|
def initialize(a, o)
|
252
|
+
#o ||= {}
|
255
253
|
# todo extract this to a framework, see issue #21 on facebook
|
256
254
|
o = DEFAULTS.merge({
|
257
255
|
reader: a,
|
@@ -276,13 +274,16 @@ module MooseX
|
|
276
274
|
validate.call(o[field], a)
|
277
275
|
end
|
278
276
|
|
279
|
-
if o[:is].eql? :
|
277
|
+
if o[:is].eql? :ro
|
278
|
+
o[:writter] = nil
|
279
|
+
elsif o[:is].eql? :lazy
|
280
280
|
o[:lazy] = true
|
281
|
+
o[:writter] = nil
|
281
282
|
end
|
282
283
|
|
283
284
|
unless o[:lazy]
|
284
285
|
o[:builder] = nil
|
285
|
-
end
|
286
|
+
end
|
286
287
|
|
287
288
|
@attr_symbol = a
|
288
289
|
@is = o[:is]
|
@@ -299,35 +300,42 @@ module MooseX
|
|
299
300
|
@init_arg = o[:init_arg]
|
300
301
|
@trigger = o[:trigger]
|
301
302
|
@coerce = o[:coerce]
|
302
|
-
|
303
|
-
|
304
|
-
def init(object, args)
|
305
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
306
|
-
|
307
|
-
value = nil
|
303
|
+
@methods = {}
|
308
304
|
|
309
|
-
|
310
|
-
|
311
|
-
object.define_singleton_method method do |*args|
|
312
|
-
self.send(attr_symbol).send(target_method, *args)
|
313
|
-
end
|
305
|
+
if @reader
|
306
|
+
@methods[@reader] = generate_reader
|
314
307
|
end
|
315
|
-
|
308
|
+
|
309
|
+
if @writter
|
310
|
+
@methods[@writter] = generate_writter
|
311
|
+
end
|
312
|
+
inst_variable_name = "@#{@attr_symbol}".to_sym
|
316
313
|
if @predicate
|
317
|
-
|
314
|
+
@methods[@predicate] = Proc.new do
|
318
315
|
instance_variable_defined? inst_variable_name
|
319
316
|
end
|
320
317
|
end
|
321
318
|
|
322
319
|
if @clearer
|
323
|
-
|
320
|
+
@methods[@clearer] = Proc.new do
|
324
321
|
if instance_variable_defined? inst_variable_name
|
325
322
|
remove_instance_variable inst_variable_name
|
326
323
|
end
|
327
324
|
end
|
328
325
|
end
|
329
|
-
|
326
|
+
|
327
|
+
attr_symbol = @attr_symbol
|
328
|
+
@handles.each_pair do | method, target_method |
|
329
|
+
@methods[method] = Proc.new do |*args|
|
330
|
+
self.send(attr_symbol).send(target_method, *args)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def init(object, args)
|
336
|
+
value = nil
|
330
337
|
value_from_default = false
|
338
|
+
|
331
339
|
if args.has_key? @init_arg
|
332
340
|
value = args[ @init_arg ]
|
333
341
|
elsif @default
|
@@ -347,11 +355,12 @@ module MooseX
|
|
347
355
|
@trigger.call(object, value)
|
348
356
|
end
|
349
357
|
|
358
|
+
inst_variable_name = "@#{@attr_symbol}".to_sym
|
350
359
|
object.instance_variable_set inst_variable_name, value
|
351
|
-
|
352
360
|
end
|
353
|
-
|
354
|
-
|
361
|
+
|
362
|
+
private
|
363
|
+
def generate_reader
|
355
364
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
356
365
|
|
357
366
|
builder = @builder
|
@@ -378,7 +387,7 @@ module MooseX
|
|
378
387
|
end
|
379
388
|
end
|
380
389
|
|
381
|
-
def
|
390
|
+
def generate_writter
|
382
391
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
383
392
|
coerce = @coerce
|
384
393
|
type_check = @isa
|
data/lib/moosex/version.rb
CHANGED
data/moosex.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Moosex::VERSION
|
9
9
|
spec.authors = ["Tiago Peczenyj"]
|
10
10
|
spec.email = ["tiago.peczenyj@gmail.com"]
|
11
|
-
spec.summary = %q{A postmodern object
|
12
|
-
spec.description = %q{MooseX is an extension of Ruby object
|
11
|
+
spec.summary = %q{A postmodern object DSL for Ruby}
|
12
|
+
spec.description = %q{MooseX is an extension of Ruby object DSL. The main goal of MooseX is to make Ruby Object Oriented programming easier, more consistent, and less tedious. With MooseX you can think more about what you want to do and less about the mechanics of OOP. It is a port of Moose/Moo from Perl to Ruby world.}
|
13
13
|
spec.homepage = "http://github.com/peczenyj/MooseX"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
data/samples/point.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
|
3
|
+
class Point
|
4
|
+
include MooseX
|
5
|
+
|
6
|
+
has x: {
|
7
|
+
is: :rw, # read-write (mandatory)
|
8
|
+
isa: Integer, # should be Integer
|
9
|
+
default: 0, # default value is 0 (constant)
|
10
|
+
}
|
11
|
+
|
12
|
+
has y: {
|
13
|
+
is: :rw,
|
14
|
+
isa: Integer,
|
15
|
+
default: lambda { 0 }, # you should specify a lambda
|
16
|
+
}
|
17
|
+
|
18
|
+
def clear!
|
19
|
+
self.x= 0 # to run with type-check you must
|
20
|
+
self.y= 0 # use the setter instad @x=
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"Point[x=#{self.x}, y=#{self.y}]"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Point3D < Point
|
29
|
+
has z: {
|
30
|
+
is: :rw, # read-write (mandatory)
|
31
|
+
isa: Integer, # should be Integer
|
32
|
+
default: 0, # default value is 0 (constant)
|
33
|
+
}
|
34
|
+
|
35
|
+
def clear!
|
36
|
+
self.x= 0 # to run with type-check you must
|
37
|
+
self.y= 0 # use the setter instad @x=
|
38
|
+
self.z= 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"Point[x=#{self.x}, y=#{self.y}, z=#{self.z}]"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
p1 = Point.new(x: 4, y:5)
|
47
|
+
p2 = Point.new()
|
48
|
+
p3 = Point3D.new(x: 4, y:5, z:6)
|
49
|
+
p4 = Point3D.new(x: 4, y:5)
|
50
|
+
p5 = Point3D.new()
|
51
|
+
|
52
|
+
puts ">> objects"
|
53
|
+
puts p1, p2, p3, p4, p5
|
54
|
+
|
55
|
+
p1.clear!
|
56
|
+
p3.clear!
|
57
|
+
|
58
|
+
puts ">> clear"
|
59
|
+
puts p1, p3
|
data/spec/baz_spec.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
|
3
|
+
class Baz
|
4
|
+
include MooseX
|
5
|
+
|
6
|
+
has bam: {
|
7
|
+
is: :ro, # read-only, you should specify in new only
|
8
|
+
isa: lambda do |bam| # you should add your own validator
|
9
|
+
raise 'bam should be less than 100' if bam > 100
|
10
|
+
end,
|
11
|
+
required: true,
|
12
|
+
}
|
13
|
+
|
14
|
+
has boom: {
|
15
|
+
is: :rw,
|
16
|
+
predicate: true, # add has_boom? method, ask if the attribute is unset
|
17
|
+
clearer: true, # add clear_boom! method, unset the attribute
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "Baz" do
|
22
|
+
it "should require bam if necessary" do
|
23
|
+
baz = Baz.new( bam: 99 )
|
24
|
+
baz.bam.should == 99
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not be possible update baz (read only)" do
|
28
|
+
baz = Baz.new( bam: 99 )
|
29
|
+
expect {
|
30
|
+
baz.bam = 1024
|
31
|
+
}.to raise_error(NoMethodError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should run the lambda isa" do
|
35
|
+
expect {
|
36
|
+
Baz.new( bam: 199 )
|
37
|
+
}.to raise_error(/bam should be less than 100/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should inject methods in the class (predicate)" do
|
41
|
+
baz = Baz.new( bam: 99 )
|
42
|
+
|
43
|
+
baz.respond_to?(:has_boom?).should be_true
|
44
|
+
Baz.instance_methods.member?(:has_boom?).should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should inject methods in the class (clearer)" do
|
48
|
+
baz = Baz.new( bam: 99 )
|
49
|
+
|
50
|
+
baz.respond_to?(:clear_boom!).should be_true
|
51
|
+
Baz.instance_methods.member?(:clear_boom!).should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "rw acessor should has nil value, supports predicate" do
|
55
|
+
baz = Baz.new( bam: 99 )
|
56
|
+
|
57
|
+
baz.has_boom?.should be_false
|
58
|
+
baz.boom.should be_nil
|
59
|
+
baz.boom= 0
|
60
|
+
baz.has_boom?.should be_true
|
61
|
+
baz.boom.should be_zero
|
62
|
+
end
|
63
|
+
|
64
|
+
it "rw acessor should has nil value, supports clearer" do
|
65
|
+
baz = Baz.new( bam: 99, boom: 0 )
|
66
|
+
|
67
|
+
baz.has_boom?.should be_true
|
68
|
+
baz.boom.should be_zero
|
69
|
+
|
70
|
+
baz.clear_boom!
|
71
|
+
|
72
|
+
baz.has_boom?.should be_false
|
73
|
+
baz.boom.should be_nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be possible call the clearer twice" do
|
77
|
+
baz = Baz.new( bam: 99, boom: 0 )
|
78
|
+
|
79
|
+
baz.clear_boom!
|
80
|
+
baz.clear_boom!
|
81
|
+
|
82
|
+
baz.has_boom?.should be_false
|
83
|
+
baz.boom.should be_nil
|
84
|
+
end
|
85
|
+
end
|
data/spec/build_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
|
3
|
+
class BuildExample
|
4
|
+
include MooseX
|
5
|
+
|
6
|
+
has [:x, :y], {
|
7
|
+
is: :rw,
|
8
|
+
required: true,
|
9
|
+
}
|
10
|
+
def BUILD
|
11
|
+
if self.x == self.y
|
12
|
+
raise "invalid: you should use x != y"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "BuildExample" do
|
18
|
+
it "should raise exception on build" do
|
19
|
+
expect {
|
20
|
+
BuildExample.new(x: 0, y: 0)
|
21
|
+
}.to raise_error(/invalid: you should use x != y/)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
|
3
|
+
class BuildArgsExample
|
4
|
+
include MooseX
|
5
|
+
|
6
|
+
has [:x, :y], {
|
7
|
+
is: :rw,
|
8
|
+
required: true,
|
9
|
+
}
|
10
|
+
|
11
|
+
def BUILDARGS(args)
|
12
|
+
|
13
|
+
args[:x] = 1024
|
14
|
+
args[:y] = - args[:y]
|
15
|
+
|
16
|
+
args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "BuildArgsExample" do
|
21
|
+
it "should create the object" do
|
22
|
+
ex = BuildArgsExample.new(x: 10, y: -2)
|
23
|
+
ex.x.should == 1024
|
24
|
+
ex.y.should == 2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class BuildArgsExample2
|
29
|
+
include MooseX
|
30
|
+
|
31
|
+
has [:x, :y], {
|
32
|
+
is: :rw,
|
33
|
+
required: true,
|
34
|
+
}
|
35
|
+
|
36
|
+
def BUILDARGS(x=4,y=8)
|
37
|
+
args = {}
|
38
|
+
args[:x] = x
|
39
|
+
args[:y] = y
|
40
|
+
|
41
|
+
args
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "BuildArgsExample2" do
|
46
|
+
it "should create the object" do
|
47
|
+
ex = BuildArgsExample2.new(1,2)
|
48
|
+
ex.x.should == 1
|
49
|
+
ex.y.should == 2
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should create the object II" do
|
53
|
+
ex = BuildArgsExample2.new(1)
|
54
|
+
ex.x.should == 1
|
55
|
+
ex.y.should == 8
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should create the object III" do
|
59
|
+
ex = BuildArgsExample2.new()
|
60
|
+
ex.x.should == 4
|
61
|
+
ex.y.should == 8
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class BuildArgsExample3
|
66
|
+
include MooseX
|
67
|
+
|
68
|
+
has [:x, :y], {
|
69
|
+
is: :rw,
|
70
|
+
required: true,
|
71
|
+
}
|
72
|
+
|
73
|
+
def BUILDARGS(x: 4,y: 8)
|
74
|
+
args = {}
|
75
|
+
args[:x] = x
|
76
|
+
args[:y] = y
|
77
|
+
|
78
|
+
args
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "BuildArgsExample3" do
|
83
|
+
it "should create the object" do
|
84
|
+
ex = BuildArgsExample3.new(x: 1, y:2)
|
85
|
+
ex.x.should == 1
|
86
|
+
ex.y.should == 2
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should create the object II" do
|
90
|
+
ex = BuildArgsExample3.new(x: 1)
|
91
|
+
ex.x.should == 1
|
92
|
+
ex.y.should == 8
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should create the object III" do
|
96
|
+
ex = BuildArgsExample3.new()
|
97
|
+
ex.x.should == 4
|
98
|
+
ex.y.should == 8
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should create the object III" do
|
102
|
+
ex = BuildArgsExample3.new(y: 6)
|
103
|
+
ex.x.should == 4
|
104
|
+
ex.y.should == 6
|
105
|
+
end
|
106
|
+
end
|