moosex 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Changelog +8 -0
- data/Gemfile.lock +3 -3
- data/README.md +216 -89
- data/Rakefile +1 -1
- data/lib/moosex.rb +87 -39
- data/lib/moosex/version.rb +1 -1
- data/moosex.gemspec +2 -0
- data/spec/moosex_spec.rb +291 -117
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8494a9819b7d500cafd3b8296714bd59cbcdad27
|
4
|
+
data.tar.gz: 2939d832b895dd24a00a829030de65f315cb6005
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2510660f1124aedb3221696c4b20f4e45aaa3cf2c6ae42f2457c19d918ef45fb7e6d9afedca3b37e71d3cd3c1997e79f83f5064d4bb050a2b6943c647a8859e9
|
7
|
+
data.tar.gz: d0551a59229e8bbb1bffabbfe72443d0d2e2993e3ceae24d99afdb7adbd8ee616f01ef39ef3c703eca04b07681b8e4e79caf0e70178b6e462b87eaa0a5fd3758
|
data/.travis.yml
CHANGED
data/Changelog
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.0.7 - 2014-02-01
|
2
|
+
- add min version of ruby should be 2.0.x
|
3
|
+
|
4
|
+
0.0.6 - 2014-02-01
|
5
|
+
- fix bug when extends subclass, now it is safe #18
|
6
|
+
- required attr with default value will no longer throw exception #30
|
7
|
+
- supports handles for: single method, array, Class or Module #27
|
8
|
+
|
1
9
|
0.0.5 - 2014-01-31
|
2
10
|
- should support handles #13 (partial)
|
3
11
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
moosex (0.0.
|
4
|
+
moosex (0.0.7)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -13,9 +13,9 @@ GEM
|
|
13
13
|
rspec-expectations (~> 2.14.0)
|
14
14
|
rspec-mocks (~> 2.14.0)
|
15
15
|
rspec-core (2.14.7)
|
16
|
-
rspec-expectations (2.14.
|
16
|
+
rspec-expectations (2.14.5)
|
17
17
|
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
-
rspec-mocks (2.14.
|
18
|
+
rspec-mocks (2.14.5)
|
19
19
|
|
20
20
|
PLATFORMS
|
21
21
|
ruby
|
data/README.md
CHANGED
@@ -2,96 +2,107 @@
|
|
2
2
|
|
3
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
|
-
|
6
|
-
require 'moosex'
|
7
|
-
|
8
|
-
class Point
|
9
|
-
include MooseX
|
10
|
-
|
11
|
-
has :x , {
|
12
|
-
:is => :rw, # read-write
|
13
|
-
:isa => Integer, # should be Integer
|
14
|
-
:default => 0, # default value is 0 (constant)
|
15
|
-
}
|
16
|
-
|
17
|
-
has :y , {
|
18
|
-
:is => :rw,
|
19
|
-
:isa => Integer,
|
20
|
-
:default => lambda { 0 }, # you should specify a lambda
|
21
|
-
}
|
22
|
-
|
23
|
-
def clear
|
24
|
-
self.x= 0 # to run with type-check you must
|
25
|
-
self.y= 0 # use the setter instad @x=
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Foo
|
30
|
-
include MooseX
|
31
|
-
|
32
|
-
has :bar, {
|
33
|
-
:is => :rwp, # read-write-private (private setter)
|
34
|
-
:isa => Integer,
|
35
|
-
:required => true, # you should require in the constructor
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
|
-
class Baz
|
40
|
-
include MooseX
|
41
|
-
|
42
|
-
has :bam, {
|
43
|
-
:is => :ro, # read-only, you should specify in new only
|
44
|
-
:isa => lambda {|x| # you should add your own validator
|
45
|
-
raise 'x should be less than 100' if x > 100
|
46
|
-
},
|
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
|
50
|
-
}
|
51
|
-
|
52
|
-
end
|
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
|
-
|
70
|
-
class Target
|
71
|
-
def method_x
|
72
|
-
1024
|
73
|
-
end
|
74
|
-
end
|
5
|
+
THIS MODULE IS EXPERIMENTAL YET! BE CAREFUL!
|
75
6
|
|
76
|
-
|
77
|
-
include MooseX
|
7
|
+
Talk is cheap. Show me the code!
|
78
8
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
9
|
+
```ruby
|
10
|
+
require 'moosex'
|
11
|
+
|
12
|
+
class Point
|
13
|
+
include MooseX
|
14
|
+
|
15
|
+
has x: {
|
16
|
+
is: :rw, # read-write (mandatory)
|
17
|
+
isa: Integer, # should be Integer
|
18
|
+
default: 0, # default value is 0 (constant)
|
19
|
+
}
|
20
|
+
|
21
|
+
has y: {
|
22
|
+
is: :rw,
|
23
|
+
isa: Integer,
|
24
|
+
default: lambda { 0 }, # you should specify a lambda
|
25
|
+
}
|
26
|
+
|
27
|
+
def clear
|
28
|
+
self.x= 0 # to run with type-check you must
|
29
|
+
self.y= 0 # use the setter instad @x=
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Foo
|
34
|
+
include MooseX
|
35
|
+
|
36
|
+
has bar: {
|
37
|
+
is: :rwp, # read-write-private (private setter)
|
38
|
+
required: true, # you should require in the constructor
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
class Baz
|
43
|
+
include MooseX
|
44
|
+
|
45
|
+
has bam: {
|
46
|
+
is: :ro, # read-only, you should specify in new only
|
47
|
+
isa: lambda do |bam| # you should add your own validator
|
48
|
+
raise 'bam should be less than 100' if bam > 100
|
49
|
+
end,
|
50
|
+
required: true,
|
51
|
+
}
|
52
|
+
|
53
|
+
has boom: {
|
54
|
+
is: :rw,
|
55
|
+
predicate: true, # add has_boom? method, ask if the attribute is unset
|
56
|
+
clearer: true, # add reset_boom! method, unset the attribute
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
class Lol
|
61
|
+
include MooseX
|
62
|
+
|
63
|
+
has [:a, :b], { # define attributes a and b
|
64
|
+
is: :ro, # with same set of properties
|
65
|
+
default: 0,
|
66
|
+
}
|
67
|
+
|
68
|
+
has c: { # alternative syntax to be
|
69
|
+
is: :ro, # more similar to Moo/Moose
|
70
|
+
default: 1,
|
71
|
+
predicate: :can_haz_c?, # custom predicate
|
72
|
+
clearer: "desintegrate_c", # force coerce to symbol
|
73
|
+
}
|
74
|
+
|
75
|
+
has [:d, :e] => {
|
76
|
+
is: "ro", # can coerce from strings
|
77
|
+
default: 2,
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
class Proxy
|
82
|
+
include MooseX
|
83
|
+
|
84
|
+
has target: {
|
85
|
+
is: :ro,
|
86
|
+
default: lambda { Target.new }, # default, new instace of Target
|
87
|
+
handles: { # handles is for delegation,
|
88
|
+
my_method_x: :method_x, # inject methods with new names
|
89
|
+
my_method_y: :method_y, # old => obj.target.method_x
|
90
|
+
}, # now => obj.my_method_x
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
class Target
|
95
|
+
def method_x; 1024; end # works with simple methods
|
96
|
+
def method_y(a,b,c); a + b + c; end # or methods with arguments
|
97
|
+
end
|
98
|
+
|
99
|
+
# now you have a generic constructor
|
100
|
+
p1 = Point.new # x and y will be 0
|
101
|
+
p2 = Point.new( x: 5 ) # y will be 0
|
102
|
+
p3 = Point.new( x: 5, y: 4)
|
103
|
+
foo = Foo.new( bar: 123 ) # without bar will raise exception
|
104
|
+
baz = Baz.new( bam: 99 ) # if bam > 100 will raise exception
|
105
|
+
Proxy.new.my_method_x # will call method_x in target, return 1024
|
95
106
|
```
|
96
107
|
|
97
108
|
## Installation
|
@@ -108,10 +119,126 @@ Or install it yourself as:
|
|
108
119
|
|
109
120
|
$ gem install moosex
|
110
121
|
|
111
|
-
|
122
|
+
You need ruby 2.0.x or superior.
|
123
|
+
|
124
|
+
## Description
|
112
125
|
|
113
126
|
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.
|
114
127
|
|
128
|
+
Read more about Moose on http://moose.iinteractive.com/en/
|
129
|
+
|
130
|
+
## Motivation
|
131
|
+
|
132
|
+
It is fun
|
133
|
+
|
134
|
+
## Usage
|
135
|
+
|
136
|
+
When you incluse the MooseX module in your class you can declare your attributes. The module provides a new constructor for you, you should not define 'initialize' anymore.
|
137
|
+
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
require 'moosex'
|
141
|
+
|
142
|
+
class Point
|
143
|
+
include MooseX
|
144
|
+
|
145
|
+
has x: {
|
146
|
+
is: :rw, # read-write (mandatory)
|
147
|
+
isa: Integer, # should be Integer
|
148
|
+
default: 0, # default value is 0 (constant)
|
149
|
+
required: false, # if true, will be required in the constructor
|
150
|
+
predicate: false, # if true, add has_x? method
|
151
|
+
clearer: false, # if true, add clear_x! method
|
152
|
+
handles: [ :to_s ],# used for method delegation
|
153
|
+
}
|
154
|
+
...
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
in this example we add the attribute x, with read-write acessors, a default value and a type check.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
p1 = Point.new # x will be initialize with 0 (default)
|
162
|
+
p2 = Point.new(x: 50) # initialize x in the constructur
|
163
|
+
p3 = Point.new(x: "50") # will raise exception
|
164
|
+
p1.x = "lol" # will raise too
|
165
|
+
```
|
166
|
+
|
167
|
+
to use the type check feature you must use the writter method for the attribute.
|
168
|
+
|
169
|
+
### Advantages
|
170
|
+
|
171
|
+
instead
|
172
|
+
```ruby
|
173
|
+
class Foo
|
174
|
+
attr_accessor :bar, :baz, :bam
|
175
|
+
|
176
|
+
def initialize(bar=0, baz=0, bam=0)
|
177
|
+
unless [bar, baz, bam].all? {|x| x.is_a? Integer }
|
178
|
+
raise "you should use only Integers to build Foo"
|
179
|
+
end
|
180
|
+
@bar = bar
|
181
|
+
@baz = baz
|
182
|
+
@bam = bam
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
you can
|
187
|
+
```ruby
|
188
|
+
class Foo
|
189
|
+
include MooseX
|
190
|
+
|
191
|
+
has [:bar, :baz, :bam], {
|
192
|
+
is: :rw,
|
193
|
+
isa: Integer,
|
194
|
+
default: 0
|
195
|
+
}
|
196
|
+
end
|
197
|
+
```
|
198
|
+
instead
|
199
|
+
```ruby
|
200
|
+
class Proxy
|
201
|
+
def initialize(target)
|
202
|
+
@target=target
|
203
|
+
end
|
204
|
+
|
205
|
+
def method_x(a,b,c)
|
206
|
+
@target.method_x(a,b,c)
|
207
|
+
end
|
208
|
+
def method_y(a,b,c)
|
209
|
+
@target.method_y(a,b,c)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
```
|
213
|
+
you can
|
214
|
+
```ruby
|
215
|
+
class Proxy
|
216
|
+
include MooseX
|
217
|
+
|
218
|
+
has :target, {
|
219
|
+
is: :ro,
|
220
|
+
handles => [ :method_x, :method_y ]
|
221
|
+
}
|
222
|
+
end
|
223
|
+
```
|
224
|
+
and much more
|
225
|
+
|
226
|
+
## TODO
|
227
|
+
|
228
|
+
1. Support to lazy attributes
|
229
|
+
2. Support to BUILD and BUILDARGS hook
|
230
|
+
3. Support to Roles ( it is a Module on Steroids )
|
231
|
+
4. Support to after/before/around
|
232
|
+
5. Improve the typecheck system (we should specify: we need an array of positive integers)
|
233
|
+
6. Improve the exception and warning system
|
234
|
+
7. Profit!
|
235
|
+
|
236
|
+
## Limitations
|
237
|
+
|
238
|
+
Experimental module, be careful.
|
239
|
+
|
240
|
+
Now has limited support to subclassing.
|
241
|
+
|
115
242
|
## Contributing
|
116
243
|
|
117
244
|
1. Fork it ( http://github.com/peczenyj/MooseX/fork )
|
data/Rakefile
CHANGED
data/lib/moosex.rb
CHANGED
@@ -7,11 +7,11 @@ require "moosex/version"
|
|
7
7
|
|
8
8
|
module MooseX
|
9
9
|
|
10
|
-
def
|
10
|
+
def MooseX.included(c)
|
11
11
|
|
12
|
-
|
12
|
+
c.extend(MooseX::Core)
|
13
13
|
|
14
|
-
|
14
|
+
c.class_exec do
|
15
15
|
meta = MooseX::Meta.new
|
16
16
|
|
17
17
|
define_singleton_method(:__meta) { meta }
|
@@ -22,9 +22,34 @@ module MooseX
|
|
22
22
|
self.class.__meta().init(self, args)
|
23
23
|
|
24
24
|
end
|
25
|
+
|
26
|
+
def c.inherited(subclass)
|
27
|
+
subclass.class_exec do
|
28
|
+
old_meta = subclass.__meta
|
29
|
+
|
30
|
+
meta = MooseX::Meta.new(old_meta.attrs)
|
31
|
+
|
32
|
+
define_singleton_method(:__meta) { meta }
|
33
|
+
end
|
34
|
+
end
|
25
35
|
|
26
36
|
end
|
27
37
|
|
38
|
+
class Meta
|
39
|
+
attr_reader :attrs
|
40
|
+
def initialize(attrs=[])
|
41
|
+
@attrs = attrs.map{|att| att.clone }
|
42
|
+
end
|
43
|
+
|
44
|
+
def add(attr)
|
45
|
+
@attrs << attr
|
46
|
+
end
|
47
|
+
|
48
|
+
def init(object, args)
|
49
|
+
@attrs.each{ |attr| attr.init(object, args) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
28
53
|
module Core
|
29
54
|
|
30
55
|
def has(attr_name, attr_options = {})
|
@@ -63,32 +88,32 @@ module MooseX
|
|
63
88
|
|
64
89
|
class Attribute
|
65
90
|
|
66
|
-
attr_reader :attr_symbol, :is
|
67
|
-
|
91
|
+
attr_reader :attr_symbol, :is
|
68
92
|
DEFAULTS= {
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
93
|
+
lazy: false,
|
94
|
+
clearer: false,
|
95
|
+
required: false,
|
96
|
+
predicate: false,
|
97
|
+
isa: lambda { |x| true },
|
98
|
+
handles: {},
|
74
99
|
}
|
75
100
|
|
76
101
|
REQUIRED = [ :is ]
|
77
102
|
|
78
103
|
VALIDATE = {
|
79
|
-
:
|
104
|
+
is: lambda do |is, field_name|
|
80
105
|
unless [:rw, :rwp, :ro, :lazy].include?(is)
|
81
106
|
raise "invalid value for field '#{field_name}' is '#{is}', must be one of :rw, :rwp, :ro or :lazy"
|
82
107
|
end
|
83
108
|
end,
|
84
|
-
:
|
109
|
+
handles: lambda {|handles, field_name| true }, # TODO: add implementation
|
85
110
|
};
|
86
111
|
|
87
112
|
COERCE = {
|
88
|
-
:
|
113
|
+
is: lambda do |is, field_name|
|
89
114
|
is.to_sym
|
90
115
|
end,
|
91
|
-
:
|
116
|
+
isa: lambda do |isa, field_name|
|
92
117
|
return isa if isa.is_a? Proc
|
93
118
|
|
94
119
|
return lambda do |new_value|
|
@@ -97,15 +122,18 @@ module MooseX
|
|
97
122
|
end
|
98
123
|
end
|
99
124
|
end,
|
100
|
-
:
|
125
|
+
default: lambda do |default, field_name|
|
101
126
|
return default if default.is_a? Proc
|
102
127
|
|
103
128
|
return lambda { default }
|
104
129
|
end,
|
105
|
-
:
|
130
|
+
required: lambda do |required, field_name|
|
106
131
|
!!required
|
107
132
|
end,
|
108
|
-
:
|
133
|
+
lazy: lambda do |lazy, field_name|
|
134
|
+
!!lazy
|
135
|
+
end,
|
136
|
+
predicate: lambda do |predicate, field_name|
|
109
137
|
if ! predicate
|
110
138
|
return false
|
111
139
|
elsif predicate.is_a? TrueClass
|
@@ -119,7 +147,7 @@ module MooseX
|
|
119
147
|
raise "cannot coerce field predicate to a symbol for #{field_name}: #{e}"
|
120
148
|
end
|
121
149
|
end,
|
122
|
-
:
|
150
|
+
clearer: lambda do|clearer, field_name|
|
123
151
|
if ! clearer
|
124
152
|
return false
|
125
153
|
elsif clearer.is_a? TrueClass
|
@@ -133,11 +161,44 @@ module MooseX
|
|
133
161
|
raise "cannot coerce field clearer to a symbol for #{field_name}: #{e}"
|
134
162
|
end
|
135
163
|
end,
|
136
|
-
:
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
164
|
+
handles: lambda do |handles, field_name|
|
165
|
+
|
166
|
+
unless handles.is_a? Hash
|
167
|
+
|
168
|
+
array_of_handles = handles
|
169
|
+
|
170
|
+
unless array_of_handles.is_a? Array
|
171
|
+
array_of_handles = [ array_of_handles ]
|
172
|
+
end
|
173
|
+
|
174
|
+
handles = array_of_handles.map do |handle|
|
175
|
+
|
176
|
+
if handle == BasicObject
|
177
|
+
|
178
|
+
raise "ops, should not use BasicObject for handles in #{field_name}"
|
179
|
+
|
180
|
+
elsif handle.is_a? Class
|
181
|
+
|
182
|
+
handle = handle.public_instance_methods - handle.superclass.public_instance_methods
|
183
|
+
|
184
|
+
elsif handle.is_a? Module
|
185
|
+
|
186
|
+
handle = handle.public_instance_methods
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
handle
|
191
|
+
|
192
|
+
end.flatten.reduce({}) do |hash, method_name|
|
193
|
+
hash.merge({ method_name => method_name })
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
handles.map do |key,value|
|
198
|
+
{ key.to_sym => value.to_sym }
|
199
|
+
end.reduce({}) do |hash,e|
|
200
|
+
hash.merge(e)
|
201
|
+
end
|
141
202
|
end,
|
142
203
|
};
|
143
204
|
|
@@ -169,6 +230,7 @@ module MooseX
|
|
169
230
|
@predicate = o[:predicate]
|
170
231
|
@clearer = o[:clearer]
|
171
232
|
@handles = o[:handles]
|
233
|
+
@lazy = o[:lazy]
|
172
234
|
end
|
173
235
|
|
174
236
|
def init(object, args)
|
@@ -200,10 +262,10 @@ module MooseX
|
|
200
262
|
|
201
263
|
if args.has_key? @attr_symbol
|
202
264
|
value = args[ @attr_symbol ]
|
203
|
-
elsif @required
|
204
|
-
raise "attr \"#{@attr_symbol}\" is required"
|
205
265
|
elsif @default
|
206
266
|
value = @default.call
|
267
|
+
elsif @required
|
268
|
+
raise "attr \"#{@attr_symbol}\" is required"
|
207
269
|
else
|
208
270
|
return
|
209
271
|
end
|
@@ -242,18 +304,4 @@ module MooseX
|
|
242
304
|
return @isa
|
243
305
|
end
|
244
306
|
end
|
245
|
-
|
246
|
-
class Meta
|
247
|
-
def initialize
|
248
|
-
@attrs = []
|
249
|
-
end
|
250
|
-
|
251
|
-
def add(attr)
|
252
|
-
@attrs << attr
|
253
|
-
end
|
254
|
-
|
255
|
-
def init(object, args)
|
256
|
-
@attrs.each{ |attr| attr.init(object, args) }
|
257
|
-
end
|
258
|
-
end
|
259
307
|
end
|
data/lib/moosex/version.rb
CHANGED
data/moosex.gemspec
CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.required_ruby_version = '~> 2.0'
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.5"
|
22
24
|
spec.add_development_dependency "rake"
|
23
25
|
spec.add_development_dependency "rspec"
|
data/spec/moosex_spec.rb
CHANGED
@@ -1,112 +1,25 @@
|
|
1
|
-
#require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
1
|
require 'moosex'
|
3
2
|
|
4
3
|
class Point
|
5
4
|
include MooseX
|
6
|
-
|
7
|
-
has :x , {
|
8
|
-
:is => :rw,
|
9
|
-
:isa => Integer,
|
10
|
-
:default => 0,
|
11
|
-
}
|
12
|
-
|
13
|
-
has :y , {
|
14
|
-
:is => :rw,
|
15
|
-
:isa => Integer,
|
16
|
-
:default => lambda { 0 },
|
17
|
-
}
|
18
|
-
|
19
|
-
def clear
|
20
|
-
self.x= 0
|
21
|
-
self.y= 0
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Foo
|
26
|
-
include MooseX
|
27
|
-
|
28
|
-
has :bar, {
|
29
|
-
:is => :rwp,
|
30
|
-
:isa => Integer,
|
31
|
-
:required => true
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
class Baz
|
36
|
-
include MooseX
|
37
|
-
|
38
|
-
has :bam, {
|
39
|
-
:is => :ro,
|
40
|
-
:isa => lambda {|bam| raise 'bam should be less than 100' if bam > 100},
|
41
|
-
:required => true
|
42
|
-
}
|
43
|
-
has :boom, {
|
44
|
-
:is => :rw,
|
45
|
-
:predicate => true,
|
46
|
-
:clearer => true,
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
class Lol
|
51
|
-
include MooseX
|
52
5
|
|
53
|
-
has
|
54
|
-
:
|
55
|
-
:
|
6
|
+
has x: {
|
7
|
+
is: :rw, # read-write (mandatory)
|
8
|
+
isa: Integer, # should be Integer
|
9
|
+
default: 0, # default value is 0 (constant)
|
56
10
|
}
|
57
11
|
|
58
|
-
has :
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:clearer => "reset_option_c", # force coerce
|
12
|
+
has y: {
|
13
|
+
is: :rw,
|
14
|
+
isa: Integer,
|
15
|
+
default: lambda { 0 }, # you should specify a lambda
|
63
16
|
}
|
64
17
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
class Target
|
72
|
-
def method_x
|
73
|
-
1024
|
74
|
-
end
|
75
|
-
|
76
|
-
def method_y(a,b,c)
|
77
|
-
a + b + c
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
class Proxy
|
82
|
-
include MooseX
|
83
|
-
|
84
|
-
has :target, {
|
85
|
-
:is => :ro,
|
86
|
-
:isa => Target,
|
87
|
-
:default => lambda { Target.new() },
|
88
|
-
:handles => {
|
89
|
-
:my_method_x => :method_x,
|
90
|
-
:my_method_y => :method_y,
|
91
|
-
},
|
92
|
-
}
|
93
|
-
end
|
94
|
-
|
95
|
-
describe "Proxy" do
|
96
|
-
it "should delegate method_x to the target" do
|
97
|
-
p = Proxy.new
|
98
|
-
|
99
|
-
p.target.method_x.should == 1024
|
100
|
-
p.my_method_x.should == 1024
|
18
|
+
def clear
|
19
|
+
self.x= 0 # to run with type-check you must
|
20
|
+
self.y= 0 # use the setter instad @x=
|
101
21
|
end
|
102
|
-
|
103
|
-
it "should delegate method_y to the target" do
|
104
|
-
p = Proxy.new
|
105
|
-
|
106
|
-
p.target.method_y(1,2,3).should == 6
|
107
|
-
p.my_method_y(1,2,3).should == 6
|
108
|
-
end
|
109
|
-
end
|
22
|
+
end
|
110
23
|
|
111
24
|
describe "Point" do
|
112
25
|
describe "should has an intelligent constructor" do
|
@@ -117,13 +30,13 @@ describe "Point" do
|
|
117
30
|
end
|
118
31
|
|
119
32
|
it "should initialize only y" do
|
120
|
-
p = Point.new( :
|
33
|
+
p = Point.new( x: 5 )
|
121
34
|
p.x.should == 5
|
122
35
|
p.y.should be_zero
|
123
36
|
end
|
124
37
|
|
125
38
|
it "should initialize x and y" do
|
126
|
-
p = Point.new( :
|
39
|
+
p = Point.new( x: 5, y: 4)
|
127
40
|
p.x.should == 5
|
128
41
|
p.y.should == 4
|
129
42
|
end
|
@@ -145,12 +58,12 @@ describe "Point" do
|
|
145
58
|
|
146
59
|
it "for x, with type check" do
|
147
60
|
expect {
|
148
|
-
Point.new(:
|
61
|
+
Point.new(x: "lol")
|
149
62
|
}.to raise_error('isa check for "x" failed: is not instance of Integer!')
|
150
63
|
end
|
151
64
|
|
152
65
|
it "clear should clean attributes" do
|
153
|
-
p = Point.new( :
|
66
|
+
p = Point.new( x: 5, y: 4)
|
154
67
|
p.clear
|
155
68
|
p.x.should be_zero
|
156
69
|
p.y.should be_zero
|
@@ -158,6 +71,15 @@ describe "Point" do
|
|
158
71
|
end
|
159
72
|
end
|
160
73
|
|
74
|
+
class Foo
|
75
|
+
include MooseX
|
76
|
+
|
77
|
+
has bar: {
|
78
|
+
is: :rwp, # read-write-private (private setter)
|
79
|
+
required: true, # you should require in the constructor
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
161
83
|
describe "Foo" do
|
162
84
|
it "should require bar if necessary" do
|
163
85
|
expect {
|
@@ -166,26 +88,44 @@ describe "Foo" do
|
|
166
88
|
end
|
167
89
|
|
168
90
|
it "should require bar if necessary" do
|
169
|
-
foo = Foo.new( :
|
91
|
+
foo = Foo.new( bar: 123 )
|
170
92
|
foo.bar.should == 123
|
171
93
|
end
|
172
94
|
|
173
95
|
it "should not be possible update bar (setter private)" do
|
174
|
-
foo = Foo.new( :
|
96
|
+
foo = Foo.new( bar: 123 )
|
175
97
|
expect {
|
176
98
|
foo.bar = 1024
|
177
99
|
}.to raise_error(NoMethodError)
|
178
100
|
end
|
179
101
|
end
|
180
102
|
|
103
|
+
class Baz
|
104
|
+
include MooseX
|
105
|
+
|
106
|
+
has bam: {
|
107
|
+
is: :ro, # read-only, you should specify in new only
|
108
|
+
isa: lambda do |bam| # you should add your own validator
|
109
|
+
raise 'bam should be less than 100' if bam > 100
|
110
|
+
end,
|
111
|
+
required: true,
|
112
|
+
}
|
113
|
+
|
114
|
+
has boom: {
|
115
|
+
is: :rw,
|
116
|
+
predicate: true, # add has_boom? method, ask if the attribute is unset
|
117
|
+
clearer: true, # add reset_boom! method, unset the attribute
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
181
121
|
describe "Baz" do
|
182
122
|
it "should require bam if necessary" do
|
183
|
-
baz = Baz.new( :
|
123
|
+
baz = Baz.new( bam: 99 )
|
184
124
|
baz.bam.should == 99
|
185
125
|
end
|
186
126
|
|
187
127
|
it "should not be possible update baz (read only)" do
|
188
|
-
baz = Baz.new( :
|
128
|
+
baz = Baz.new( bam: 99 )
|
189
129
|
expect {
|
190
130
|
baz.bam = 1024
|
191
131
|
}.to raise_error(NoMethodError)
|
@@ -193,12 +133,12 @@ describe "Baz" do
|
|
193
133
|
|
194
134
|
it "should run the lambda isa" do
|
195
135
|
expect {
|
196
|
-
Baz.new( :
|
136
|
+
Baz.new( bam: 199 )
|
197
137
|
}.to raise_error(/bam should be less than 100/)
|
198
138
|
end
|
199
139
|
|
200
140
|
it "rw acessor should has nil value, supports predicate" do
|
201
|
-
baz = Baz.new( :
|
141
|
+
baz = Baz.new( bam: 99 )
|
202
142
|
|
203
143
|
baz.has_boom?.should be_false
|
204
144
|
baz.boom.should be_nil
|
@@ -208,7 +148,7 @@ describe "Baz" do
|
|
208
148
|
end
|
209
149
|
|
210
150
|
it "rw acessor should has nil value, supports clearer" do
|
211
|
-
baz = Baz.new( :
|
151
|
+
baz = Baz.new( bam: 99, boom: 0 )
|
212
152
|
|
213
153
|
baz.has_boom?.should be_true
|
214
154
|
baz.boom.should be_zero
|
@@ -220,7 +160,7 @@ describe "Baz" do
|
|
220
160
|
end
|
221
161
|
|
222
162
|
it "should be possible call the clearer twice" do
|
223
|
-
baz = Baz.new( :
|
163
|
+
baz = Baz.new( bam: 99, boom: 0 )
|
224
164
|
|
225
165
|
baz.reset_boom!
|
226
166
|
baz.reset_boom!
|
@@ -230,9 +170,31 @@ describe "Baz" do
|
|
230
170
|
end
|
231
171
|
end
|
232
172
|
|
173
|
+
class Lol
|
174
|
+
include MooseX
|
175
|
+
|
176
|
+
has [:a, :b], { # define attributes a and b
|
177
|
+
is: :ro, # with same set of properties
|
178
|
+
default: 0,
|
179
|
+
}
|
180
|
+
|
181
|
+
has c: { # alternative syntax to be
|
182
|
+
is: :ro, # more similar to Moo/Moose
|
183
|
+
default: 1,
|
184
|
+
predicate: :can_haz_c?, # custom predicate
|
185
|
+
clearer: "desintegrate_c", # force coerce to symbol
|
186
|
+
}
|
187
|
+
|
188
|
+
has [:d, :e] => {
|
189
|
+
is: "ro", # can coerce from strings
|
190
|
+
default: 2,
|
191
|
+
required: true,
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
233
195
|
describe "Lol" do
|
234
196
|
it "Lol should has five arguments" do
|
235
|
-
lol = Lol.new(:
|
197
|
+
lol = Lol.new(a: 5, d: -1)
|
236
198
|
lol.a.should == 5
|
237
199
|
lol.b.should be_zero
|
238
200
|
lol.c.should == 1
|
@@ -241,10 +203,222 @@ describe "Lol" do
|
|
241
203
|
end
|
242
204
|
|
243
205
|
it "Lol should support custom predicate and clearer" do
|
244
|
-
lol = Lol.new(:
|
206
|
+
lol = Lol.new(a: 5, d: -1)
|
245
207
|
|
246
|
-
lol.
|
247
|
-
lol.
|
248
|
-
lol.
|
208
|
+
lol.can_haz_c?.should be_true
|
209
|
+
lol.desintegrate_c
|
210
|
+
lol.can_haz_c?.should be_false
|
249
211
|
end
|
250
|
-
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class ProxyToTarget
|
215
|
+
include MooseX
|
216
|
+
|
217
|
+
has target: {
|
218
|
+
is: :ro,
|
219
|
+
default: lambda { Target.new }, # default, new instace of Target
|
220
|
+
handles: { # handles is for delegation,
|
221
|
+
my_method_x: :method_x, # inject methods with new names
|
222
|
+
my_method_y: :method_y, # old => obj.target.method_x
|
223
|
+
}, # now => obj.my_method_x
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
module TargetModule
|
228
|
+
def method_x; 1024; end # works with simple methods
|
229
|
+
def method_y(a,b,c); a + b + c; end # or methods with arguments
|
230
|
+
end
|
231
|
+
|
232
|
+
class Target
|
233
|
+
include TargetModule
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "ProxyToTarget" do
|
237
|
+
it "should delegate method_x to the target" do
|
238
|
+
p = ProxyToTarget.new
|
239
|
+
|
240
|
+
p.target.method_x.should == 1024
|
241
|
+
p.my_method_x.should == 1024
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should delegate method_y to the target" do
|
245
|
+
p = ProxyToTarget.new
|
246
|
+
|
247
|
+
p.target.method_y(1,2,3).should == 6
|
248
|
+
p.my_method_y(1,2,3).should == 6
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class ProxyToTargetUsingArrayOfMethods
|
253
|
+
include MooseX
|
254
|
+
|
255
|
+
has targetz: {
|
256
|
+
is: :ro,
|
257
|
+
default: lambda { Target.new },
|
258
|
+
handles: [
|
259
|
+
:method_x, :method_y # will inject all methods with same name
|
260
|
+
]
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "ProxyToTargetUsingArrayOfMethods" do
|
265
|
+
it "should delegate method_x to the target" do
|
266
|
+
p = ProxyToTargetUsingArrayOfMethods.new
|
267
|
+
|
268
|
+
p.targetz.method_x.should == 1024
|
269
|
+
p.method_x.should == 1024
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should delegate method_y to the target" do
|
273
|
+
p = ProxyToTargetUsingArrayOfMethods.new
|
274
|
+
|
275
|
+
p.targetz.method_y(1,2,3).should == 6
|
276
|
+
p.method_y(1,2,3).should == 6
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class ProxyToTargetUsingSingleMethod
|
281
|
+
include MooseX
|
282
|
+
|
283
|
+
has target: {
|
284
|
+
is: :ro,
|
285
|
+
default: lambda { Target.new },
|
286
|
+
handles: "method_x" # coerce to an array of symbols
|
287
|
+
}
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "ProxyToTargetUsingSingleMethod" do
|
291
|
+
it "should delegate method_x to the target" do
|
292
|
+
p = ProxyToTargetUsingSingleMethod.new
|
293
|
+
|
294
|
+
p.target.method_x.should == 1024
|
295
|
+
p.method_x.should == 1024
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class ProxyToTargetUsingModule
|
300
|
+
include MooseX
|
301
|
+
|
302
|
+
has target: {
|
303
|
+
is: :ro,
|
304
|
+
default: lambda { Target.new },
|
305
|
+
handles: TargetModule # will import all methods from module
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "ProxyToTargetUsingModule" do
|
310
|
+
it "should delegate method_x to the target" do
|
311
|
+
p = ProxyToTargetUsingModule.new
|
312
|
+
|
313
|
+
p.target.method_x.should == 1024
|
314
|
+
p.method_x.should == 1024
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should delegate method_y to the target" do
|
318
|
+
p = ProxyToTargetUsingModule.new
|
319
|
+
|
320
|
+
p.target.method_y(1,2,3).should == 6
|
321
|
+
p.method_y(1,2,3).should == 6
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
class ProxyToTargetUsingClass
|
326
|
+
include MooseX
|
327
|
+
|
328
|
+
has target: {
|
329
|
+
is: :ro,
|
330
|
+
default: lambda { Target.new },
|
331
|
+
handles: Target # will use only public methods on Target class
|
332
|
+
} # exclude methods from superclass
|
333
|
+
end
|
334
|
+
|
335
|
+
describe "ProxyToTargetUsingClass" do
|
336
|
+
it "should delegate method_x to the target" do
|
337
|
+
p = ProxyToTargetUsingClass.new
|
338
|
+
|
339
|
+
p.target.method_x.should == 1024
|
340
|
+
p.method_x.should == 1024
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should delegate method_y to the target" do
|
344
|
+
p = ProxyToTargetUsingClass.new
|
345
|
+
|
346
|
+
p.target.method_y(1,2,3).should == 6
|
347
|
+
p.method_y(1,2,3).should == 6
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
class Point3D < Point
|
352
|
+
|
353
|
+
has x: { # override original attr!
|
354
|
+
is: :rw,
|
355
|
+
isa: Integer,
|
356
|
+
default: 1,
|
357
|
+
}
|
358
|
+
|
359
|
+
has z: {
|
360
|
+
is: :rw, # read-write (mandatory)
|
361
|
+
isa: Integer, # should be Integer
|
362
|
+
default: 0, # default value is 0 (constant)
|
363
|
+
}
|
364
|
+
|
365
|
+
def clear
|
366
|
+
self.x= 0 # to run with type-check you must
|
367
|
+
self.y= 0 # use the setter instad @x=
|
368
|
+
self.z= 0
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
describe "Point3D" do
|
373
|
+
describe "should has an intelligent constructor" do
|
374
|
+
it "without arguments, should initialize with default values" do
|
375
|
+
p = Point3D.new
|
376
|
+
p.x.should == 1
|
377
|
+
p.y.should be_zero
|
378
|
+
p.z.should be_zero
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should initialize only y" do
|
382
|
+
p = Point3D.new( x: 5 )
|
383
|
+
p.x.should == 5
|
384
|
+
p.y.should be_zero
|
385
|
+
p.z.should be_zero
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should initialize x and y" do
|
389
|
+
p = Point3D.new( x: 5, y: 4, z: 8)
|
390
|
+
p.x.should == 5
|
391
|
+
p.y.should == 4
|
392
|
+
p.z.should == 8
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
describe "should create a getter and a setter" do
|
397
|
+
it "for z" do
|
398
|
+
p = Point3D.new
|
399
|
+
p.z= 5
|
400
|
+
p.z.should == 5
|
401
|
+
end
|
402
|
+
|
403
|
+
it "for z, with type check" do
|
404
|
+
p = Point3D.new
|
405
|
+
expect {
|
406
|
+
p.z = "lol"
|
407
|
+
}.to raise_error('isa check for "z" failed: is not instance of Integer!')
|
408
|
+
end
|
409
|
+
|
410
|
+
it "for z, with type check" do
|
411
|
+
expect {
|
412
|
+
Point3D.new(z: "lol")
|
413
|
+
}.to raise_error('isa check for "z" failed: is not instance of Integer!')
|
414
|
+
end
|
415
|
+
|
416
|
+
it "clear should clean attributes" do
|
417
|
+
p = Point3D.new( x: 5, y: 4, z: 9)
|
418
|
+
p.clear
|
419
|
+
p.x.should be_zero
|
420
|
+
p.y.should be_zero
|
421
|
+
p.z.should be_zero
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Peczenyj
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01
|
11
|
+
date: 2014-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,9 +84,9 @@ require_paths:
|
|
84
84
|
- lib
|
85
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
89
|
+
version: '2.0'
|
90
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - ">="
|