moosex 0.0.17 → 0.0.18
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/Changelog +38 -33
- data/Gemfile.lock +1 -1
- data/README.md +43 -1
- data/lib/moosex.rb +55 -622
- data/lib/moosex/attribute.rb +192 -0
- data/lib/moosex/attribute/modifiers.rb +303 -0
- data/lib/moosex/core.rb +114 -0
- data/lib/moosex/exceptions.rb +11 -0
- data/lib/moosex/meta.rb +139 -0
- data/lib/moosex/types.rb +35 -23
- data/lib/moosex/version.rb +1 -1
- data/spec/lol_spec.rb +1 -0
- data/spec/meta_spec.rb +90 -0
- data/spec/modifiers_spec.rb +28 -0
- data/spec/types_spec.rb +12 -14
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae6315aba6d50e3443b6e0b184a3d51a4ef676d2
|
4
|
+
data.tar.gz: e80dc006c7d9ba38c22130007416ca5f29650fec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 660d4f1f3a542d7e21f66d7d8b335bfe6acb1cde810c65cf8ee37aa82227fe74185b9614a726b0e97f7d5db415182560a710cb072264cae6c7b7dce4b60b9d4c
|
7
|
+
data.tar.gz: 0f8071f6fcec75b2ef8d95e0dc2e02388abb45b7fd82aad2aec5b8eeb7be5b759ac02a2862d8f939fe7fe51c81afc326c0843772422bbc0ba577cf9d93fb68b6
|
data/Changelog
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
0.0.18 - 2013-02-12
|
2
|
+
- better meta #58
|
3
|
+
- reduce internal complexity #51, #70
|
4
|
+
- meta now has __moosex__ prefix #55
|
5
|
+
|
1
6
|
0.0.17 - 2013-02-11
|
2
|
-
|
3
|
-
|
4
|
-
|
7
|
+
- has now support an override option #56
|
8
|
+
- has now support a doc option #57
|
9
|
+
- has now should not require :is => default is :rw #54
|
5
10
|
- add weak ref support #49
|
6
11
|
|
7
12
|
0.0.16 - 2013-02-07
|
@@ -15,63 +20,63 @@
|
|
15
20
|
- add init method to enable warnings and exceptions #43
|
16
21
|
|
17
22
|
0.0.14 - 2013-02-05
|
18
|
-
|
23
|
+
- roles with around/before/after basic support #41
|
19
24
|
|
20
25
|
0.0.13 - 2013-02-05
|
21
|
-
|
26
|
+
- change around to receive a lambda #42
|
22
27
|
|
23
28
|
0.0.12 - 2013-02-05
|
24
|
-
|
25
|
-
|
29
|
+
- basic support override attributes #19
|
30
|
+
- basic support to roles #17
|
26
31
|
|
27
32
|
0.0.11 - 2013-02-04
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
- basic support to after/before/around #12
|
34
|
+
- improve exception #15
|
35
|
+
- improve type system via MooseX::Types #16
|
36
|
+
|
32
37
|
0.0.10 - 2014-02-03
|
33
|
-
|
38
|
+
- improve readme
|
34
39
|
- BUILDARGS should accept different signatures #36
|
35
40
|
- clearer create by default clear_#{attr_name}! public method
|
36
41
|
- add attr :private #35
|
37
42
|
- methods predicate, clearer and handles are no longer singleton methods #34
|
38
43
|
|
39
44
|
0.0.9 - 2014-02-02
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
- support to BUILD and BUILDARGS #5 and #6
|
46
|
+
- support to trigger #7
|
47
|
+
- support to coerce #8
|
43
48
|
|
44
49
|
0.0.8 - 2014-02-01
|
45
50
|
- should support init_arg option in attr #14
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
- support lazy attributes / builder #4
|
52
|
+
- support a custom name for getter / setter #9
|
53
|
+
|
49
54
|
0.0.7 - 2014-02-01
|
50
55
|
- add min version of ruby should be 2.0.x
|
51
56
|
|
52
57
|
0.0.6 - 2014-02-01
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
- fix bug when extends subclass, now it is safe #18
|
59
|
+
- required attr with default value will no longer throw exception #30
|
60
|
+
- supports handles for: single method, array, Class or Module #27
|
56
61
|
|
57
62
|
0.0.5 - 2014-01-31
|
58
|
-
|
63
|
+
- should support handles #13 (partial)
|
59
64
|
|
60
65
|
0.0.4 - 2014-01-31
|
61
|
-
|
66
|
+
- fix an issue related to ruby > 1.9.2
|
62
67
|
|
63
68
|
0.0.3 - 2014-01-31
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
- add support to clearer #26
|
70
|
+
- add support to predicate #11
|
71
|
+
- add support to handle more than one attribute #2
|
72
|
+
- small improvements
|
68
73
|
|
69
74
|
0.0.2 - 2014-01-31
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
- fix one important bug, not it is possible use in more than one class
|
76
|
+
- add require option
|
77
|
+
- supports :ro, :rw, :rwp ( read-only, read-write and read-write-private) options
|
78
|
+
- supports default value as lambda or constant
|
79
|
+
- supports isa as class/module name or lambda
|
75
80
|
|
76
81
|
0.0.1 - 2014-01-31
|
77
|
-
|
82
|
+
- first release
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -474,7 +474,7 @@ Optional.
|
|
474
474
|
|
475
475
|
## doc => String
|
476
476
|
|
477
|
-
You can add a string metadata about the attribute.
|
477
|
+
You can add a string metadata about the attribute. If you include MooseX with `meta: true` you can inspect the list of attributes and documentation.
|
478
478
|
|
479
479
|
Optional.
|
480
480
|
|
@@ -557,8 +557,48 @@ The around hook is agressive: it will substitute the original method for a lambd
|
|
557
557
|
|
558
558
|
it is useful to manipulate the return value or argument list, add a begin/rescue block, aditional validations, etc, if you need.
|
559
559
|
|
560
|
+
## MooseX.init
|
561
|
+
|
562
|
+
The init method is useful to change some behavior of MooseX on the fly.
|
563
|
+
|
564
|
+
### warnings => true|false
|
565
|
+
|
566
|
+
will disable all warnings from MooseX. It is Global. Default is true.
|
567
|
+
|
568
|
+
```ruby
|
569
|
+
class X
|
570
|
+
include MooseX.init(warnings: false)
|
571
|
+
```
|
572
|
+
|
573
|
+
### fatal => true|false
|
574
|
+
|
575
|
+
all warnings will raise exceptions. It is Global. Default is false
|
576
|
+
|
577
|
+
```ruby
|
578
|
+
class X
|
579
|
+
include MooseX.init(fatal: true)
|
580
|
+
```
|
581
|
+
|
582
|
+
### meta => Symbol|String|true|false
|
583
|
+
|
584
|
+
will expose an alias for the class metadata in the class. If you use true, we will generate a method 'meta', if you provide a Symbol or String we will create a method with same name.
|
585
|
+
|
586
|
+
```ruby
|
587
|
+
class X
|
588
|
+
include MooseX.init(meta: true)
|
589
|
+
|
590
|
+
has :foo, { is: :rw, doc => "this is foo"}
|
591
|
+
end
|
592
|
+
|
593
|
+
X.meta.info # will return { :foo => "this is foo" }
|
594
|
+
```
|
595
|
+
|
596
|
+
The metadata has two public methods: attrs and info. Be careful.
|
597
|
+
|
560
598
|
## MooseX::Types
|
561
599
|
|
600
|
+
**WARNING** This will be extract for a parallel project/ gem!
|
601
|
+
|
562
602
|
MooseX has a built-in type system to be helpful in many circunstances. How many times you need check if some argument is_a? Something? Or it respond_to? :some_method ? Now it is over. If you include the **MooseX::Types** module in your MooseX class you can use:
|
563
603
|
|
564
604
|
### isAny
|
@@ -860,6 +900,8 @@ ex3 = BuildArgsExample2.new() # x == 4, y == 8
|
|
860
900
|
|
861
901
|
## EVENTS
|
862
902
|
|
903
|
+
**WARNING** This will be extract for a parallel project/ gem!
|
904
|
+
|
863
905
|
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 ).
|
864
906
|
|
865
907
|
```ruby
|
data/lib/moosex.rb
CHANGED
@@ -4,21 +4,26 @@
|
|
4
4
|
# Author:: Tiago Peczenyj (mailto:tiago.peczenyj@gmail.com)
|
5
5
|
# Copyright:: Copyright (c) 2014 Tiago Peczenyj
|
6
6
|
# License:: MIT
|
7
|
-
|
7
|
+
|
8
8
|
require "moosex/version"
|
9
9
|
require "moosex/types"
|
10
|
+
require "moosex/exceptions"
|
11
|
+
require "moosex/meta"
|
12
|
+
require "moosex/core"
|
13
|
+
require "moosex/attribute"
|
10
14
|
require "weakref"
|
11
15
|
|
12
16
|
module MooseX
|
17
|
+
@@ALIAS = nil
|
13
18
|
@@MOOSEX_WARNINGS = true
|
14
19
|
@@MOOSEX_FATAL = false
|
15
|
-
|
16
|
-
class FatalError < StandardError
|
17
|
-
end
|
18
|
-
|
20
|
+
|
19
21
|
def self.warn(x, *c)
|
20
|
-
|
21
|
-
|
22
|
+
if @@MOOSEX_FATAL
|
23
|
+
raise FatalError, "[MooseX] exception: #{x}",*c
|
24
|
+
elsif @@MOOSEX_WARNINGS
|
25
|
+
Kernel.warn("[MooseX] warning: #{x}")
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
def self.init(args={})
|
@@ -29,648 +34,76 @@ module MooseX
|
|
29
34
|
if args.has_key? :fatal
|
30
35
|
@@MOOSEX_FATAL = !! args[:fatal]
|
31
36
|
end
|
32
|
-
|
33
|
-
self
|
34
|
-
end
|
35
|
-
|
36
|
-
class RequiredMethodNotFoundError < NameError
|
37
|
-
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
c.extend(MooseX::Core)
|
42
|
-
|
43
|
-
def c.init(*args)
|
44
|
-
__meta.roles.each{|role| role.call(*args)}
|
45
|
-
|
46
|
-
self
|
38
|
+
if args.has_key?(:meta) && !! args[:meta]
|
39
|
+
@@ALIAS = (args[:meta].is_a?(TrueClass))? :meta : args[:meta]
|
47
40
|
end
|
48
41
|
|
49
|
-
|
50
|
-
|
51
|
-
MooseX.included(x)
|
52
|
-
x.__meta.load_from(self.__meta)
|
53
|
-
|
54
|
-
return unless x.is_a? Class
|
55
|
-
|
56
|
-
x.__meta.load_hooks(self.__meta)
|
57
|
-
self.__meta.init_klass(x)
|
58
|
-
|
59
|
-
x.__meta.requires.each do |method|
|
60
|
-
unless x.public_instance_methods.include? method
|
61
|
-
MooseX.warn "you must implement method '#{method}' in #{x} #{x.class}: required"# if $MOOSEX_DEBUG
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
meta = MooseX::Meta.new
|
67
|
-
|
68
|
-
unless c.respond_to? :__meta
|
69
|
-
c.class_exec do
|
70
|
-
define_singleton_method(:__meta) { meta }
|
71
|
-
define_singleton_method(:__meta_define_method) do |method_name, &proc|
|
72
|
-
define_method(method_name, proc)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def initialize(*args)
|
78
|
-
if self.respond_to? :BUILDARGS
|
79
|
-
args = self.BUILDARGS(*args)
|
80
|
-
else
|
81
|
-
args = args[0]
|
82
|
-
end
|
83
|
-
|
84
|
-
self.class.__meta().init(self, args || {})
|
85
|
-
|
86
|
-
self.BUILD() if self.respond_to? :BUILD
|
87
|
-
end
|
88
|
-
|
89
|
-
def c.inherited(subclass)
|
90
|
-
subclass.class_exec do
|
91
|
-
old_meta = subclass.__meta
|
92
|
-
|
93
|
-
meta = MooseX::Meta.new(old_meta)
|
94
|
-
|
95
|
-
define_singleton_method(:__meta) { meta }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
42
|
+
self
|
99
43
|
end
|
100
|
-
|
101
|
-
class Meta
|
102
|
-
attr_reader :attrs, :requires, :before, :after, :around, :roles
|
103
44
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@after = Hash.new { |hash, key| hash[key] = [] }
|
111
|
-
@around = Hash.new { |hash, key| hash[key] = [] }
|
45
|
+
def initialize(*args)
|
46
|
+
if self.respond_to? :BUILDARGS
|
47
|
+
args = self.BUILDARGS(*args)
|
48
|
+
else
|
49
|
+
args = args[0]
|
50
|
+
end
|
112
51
|
|
113
|
-
|
114
|
-
old_meta.attrs.each_pair do |key, value|
|
115
|
-
@attrs[key] = value.clone
|
116
|
-
end
|
117
|
-
@requires = old_meta.requires.clone
|
118
|
-
end
|
119
|
-
end
|
52
|
+
self.class.__moosex__meta.init(self, args || {})
|
120
53
|
|
121
|
-
|
122
|
-
|
123
|
-
@attrs[key] = value.clone
|
124
|
-
end
|
125
|
-
@requires += other_meta.requires
|
126
|
-
end
|
127
|
-
|
128
|
-
def load_hooks(other_meta)
|
129
|
-
other_meta.before.each_pair do |m, b|
|
130
|
-
@before[m] += b.clone
|
131
|
-
end
|
132
|
-
other_meta.after.each_pair do |m, b|
|
133
|
-
@after[m] += b.clone
|
134
|
-
end
|
135
|
-
other_meta.around.each_pair do |m, b|
|
136
|
-
@around[m] += b.clone
|
137
|
-
end
|
138
|
-
end
|
54
|
+
self.BUILD() if self.respond_to? :BUILD
|
55
|
+
end
|
139
56
|
|
140
|
-
|
141
|
-
if @attrs.has_key?(attr.attr_symbol) && ! attr.override
|
142
|
-
raise FatalError, "#{attr.attr_symbol} already exists, you should specify override: true"
|
143
|
-
end
|
144
|
-
@attrs[attr.attr_symbol] = attr
|
145
|
-
end
|
57
|
+
def MooseX.included(class_or_module)
|
146
58
|
|
147
|
-
|
148
|
-
@requires << method
|
149
|
-
end
|
59
|
+
class_or_module.extend(MooseX::Core)
|
150
60
|
|
151
|
-
|
152
|
-
|
153
|
-
end
|
61
|
+
unless class_or_module.respond_to? :__moosex__meta
|
62
|
+
meta = MooseX::Meta.new
|
154
63
|
|
155
|
-
|
156
|
-
@after[method_name] << block.clone
|
157
|
-
end
|
64
|
+
class_or_module.define_singleton_method(:__moosex__meta) { meta }
|
158
65
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
def add_role(block)
|
164
|
-
@roles << block
|
165
|
-
end
|
166
|
-
|
167
|
-
def init_klass(klass)
|
168
|
-
#return if @initialized
|
169
|
-
|
170
|
-
[@before.keys + @after.keys + @around.keys].flatten.uniq.each do |method_name|
|
171
|
-
begin
|
172
|
-
method = klass.instance_method method_name
|
173
|
-
rescue => e
|
174
|
-
MooseX.warn "Unable to apply hooks (after/before/around) in #{klass}::#{method_name} : #{e}" # if $MOOSEX_DEBUG
|
175
|
-
next
|
66
|
+
if @@ALIAS
|
67
|
+
class_or_module.class_eval do
|
68
|
+
class_or_module.define_singleton_method(@@ALIAS) { meta }
|
176
69
|
end
|
177
|
-
|
178
|
-
|
179
|
-
after = @after[method_name]
|
180
|
-
around = @around[method_name]
|
181
|
-
|
182
|
-
klass.__meta_define_method(method_name) do |*args, &proc|
|
183
|
-
before.each{|b| b.call(self,*args, &proc)}
|
184
|
-
|
185
|
-
original = lambda do |object, *args, &proc|
|
186
|
-
method.bind(object).call(*args, &proc)
|
187
|
-
end
|
188
|
-
|
189
|
-
result = around.inject(original) do |lambda1, lambda2|
|
190
|
-
lambda2.curry[lambda1]
|
191
|
-
end.call(self, *args, &proc)
|
192
|
-
|
193
|
-
after.each{|b| b.call(self,*args, &proc)}
|
194
|
-
|
195
|
-
result
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def init(object, args)
|
201
|
-
@attrs.each_pair{ |symbol, attr| attr.init(object, args) }
|
202
|
-
|
203
|
-
MooseX.warn "unused attributes #{args} for #{object.class}", caller unless args.empty?
|
204
|
-
|
205
|
-
@requires.each do |method|
|
206
|
-
unless object.respond_to? method
|
207
|
-
raise RequiredMethodNotFoundError,
|
208
|
-
"you must implement method '#{method}' in #{object.class}: required"
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
module Core
|
215
|
-
def on_init(&block)
|
216
|
-
__meta.add_role(block)
|
70
|
+
@@ALIAS = false
|
71
|
+
end
|
217
72
|
end
|
218
73
|
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
define_method method_name do |*args, &proc|
|
225
|
-
result = method.bind(self).call(*args, &proc)
|
226
|
-
block.call(self,*args,&proc)
|
227
|
-
result
|
228
|
-
end
|
229
|
-
rescue => e
|
230
|
-
MooseX.warn "unable to apply hook after in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
|
231
|
-
__meta.add_after(method_name, block)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def before(*methods_name, &block)
|
237
|
-
methods_name.each do |method_name|
|
238
|
-
begin
|
239
|
-
method = instance_method method_name
|
240
|
-
|
241
|
-
define_method method_name do |*args, &proc|
|
242
|
-
block.call(self,*args, &proc)
|
243
|
-
method.bind(self).call(*args, &proc)
|
244
|
-
end
|
245
|
-
rescue => e
|
246
|
-
MooseX.warn "unable to apply hook before in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
|
247
|
-
__meta.add_before(method_name, block)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def around(*methods_name, &block)
|
253
|
-
methods_name.each do |method_name|
|
254
|
-
begin
|
255
|
-
|
256
|
-
method = instance_method method_name
|
257
|
-
|
258
|
-
code = Proc.new do | o, *a, &proc|
|
259
|
-
method.bind(o).call(*a,&proc)
|
260
|
-
end
|
261
|
-
|
262
|
-
define_method method_name do |*args, &proc|
|
263
|
-
block.call(code, self,*args, &proc)
|
264
|
-
end
|
265
|
-
|
266
|
-
rescue => e
|
267
|
-
MooseX.warn "unable to apply hook around in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
|
268
|
-
__meta.add_around(method_name, block)
|
269
|
-
end
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
def requires(*methods)
|
274
|
-
|
275
|
-
methods.each do |method_name|
|
276
|
-
__meta.add_requires(method_name)
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def has(attr_name, attr_options = {})
|
281
|
-
if attr_name.is_a? Array
|
282
|
-
attr_name.each do |attr|
|
283
|
-
has(attr, attr_options)
|
284
|
-
end
|
285
|
-
elsif attr_name.is_a? Hash
|
286
|
-
attr_name.each_pair do |attr, options |
|
287
|
-
has(attr, options)
|
288
|
-
end
|
289
|
-
else
|
290
|
-
attr = MooseX::Attribute.new(attr_name, attr_options, self)
|
291
|
-
|
292
|
-
__meta.add(attr)
|
293
|
-
|
294
|
-
attr.methods.each_pair do |method, proc|
|
295
|
-
define_method method, &proc
|
296
|
-
end
|
297
|
-
|
298
|
-
if attr.is.eql?(:rwp)
|
299
|
-
private attr.writter
|
300
|
-
elsif attr.is.eql?(:private)
|
301
|
-
private attr.writter
|
302
|
-
private attr.reader
|
303
|
-
end
|
304
|
-
end
|
74
|
+
def class_or_module.init(*args)
|
75
|
+
__moosex__meta.init_roles(*args)
|
76
|
+
|
77
|
+
self
|
305
78
|
end
|
306
|
-
end
|
307
|
-
|
308
|
-
class InvalidAttributeError < TypeError
|
309
|
-
|
310
|
-
end
|
311
79
|
|
312
|
-
|
313
|
-
|
80
|
+
def class_or_module.inherited(subclass)
|
81
|
+
old_meta = subclass.__moosex__meta
|
314
82
|
|
315
|
-
|
316
|
-
DEFAULTS= {
|
317
|
-
is: :rw,
|
318
|
-
weak: false,
|
319
|
-
lazy: false,
|
320
|
-
clearer: false,
|
321
|
-
required: false,
|
322
|
-
predicate: false,
|
323
|
-
isa: isAny,
|
324
|
-
handles: {},
|
325
|
-
trigger: lambda {|object,value|}, # TODO: implement
|
326
|
-
coerce: lambda {|object| object}, # TODO: implement
|
327
|
-
doc: nil,
|
328
|
-
override: false,
|
329
|
-
}
|
83
|
+
meta = MooseX::Meta.new(old_meta)
|
330
84
|
|
331
|
-
|
332
|
-
|
333
|
-
VALIDATE = {
|
334
|
-
is: lambda do |is, field_name|
|
335
|
-
unless [:rw, :rwp, :ro, :lazy, :private].include?(is)
|
336
|
-
raise InvalidAttributeError, "invalid value for field '#{field_name}' is '#{is}', must be one of :private, :rw, :rwp, :ro or :lazy"
|
337
|
-
end
|
338
|
-
end,
|
339
|
-
};
|
340
|
-
|
341
|
-
COERCE = {
|
342
|
-
is: lambda do |is, field_name|
|
343
|
-
is.to_sym
|
344
|
-
end,
|
345
|
-
isa: lambda do |isa, field_name|
|
346
|
-
isType(isa)
|
347
|
-
end,
|
348
|
-
default: lambda do |default, field_name|
|
349
|
-
return default if default.is_a? Proc
|
350
|
-
|
351
|
-
return lambda { default }
|
352
|
-
end,
|
353
|
-
required: lambda do |required, field_name|
|
354
|
-
!!required
|
355
|
-
end,
|
356
|
-
lazy: lambda do |lazy, field_name|
|
357
|
-
!!lazy
|
358
|
-
end,
|
359
|
-
predicate: lambda do |predicate, field_name|
|
360
|
-
if ! predicate
|
361
|
-
return false
|
362
|
-
elsif predicate.is_a? TrueClass
|
363
|
-
return "has_#{field_name}?".to_sym
|
364
|
-
end
|
365
|
-
|
366
|
-
begin
|
367
|
-
predicate.to_sym
|
368
|
-
rescue => e
|
369
|
-
# create a nested exception here
|
370
|
-
raise InvalidAttributeError, "cannot coerce field predicate to a symbol for #{field_name}: #{e}"
|
371
|
-
end
|
372
|
-
end,
|
373
|
-
clearer: lambda do|clearer, field_name|
|
374
|
-
if ! clearer
|
375
|
-
return false
|
376
|
-
elsif clearer.is_a? TrueClass
|
377
|
-
return "clear_#{field_name}!".to_sym
|
378
|
-
end
|
85
|
+
subclass.define_singleton_method(:__moosex__meta) { meta }
|
86
|
+
end
|
379
87
|
|
380
|
-
|
381
|
-
clearer.to_sym
|
382
|
-
rescue => e
|
383
|
-
# create a nested exception here
|
384
|
-
raise InvalidAttributeError, "cannot coerce field clearer to a symbol for #{field_name}: #{e}"
|
385
|
-
end
|
386
|
-
end,
|
387
|
-
handles: lambda do |handles, field_name|
|
388
|
-
|
389
|
-
unless handles.is_a? Hash
|
390
|
-
|
391
|
-
array_of_handles = handles
|
392
|
-
|
393
|
-
unless array_of_handles.is_a? Array
|
394
|
-
array_of_handles = [ array_of_handles ]
|
395
|
-
end
|
396
|
-
|
397
|
-
handles = array_of_handles.map do |handle|
|
398
|
-
|
399
|
-
if handle == BasicObject
|
400
|
-
|
401
|
-
raise InvalidAttributeError, "ops, should not use BasicObject for handles in #{field_name}"
|
402
|
-
|
403
|
-
elsif handle.is_a? Class
|
404
|
-
|
405
|
-
handle = handle.public_instance_methods - handle.superclass.public_instance_methods
|
406
|
-
|
407
|
-
elsif handle.is_a? Module
|
408
|
-
|
409
|
-
handle = handle.public_instance_methods
|
410
|
-
|
411
|
-
end
|
412
|
-
|
413
|
-
handle
|
414
|
-
|
415
|
-
end.flatten.reduce({}) do |hash, method_name|
|
416
|
-
hash.merge({ method_name => method_name })
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
handles.map do |key,value|
|
421
|
-
if value.is_a? Hash
|
422
|
-
raise "ops! Handle should accept only one map / currying" unless value.count == 1
|
423
|
-
|
424
|
-
original, currying = value.shift
|
425
|
-
|
426
|
-
{ key.to_sym => [original.to_sym, currying] }
|
427
|
-
else
|
428
|
-
{ key.to_sym => value.to_sym }
|
429
|
-
end
|
430
|
-
end.reduce({}) do |hash,e|
|
431
|
-
hash.merge(e)
|
432
|
-
end
|
433
|
-
end,
|
434
|
-
reader: lambda do |reader, field_name|
|
435
|
-
reader.to_sym
|
436
|
-
end,
|
437
|
-
writter: lambda do |writter, field_name|
|
438
|
-
writter.to_sym
|
439
|
-
end,
|
440
|
-
builder: lambda do |builder, field_name|
|
441
|
-
unless builder.is_a? Proc
|
442
|
-
builder_method_name = builder.to_sym
|
443
|
-
builder = lambda do |object|
|
444
|
-
object.send(builder_method_name)
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
builder
|
449
|
-
end,
|
450
|
-
init_arg: lambda do |init_arg, field_name|
|
451
|
-
init_arg.to_sym
|
452
|
-
end,
|
453
|
-
trigger: lambda do |trigger, field_name|
|
454
|
-
unless trigger.is_a? Proc
|
455
|
-
trigger_method_name = trigger.to_sym
|
456
|
-
trigger = lambda do |object, value|
|
457
|
-
object.send(trigger_method_name,value)
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
trigger
|
462
|
-
end,
|
463
|
-
coerce: lambda do |coerce, field_name|
|
464
|
-
unless coerce.is_a? Proc
|
465
|
-
coerce_method_name = coerce.to_sym
|
466
|
-
coerce = lambda do |object|
|
467
|
-
object.send(coerce_method_name)
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
coerce
|
472
|
-
end,
|
473
|
-
weak: lambda do |weak, field_name|
|
474
|
-
!! weak
|
475
|
-
end,
|
476
|
-
doc: lambda do |doc, field_name|
|
477
|
-
doc.to_s
|
478
|
-
end,
|
479
|
-
override: lambda do |override, field_name|
|
480
|
-
!! override
|
481
|
-
end,
|
482
|
-
};
|
483
|
-
|
484
|
-
def initialize(a, o ,x)
|
485
|
-
#o ||= {}
|
486
|
-
# todo extract this to a framework, see issue #21 on facebook
|
487
|
-
o = DEFAULTS.merge({
|
488
|
-
reader: a,
|
489
|
-
writter: a.to_s.concat("=").to_sym,
|
490
|
-
builder: "build_#{a}".to_sym,
|
491
|
-
init_arg: a,
|
492
|
-
}).merge(o)
|
493
|
-
|
494
|
-
REQUIRED.each { |field|
|
495
|
-
unless o.has_key?(field)
|
496
|
-
raise InvalidAttributeError, "field #{field} is required for Attribute #{a}"
|
497
|
-
end
|
498
|
-
}
|
499
|
-
COERCE.each_pair do |field, coerce|
|
500
|
-
if o.has_key? field
|
501
|
-
o[field] = coerce.call(o[field], a)
|
502
|
-
end
|
503
|
-
end
|
504
|
-
VALIDATE.each_pair do |field, validate|
|
505
|
-
return if ! o.has_key? field
|
506
|
-
|
507
|
-
validate.call(o[field], a)
|
508
|
-
end
|
509
|
-
|
510
|
-
if o[:is].eql? :ro
|
511
|
-
o[:writter] = nil
|
512
|
-
elsif o[:is].eql? :lazy
|
513
|
-
o[:lazy] = true
|
514
|
-
o[:writter] = nil
|
515
|
-
end
|
516
|
-
|
517
|
-
unless o[:lazy]
|
518
|
-
o[:builder] = nil
|
519
|
-
end
|
520
|
-
|
521
|
-
if o[:weak]
|
522
|
-
old_coerce = o[:coerce]
|
523
|
-
o[:coerce] = lambda do |value|
|
524
|
-
WeakRef.new old_coerce.call(value)
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
@attr_symbol = a
|
529
|
-
@is = o.delete(:is)
|
530
|
-
@isa = o.delete(:isa)
|
531
|
-
@default = o.delete(:default)
|
532
|
-
@required = o.delete(:required)
|
533
|
-
@predicate = o.delete(:predicate)
|
534
|
-
@clearer = o.delete(:clearer)
|
535
|
-
@handles = o.delete(:handles)
|
536
|
-
@lazy = o.delete(:lazy)
|
537
|
-
@reader = o.delete(:reader)
|
538
|
-
@writter = o.delete(:writter)
|
539
|
-
@builder = o.delete(:builder)
|
540
|
-
@init_arg = o.delete(:init_arg)
|
541
|
-
@trigger = o.delete(:trigger)
|
542
|
-
@coerce = o.delete(:coerce)
|
543
|
-
@weak = o.delete(:weak)
|
544
|
-
@documentation = o.delete(:doc)
|
545
|
-
@override = o.delete(:override)
|
546
|
-
@methods = {}
|
547
|
-
|
548
|
-
MooseX.warn "Unused attributes #{o} for attribute #{a} @ #{x} #{x.class}",caller() if ! o.empty?
|
549
|
-
|
550
|
-
if @reader
|
551
|
-
@methods[@reader] = generate_reader
|
552
|
-
end
|
553
|
-
|
554
|
-
if @writter
|
555
|
-
@methods[@writter] = generate_writter
|
556
|
-
end
|
557
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
558
|
-
if @predicate
|
559
|
-
@methods[@predicate] = Proc.new do
|
560
|
-
instance_variable_defined? inst_variable_name
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
if @clearer
|
565
|
-
@methods[@clearer] = Proc.new do
|
566
|
-
if instance_variable_defined? inst_variable_name
|
567
|
-
remove_instance_variable inst_variable_name
|
568
|
-
end
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
attr_symbol = @attr_symbol
|
573
|
-
@handles.each_pair do | method, target_method |
|
574
|
-
if target_method.is_a? Array
|
575
|
-
original, currying = target_method
|
576
|
-
|
577
|
-
@methods[method] = Proc.new do |*args, &proc|
|
578
|
-
|
579
|
-
a1 = [ currying ]
|
580
|
-
|
581
|
-
if currying.is_a?Proc
|
582
|
-
a1 = currying.call()
|
583
|
-
elsif currying.is_a? Array
|
584
|
-
a1 = currying.map{|c| (c.is_a?(Proc)) ? c.call : c }
|
585
|
-
end
|
586
|
-
|
587
|
-
self.send(attr_symbol).send(original, *a1, *args, &proc)
|
588
|
-
end
|
589
|
-
else
|
590
|
-
@methods[method] = Proc.new do |*args, &proc|
|
591
|
-
self.send(attr_symbol).send(target_method, *args, &proc)
|
592
|
-
end
|
593
|
-
end
|
594
|
-
end
|
595
|
-
end
|
596
|
-
|
597
|
-
def init(object, args)
|
598
|
-
value = nil
|
599
|
-
value_from_default = false
|
88
|
+
def class_or_module.included(other_class_or_module)
|
600
89
|
|
601
|
-
|
602
|
-
value = args.delete(@init_arg)
|
603
|
-
elsif @default
|
604
|
-
value = @default.call
|
605
|
-
value_from_default = true
|
606
|
-
elsif @required
|
607
|
-
raise InvalidAttributeError, "attr \"#{@attr_symbol}\" is required"
|
608
|
-
else
|
609
|
-
return
|
610
|
-
end
|
90
|
+
MooseX.included(other_class_or_module)
|
611
91
|
|
612
|
-
|
613
|
-
begin
|
614
|
-
@isa.call( value )
|
615
|
-
rescue MooseX::Types::TypeCheckError => e
|
616
|
-
raise MooseX::Types::TypeCheckError, "isa check for field #{attr_symbol}: #{e}"
|
617
|
-
end
|
618
|
-
unless value_from_default
|
619
|
-
@trigger.call(object, value)
|
620
|
-
end
|
621
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
622
|
-
object.instance_variable_set inst_variable_name, value
|
623
|
-
end
|
92
|
+
other_class_or_module.__moosex__meta.load_from(self)
|
624
93
|
|
625
|
-
|
626
|
-
def generate_reader
|
627
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
628
|
-
|
629
|
-
builder = @builder
|
630
|
-
before_get = lambda {|object| }
|
94
|
+
if other_class_or_module.is_a? Class
|
631
95
|
|
632
|
-
|
633
|
-
type_check = @isa
|
634
|
-
coerce = @coerce
|
635
|
-
trigger = @trigger
|
636
|
-
before_get = lambda do |object|
|
637
|
-
return if object.instance_variable_defined? inst_variable_name
|
96
|
+
other_class_or_module.__moosex__meta.load_from_klass(self)
|
638
97
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
raise MooseX::Types::TypeCheckError, "isa check for #{inst_variable_name} from builder: #{e}"
|
645
|
-
end
|
98
|
+
self.__moosex__meta.init_klass(other_class_or_module).each_pair do |method_name, proc|
|
99
|
+
other_class_or_module.class_eval do
|
100
|
+
define_method(method_name,&proc)
|
101
|
+
end
|
102
|
+
end
|
646
103
|
|
647
|
-
|
648
|
-
object.instance_variable_set(inst_variable_name, value)
|
649
|
-
end
|
650
|
-
end
|
104
|
+
other_class_or_module.__moosex__meta.verify_requires_for(other_class_or_module)
|
651
105
|
|
652
|
-
|
653
|
-
before_get.call(self)
|
654
|
-
instance_variable_get inst_variable_name
|
655
|
-
end
|
106
|
+
end
|
656
107
|
end
|
657
|
-
|
658
|
-
def generate_writter
|
659
|
-
writter_name = @writter
|
660
|
-
inst_variable_name = "@#{@attr_symbol}".to_sym
|
661
|
-
coerce = @coerce
|
662
|
-
type_check = @isa
|
663
|
-
trigger = @trigger
|
664
|
-
Proc.new do |value|
|
665
|
-
value = coerce.call(value)
|
666
|
-
begin
|
667
|
-
type_check.call( value )
|
668
|
-
rescue MooseX::Types::TypeCheckError => e
|
669
|
-
raise MooseX::Types::TypeCheckError, "isa check for #{writter_name}: #{e}"
|
670
|
-
end
|
671
|
-
trigger.call(self,value)
|
672
|
-
instance_variable_set inst_variable_name, value
|
673
|
-
end
|
674
|
-
end
|
675
108
|
end
|
676
109
|
end
|