moosex 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](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
|