moosex 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Changelog +6 -0
- data/README.md +21 -3
- data/Rakefile +3 -0
- data/lib/moosex.rb +146 -41
- data/lib/moosex/version.rb +1 -1
- data/spec/moosex_spec.rb +120 -40
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14b97eb6c1912038ced4647e89e4ebe2c10092d7
|
4
|
+
data.tar.gz: 1d1bcca3b65ea98b49b04bde2769edc5f2ffe676
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85aa1e9641e90f307dc37c29c4dbb8ba1b68dcf7784d7ab688638ac57351e790fc37f6459ebb6ca578dec571952c18f748aaef687159b98f62bc689750d2f672
|
7
|
+
data.tar.gz: 46c1e856c2c6f53d56e549ce5e7fb9a12158c1a6bb0019c0ecfa1b3a2b174db47ce02ba29f3dcdf18dbaf13c6cfe5d9016b2ed1cf52a41b6d4d0e46d19aad4bd
|
data/.travis.yml
ADDED
data/Changelog
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
0.0.3 - 2014-01-31
|
2
|
+
- add support to clearer #26
|
3
|
+
- add support to predicate #11
|
4
|
+
- add support to handle more than one attribute #2
|
5
|
+
- small improvements
|
6
|
+
|
1
7
|
0.0.2 - 2014-01-31
|
2
8
|
- fix one important bug, not it is possible use in more than one class
|
3
9
|
- add require option
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# MooseX
|
2
2
|
|
3
|
-
A postmodern object system for Ruby
|
3
|
+
A postmodern object system for Ruby [](https://travis-ci.org/peczenyj/MooseX)
|
4
4
|
|
5
5
|
```ruby
|
6
6
|
require 'moosex'
|
@@ -32,7 +32,7 @@ A postmodern object system for Ruby
|
|
32
32
|
has :bar, {
|
33
33
|
:is => :rwp, # read-write-private (private setter)
|
34
34
|
:isa => Integer,
|
35
|
-
:required => true # you should require in the constructor
|
35
|
+
:required => true, # you should require in the constructor
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
@@ -44,11 +44,29 @@ A postmodern object system for Ruby
|
|
44
44
|
:isa => lambda {|x| # you should add your own validator
|
45
45
|
raise 'x should be less than 100' if x > 100
|
46
46
|
},
|
47
|
-
:required => true
|
47
|
+
:required => true,
|
48
|
+
:predicate => true, # add has_bam? method, ask if the attribute is unset
|
49
|
+
:clearer => true, # add reset_bam! method, unset the attribute
|
48
50
|
}
|
49
51
|
|
50
52
|
end
|
51
53
|
|
54
|
+
class Lol
|
55
|
+
include MooseX
|
56
|
+
|
57
|
+
has [:a, :b], { # define attributes a and b
|
58
|
+
:is => :ro, # with same set of properties
|
59
|
+
:default => 0,
|
60
|
+
}
|
61
|
+
|
62
|
+
has :c => { # alternative syntax to be
|
63
|
+
:is => :ro, # more similar to Moo/Moose
|
64
|
+
:default => 1,
|
65
|
+
:predicate => :can_haz_c?, # custom predicate
|
66
|
+
:clearer => "desintegrate_c", # force coerce to symbol
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
52
70
|
# now you have a generic constructor
|
53
71
|
p1 = Point.new # x and y will be 0
|
54
72
|
p2 = Point.new( :x => 5 ) # y will be 0
|
data/Rakefile
ADDED
data/lib/moosex.rb
CHANGED
@@ -12,14 +12,9 @@ module MooseX
|
|
12
12
|
o.extend(MooseX::Core)
|
13
13
|
|
14
14
|
o.class_exec do
|
15
|
-
meta = MooseX::Meta.new
|
16
|
-
|
17
|
-
#class_variable_set "@@meta".to_sym, meta
|
15
|
+
meta = MooseX::Meta.new
|
18
16
|
|
19
|
-
define_singleton_method
|
20
|
-
meta
|
21
|
-
# class_variable_get "@@meta".to_sym
|
22
|
-
end
|
17
|
+
define_singleton_method(:__meta) { meta }
|
23
18
|
end
|
24
19
|
|
25
20
|
def initialize(args={})
|
@@ -32,56 +27,175 @@ module MooseX
|
|
32
27
|
|
33
28
|
module Core
|
34
29
|
|
35
|
-
def has(attr_name, attr_options)
|
36
|
-
|
30
|
+
def has(attr_name, attr_options = {})
|
31
|
+
if attr_name.is_a? Array
|
32
|
+
attr_name.each do |attr|
|
33
|
+
has(attr, attr_options)
|
34
|
+
end
|
35
|
+
elsif attr_name.is_a? Hash
|
36
|
+
attr_name.each_pair do |attr, options |
|
37
|
+
has(attr, options)
|
38
|
+
end
|
39
|
+
else
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
define_method attr_name, &g
|
41
|
-
|
42
|
-
s = attr.generate_setter
|
43
|
-
|
44
|
-
if attr_options[:is].eql? :rw
|
45
|
-
|
46
|
-
define_method "#{attr_name}=", &s
|
47
|
-
|
48
|
-
elsif attr_options[:is].eql? :rwp
|
41
|
+
attr = MooseX::Attribute.new(attr_name, attr_options)
|
49
42
|
|
50
|
-
|
43
|
+
g = attr.generate_getter
|
44
|
+
|
45
|
+
define_method attr.attr_symbol, &g
|
46
|
+
|
47
|
+
s = attr.generate_setter
|
48
|
+
|
49
|
+
case attr.is
|
50
|
+
when :rw
|
51
|
+
define_method "#{attr.attr_symbol}=", &s
|
51
52
|
|
52
|
-
|
53
|
+
when :rwp
|
54
|
+
define_method "#{attr.attr_symbol}=", &s
|
55
|
+
|
56
|
+
private "#{attr.attr_symbol}="
|
57
|
+
end
|
53
58
|
|
59
|
+
__meta.add(attr)
|
54
60
|
end
|
55
|
-
|
56
|
-
__meta.add(attr)
|
57
61
|
end
|
58
|
-
|
59
62
|
end
|
60
63
|
|
61
64
|
class Attribute
|
62
65
|
|
66
|
+
attr_reader :attr_symbol, :is, :isa, :default, :required
|
67
|
+
|
68
|
+
DEFAULTS= {
|
69
|
+
:clearer => false,
|
70
|
+
:required => false,
|
71
|
+
:predicate => false,
|
72
|
+
:isa => lambda { |x| true },
|
73
|
+
}
|
74
|
+
|
75
|
+
REQUIRED = [ :is ]
|
76
|
+
|
77
|
+
VALIDATE = {
|
78
|
+
:is => lambda do |is, field_name|
|
79
|
+
unless [:rw, :rwp, :ro, :lazy].include?(is)
|
80
|
+
raise "invalid value for field '#{field_name}' is '#{is}', must be one of :rw, :rwp, :ro or :lazy"
|
81
|
+
end
|
82
|
+
end,
|
83
|
+
};
|
84
|
+
|
85
|
+
COERCE = {
|
86
|
+
:is => lambda do |is, field_name|
|
87
|
+
is.to_sym
|
88
|
+
end,
|
89
|
+
:isa => lambda do |isa, field_name|
|
90
|
+
return isa if isa.is_a? Proc
|
91
|
+
|
92
|
+
return lambda do |new_value|
|
93
|
+
unless new_value.is_a?(isa)
|
94
|
+
raise "isa check for \"#{field_name}\" failed: is not instance of #{isa}!"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end,
|
98
|
+
:default => lambda do |default, field_name|
|
99
|
+
return default if default.is_a? Proc
|
100
|
+
|
101
|
+
return lambda { default }
|
102
|
+
end,
|
103
|
+
:required => lambda do |required, field_name|
|
104
|
+
!!required
|
105
|
+
end,
|
106
|
+
:predicate => lambda do |predicate, field_name|
|
107
|
+
begin
|
108
|
+
if ! predicate
|
109
|
+
return false
|
110
|
+
elsif predicate.is_a? TrueClass
|
111
|
+
return "has_#{field_name}?".to_sym,
|
112
|
+
end
|
113
|
+
|
114
|
+
return predicate.to_sym
|
115
|
+
rescue e
|
116
|
+
# create a nested exception here
|
117
|
+
raise "cannot coerce field predicate to a symbol for #{field_name}: #{e}"
|
118
|
+
end
|
119
|
+
end,
|
120
|
+
:clearer => lambda do |clearer, field_name|
|
121
|
+
begin
|
122
|
+
if ! clearer
|
123
|
+
return false
|
124
|
+
elsif clearer.is_a? TrueClass
|
125
|
+
return "reset_#{field_name}!".to_sym,
|
126
|
+
end
|
127
|
+
|
128
|
+
return clearer.to_sym
|
129
|
+
rescue e
|
130
|
+
# create a nested exception here
|
131
|
+
raise "cannot coerce field clearer to a symbol for #{field_name}: #{e}"
|
132
|
+
end
|
133
|
+
end,
|
134
|
+
};
|
135
|
+
|
63
136
|
def initialize(a, o)
|
137
|
+
# todo extract this to a framework, see issue #21 on facebook
|
138
|
+
o = DEFAULTS.merge(o)
|
139
|
+
|
140
|
+
REQUIRED.each { |field|
|
141
|
+
unless o.has_key?(field)
|
142
|
+
raise "field #{field} is required for Attribute #{a}"
|
143
|
+
end
|
144
|
+
}
|
145
|
+
COERCE.each_pair do |field, coerce|
|
146
|
+
if o.has_key? field
|
147
|
+
o[field] = coerce.call(o[field], a)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
VALIDATE.each_pair do |field, validate|
|
151
|
+
return if ! o.has_key? field
|
152
|
+
|
153
|
+
validate.call(o[field], a)
|
154
|
+
end
|
155
|
+
|
64
156
|
@attr_symbol = a
|
65
|
-
@
|
157
|
+
@is = o[:is]
|
158
|
+
@isa = o[:isa]
|
159
|
+
@default = o[:default]
|
160
|
+
@required = o[:required]
|
161
|
+
@predicate = o[:predicate]
|
162
|
+
@clearer = o[:clearer]
|
66
163
|
end
|
67
164
|
|
68
165
|
def init(object, args)
|
166
|
+
inst_variable_name = "@#{@attr_symbol}".to_sym
|
167
|
+
|
69
168
|
setter = @attr_symbol.to_s.concat("=").to_sym
|
70
169
|
value = nil
|
71
|
-
|
170
|
+
|
171
|
+
if @predicate
|
172
|
+
object.define_singleton_method @predicate do
|
173
|
+
instance_variable_defined? inst_variable_name
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
if @clearer
|
178
|
+
object.define_singleton_method @clearer do
|
179
|
+
if instance_variable_defined? inst_variable_name
|
180
|
+
remove_instance_variable inst_variable_name
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
72
185
|
if args.has_key? @attr_symbol
|
73
186
|
value = args[ @attr_symbol ]
|
74
|
-
elsif @
|
187
|
+
elsif @required
|
75
188
|
raise "attr \"#{@attr_symbol}\" is required"
|
189
|
+
elsif @default
|
190
|
+
value = @default.call
|
76
191
|
else
|
77
|
-
|
192
|
+
return
|
78
193
|
end
|
79
194
|
|
80
|
-
if @
|
195
|
+
if @is.eql? :ro
|
81
196
|
|
82
197
|
# TODO: remove redundancy
|
83
198
|
|
84
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
85
199
|
type_check = generate_type_check
|
86
200
|
type_check.call(value)
|
87
201
|
object.instance_variable_set inst_variable_name, value
|
@@ -108,17 +222,8 @@ module MooseX
|
|
108
222
|
end
|
109
223
|
|
110
224
|
def generate_type_check
|
111
|
-
if @options.has_key? :isa
|
112
|
-
isa = @options[:isa]
|
113
|
-
|
114
|
-
return isa if isa.is_a? Proc
|
115
225
|
|
116
|
-
|
117
|
-
raise "isa check for \"#{@attr_symbol}\" failed: lol is not #{isa}!" unless new_value.is_a? isa
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
lambda { |new_value| }
|
226
|
+
return @isa
|
122
227
|
end
|
123
228
|
end
|
124
229
|
|
data/lib/moosex/version.rb
CHANGED
data/spec/moosex_spec.rb
CHANGED
@@ -37,13 +37,38 @@ class Baz
|
|
37
37
|
|
38
38
|
has :bam, {
|
39
39
|
:is => :ro,
|
40
|
-
:isa => lambda {|
|
40
|
+
:isa => lambda {|bam| raise 'bam should be less than 100' if bam > 100},
|
41
41
|
:required => true
|
42
42
|
}
|
43
|
+
has :boom, {
|
44
|
+
:is => :rw,
|
45
|
+
:predicate => true,
|
46
|
+
:clearer => true,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
class Lol
|
51
|
+
include MooseX
|
52
|
+
|
53
|
+
has [:a, :b], {
|
54
|
+
:is => :ro,
|
55
|
+
:default => 0,
|
56
|
+
}
|
57
|
+
|
58
|
+
has :c => {
|
59
|
+
:is => :ro,
|
60
|
+
:default => 1,
|
61
|
+
:predicate => :has_option_c?,
|
62
|
+
:clearer => "reset_option_c", # force coerce
|
63
|
+
}
|
43
64
|
|
65
|
+
has [:d, :e] => {
|
66
|
+
:is => "ro",
|
67
|
+
:default => 2,
|
68
|
+
}
|
44
69
|
end
|
45
70
|
|
46
|
-
describe "
|
71
|
+
describe "Point" do
|
47
72
|
describe "should has an intelligent constructor" do
|
48
73
|
it "without arguments, should initialize with default values" do
|
49
74
|
p = Point.new
|
@@ -62,42 +87,6 @@ describe "MooseX" do
|
|
62
87
|
p.x.should == 5
|
63
88
|
p.y.should == 4
|
64
89
|
end
|
65
|
-
|
66
|
-
it "should require bar if necessary" do
|
67
|
-
expect {
|
68
|
-
Foo.new
|
69
|
-
}.to raise_error("attr \"bar\" is required")
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should require bar if necessary" do
|
73
|
-
foo = Foo.new( :bar => 123 )
|
74
|
-
foo.bar.should == 123
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should not be possible update bar (setter private)" do
|
78
|
-
foo = Foo.new( :bar => 123 )
|
79
|
-
expect {
|
80
|
-
foo.bar = 1024
|
81
|
-
}.to raise_error(NoMethodError)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should require bam if necessary" do
|
85
|
-
baz = Baz.new( :bam => 99 )
|
86
|
-
baz.bam.should == 99
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should not be possible update baz (read only)" do
|
90
|
-
baz = Baz.new( :bam => 99 )
|
91
|
-
expect {
|
92
|
-
baz.bam = 1024
|
93
|
-
}.to raise_error(NoMethodError)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should run the lambda isa" do
|
97
|
-
expect {
|
98
|
-
baz = Baz.new( :bam => 199 )
|
99
|
-
}.to raise_error(/x should be less than 100/)
|
100
|
-
end
|
101
90
|
end
|
102
91
|
|
103
92
|
describe "should create a getter and a setter" do
|
@@ -111,13 +100,13 @@ describe "MooseX" do
|
|
111
100
|
p = Point.new
|
112
101
|
expect {
|
113
102
|
p.x = "lol"
|
114
|
-
}.to raise_error('isa check for "x" failed:
|
103
|
+
}.to raise_error('isa check for "x" failed: is not instance of Integer!')
|
115
104
|
end
|
116
105
|
|
117
106
|
it "for x, with type check" do
|
118
107
|
expect {
|
119
108
|
Point.new(:x => "lol")
|
120
|
-
}.to raise_error('isa check for "x" failed:
|
109
|
+
}.to raise_error('isa check for "x" failed: is not instance of Integer!')
|
121
110
|
end
|
122
111
|
|
123
112
|
it "clear should clean attributes" do
|
@@ -127,4 +116,95 @@ describe "MooseX" do
|
|
127
116
|
p.y.should be_zero
|
128
117
|
end
|
129
118
|
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "Foo" do
|
122
|
+
it "should require bar if necessary" do
|
123
|
+
expect {
|
124
|
+
Foo.new
|
125
|
+
}.to raise_error("attr \"bar\" is required")
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should require bar if necessary" do
|
129
|
+
foo = Foo.new( :bar => 123 )
|
130
|
+
foo.bar.should == 123
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not be possible update bar (setter private)" do
|
134
|
+
foo = Foo.new( :bar => 123 )
|
135
|
+
expect {
|
136
|
+
foo.bar = 1024
|
137
|
+
}.to raise_error(NoMethodError)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "Baz" do
|
142
|
+
it "should require bam if necessary" do
|
143
|
+
baz = Baz.new( :bam => 99 )
|
144
|
+
baz.bam.should == 99
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should not be possible update baz (read only)" do
|
148
|
+
baz = Baz.new( :bam => 99 )
|
149
|
+
expect {
|
150
|
+
baz.bam = 1024
|
151
|
+
}.to raise_error(NoMethodError)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should run the lambda isa" do
|
155
|
+
expect {
|
156
|
+
Baz.new( :bam => 199 )
|
157
|
+
}.to raise_error(/bam should be less than 100/)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "rw acessor should has nil value, supports predicate" do
|
161
|
+
baz = Baz.new( :bam => 99 )
|
162
|
+
|
163
|
+
baz.has_boom?.should be_false
|
164
|
+
baz.boom.should be_nil
|
165
|
+
baz.boom= 0
|
166
|
+
baz.has_boom?.should be_true
|
167
|
+
baz.boom.should be_zero
|
168
|
+
end
|
169
|
+
|
170
|
+
it "rw acessor should has nil value, supports clearer" do
|
171
|
+
baz = Baz.new( :bam => 99, :boom => 0 )
|
172
|
+
|
173
|
+
baz.has_boom?.should be_true
|
174
|
+
baz.boom.should be_zero
|
175
|
+
|
176
|
+
baz.reset_boom!
|
177
|
+
|
178
|
+
baz.has_boom?.should be_false
|
179
|
+
baz.boom.should be_nil
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should be possible call the clearer twice" do
|
183
|
+
baz = Baz.new( :bam => 99, :boom => 0 )
|
184
|
+
|
185
|
+
baz.reset_boom!
|
186
|
+
baz.reset_boom!
|
187
|
+
|
188
|
+
baz.has_boom?.should be_false
|
189
|
+
baz.boom.should be_nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "Lol" do
|
194
|
+
it "Lol should has five arguments" do
|
195
|
+
lol = Lol.new(:a => 5, :d => -1)
|
196
|
+
lol.a.should == 5
|
197
|
+
lol.b.should be_zero
|
198
|
+
lol.c.should == 1
|
199
|
+
lol.d.should == -1
|
200
|
+
lol.e.should == 2
|
201
|
+
end
|
202
|
+
|
203
|
+
it "Lol should support custom predicate and clearer" do
|
204
|
+
lol = Lol.new(:a => 5, :d => -1)
|
205
|
+
|
206
|
+
lol.has_option_c?.should be_true
|
207
|
+
lol.reset_option_c
|
208
|
+
lol.has_option_c?.should be_false
|
209
|
+
end
|
130
210
|
end
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Peczenyj
|
@@ -63,11 +63,13 @@ extensions: []
|
|
63
63
|
extra_rdoc_files: []
|
64
64
|
files:
|
65
65
|
- .gitignore
|
66
|
+
- .travis.yml
|
66
67
|
- Changelog
|
67
68
|
- Gemfile
|
68
69
|
- Gemfile.lock
|
69
70
|
- LICENSE
|
70
71
|
- README.md
|
72
|
+
- Rakefile
|
71
73
|
- lib/moosex.rb
|
72
74
|
- lib/moosex/version.rb
|
73
75
|
- moosex.gemspec
|