moosex 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fcfa29200c1f61890442923acc7dc15d025634f0
4
- data.tar.gz: fa879cea6cd2145c67216366c944278f09510ac6
3
+ metadata.gz: 95164f069f5f27913dfeb6140414d1b0f15e9687
4
+ data.tar.gz: 8c93aee2bfaea678c830ed0a1288e170a2bc0bf2
5
5
  SHA512:
6
- metadata.gz: 258f22f909577942823e51962d8191c9827ed2cf4194bee1cb034a87cf056c0e83f55ce2738bd5769d4b4f021e53dd7855e623a953fe38672927aab3fa9d7833
7
- data.tar.gz: 729fe046482be2b464dfe0f2709167737fbb88f1b28afe0b8f2215b8ce06b82fa6b38f793ac3df3a360ec11413ae37e2ba94ec8ade4b52df6599c38d454f8a10
6
+ metadata.gz: 23c4c3ff11a33e0d034621564c0c336ce42ab71e511dd04a35cb8a23e2292ac41f80097c9071f61f163461c9f0e98408ec98006342115fa23bb35d896935e7e4
7
+ data.tar.gz: 9ee664b8973c9187a510ba14dec13fc89a4c40e3a596793f0a91233dcde4e2a6e711857941432d71ca61ad2a36dab0a03009b280112a198c3a65b1c860a1bcb5
@@ -1 +1 @@
1
- repo_token: CROa4WDRvgcQjh5zATUpjPRURmXEgATMt
1
+ repo_token: E074PGwOA6oPInUXCJnKwYt2dvkGrLyvn
data/.gitignore CHANGED
@@ -11,7 +11,7 @@ spec/reports
11
11
  test/tmp
12
12
  test/version_tmp
13
13
  tmp
14
-
14
+ vendor/
15
15
  # YARD artifacts
16
16
  .yardoc
17
17
  _yardoc
@@ -1,4 +1,6 @@
1
1
  language: ruby
2
+ before_install: gem install bundler
3
+ script: "bundle exec rspec -r spec_helper"
2
4
  rvm:
3
5
  - 2.0.0
4
6
  - 2.1.0
data/Changelog CHANGED
@@ -1,3 +1,9 @@
1
+ 0.0.17 - 2013-02-11
2
+ - has now support an override option #56
3
+ - has now support a doc option #57
4
+ - has now should not require :is => default is :rw #54
5
+ - add weak ref support #49
6
+
1
7
  0.0.16 - 2013-02-07
2
8
  - add currying to handles #46
3
9
  - not after/before/around works well with methods who receive a block
@@ -68,4 +74,4 @@
68
74
  - supports isa as class/module name or lambda
69
75
 
70
76
  0.0.1 - 2014-01-31
71
- - first release
77
+ - first release
@@ -1,15 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- moosex (0.0.16)
4
+ moosex (0.0.17)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ coveralls (0.7.0)
10
+ multi_json (~> 1.3)
11
+ rest-client
12
+ simplecov (>= 0.7)
13
+ term-ansicolor
14
+ thor
9
15
  diff-lcs (1.2.5)
10
16
  docile (1.1.3)
17
+ mime-types (2.1)
11
18
  multi_json (1.8.4)
12
19
  rake (10.1.1)
20
+ rest-client (1.6.7)
21
+ mime-types (>= 1.16)
13
22
  rspec (2.14.1)
14
23
  rspec-core (~> 2.14.0)
15
24
  rspec-expectations (~> 2.14.0)
@@ -23,13 +32,18 @@ GEM
23
32
  multi_json
24
33
  simplecov-html (~> 0.8.0)
25
34
  simplecov-html (0.8.0)
35
+ term-ansicolor (1.3.0)
36
+ tins (~> 1.0)
37
+ thor (0.18.1)
38
+ tins (1.0.0)
26
39
 
27
40
  PLATFORMS
28
41
  ruby
29
42
 
30
43
  DEPENDENCIES
31
44
  bundler (~> 1.5)
45
+ coveralls (>= 0.7.0)
32
46
  moosex!
33
47
  rake
34
48
  rspec
35
- simplecov
49
+ simplecov (>= 0.8.2)
data/README.md CHANGED
@@ -1,6 +1,40 @@
1
1
  # MooseX
2
2
 
3
- A postmodern object DSL for Ruby [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](https://travis-ci.org/peczenyj/MooseX) [![Gem Version](https://badge.fury.io/rb/moosex.png)](http://badge.fury.io/rb/moosex)
3
+ A postmodern object DSL for Ruby [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](https://travis-ci.org/peczenyj/MooseX) [![Gem Version](https://badge.fury.io/rb/moosex.png)](http://badge.fury.io/rb/moosex) [![Code Climate](https://codeclimate.com/github/peczenyj/MooseX.png)](https://codeclimate.com/github/peczenyj/MooseX) [![Dependency Status](https://gemnasium.com/peczenyj/MooseX.png)](https://gemnasium.com/peczenyj/MooseX) [![Coverage Status](https://coveralls.io/repos/peczenyj/MooseX/badge.png)](https://coveralls.io/r/peczenyj/MooseX) [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/f51f40f92298589b598a55bc753977f9 "githalytics.com")](http://githalytics.com/peczenyj/MooseX)
4
+ ## Introduction
5
+
6
+ This is another DSL for object creation, aspects, method delegation and much more. It is based on Perl Moose and Moo, two important modules who add a better way of Object Orientation development (and I enjoy A LOT). Using a declarative style, using Moose/Moo you can create attributes, methods, the entire constructor and much more. But I can't find something similar in Ruby world, so I decide port a small subset of Moose to create a powerfull DSL for object construction.
7
+
8
+ Of course, there is few similar projects in ruby like
9
+
10
+ - [Virtus](https://github.com/solnic/virtus)
11
+ - [Active Record Validations](http://edgeguides.rubyonrails.org/active_record_validations.html)
12
+
13
+ But the objetive of MooseX is different: this is a toolbox to create Classes based on DSL, with unique features like
14
+
15
+ - method delegation ( see 'handles')
16
+ - lazy attributes
17
+ - roles
18
+ - parameterized roles
19
+ - composable type check
20
+ - events
21
+
22
+ and much more.
23
+
24
+ This rubygem is based on this modules:
25
+
26
+ - [Perl Moose](http://search.cpan.org/~ether/Moose-2.1204/lib/Moose.pm)
27
+ - [Perl Moo](http://search.cpan.org/~ether/Moose-2.1204/lib/Moose.pm)
28
+ - [MooX::Types::MooseLike::Base](http://search.cpan.org/~mateu/MooX-Types-MooseLike-0.25/lib/MooX/Types/MooseLike/Base.pm)
29
+ - [MooseX::Event](http://search.cpan.org/~winter/MooseX-Event-v0.2.0/lib/MooseX/Event.pm)
30
+ - [MooseX::Role::Parameterized](http://search.cpan.org/~sartak/MooseX-Role-Parameterized-1.02/lib/MooseX/Role/Parameterized/Tutorial.pod)
31
+
32
+ See also:
33
+
34
+ - [Joose](https://code.google.com/p/joose-js/), a javascript port of Moose.
35
+ - [Perl 6](http://en.wikipedia.org/wiki/Perl_6#Object-oriented_programming) Perl 6 OO programming style.
36
+
37
+ Why MooseX? Because the namespace MooseX/MooX is open to third-party projects/plugins/extensions. You can upgrade your Moo(se) class using other components if you want. And there is one gem called 'moose' :/
4
38
 
5
39
  THIS MODULE IS EXPERIMENTAL YET! BE CAREFUL!
6
40
 
@@ -112,9 +146,9 @@ to describe one new attribute you shoud specify some properties inside a Hash. T
112
146
 
113
147
  The options for "has" are as follows:
114
148
 
115
- ### is
149
+ ### is => ro|rw|rwp|private|lazy
116
150
 
117
- **Required**, may be :ro, :rw, :rwp, :private or :lazy.
151
+ **Important**, may be :ro, :rw, :rwp, :private or :lazy. If you not specify, we will consider :rw, with all acessors with public visibility (**NEW**).
118
152
 
119
153
  "ro" specify a read-only attribute - generate only the reader method - you should specify the value in the constructor or using "default".
120
154
 
@@ -126,7 +160,7 @@ The options for "has" are as follows:
126
160
 
127
161
  "lazy" similar to "ro", but also sets "lazy" to true and "builder" to "build_#{attribute_name}".
128
162
 
129
- ### isa
163
+ ### isa => Class|lambda
130
164
 
131
165
  You can specify an optional type check for the attribute. Accepts a lambda, and it must raise one exception if the type check fails. If you provides a Class or Module, we will call the 'is_a?' method in the new value againt the Class/Module. We call the type check routine on the constructor and in each call of the writter method.
132
166
 
@@ -141,7 +175,7 @@ You can specify your own kind of type validation.
141
175
 
142
176
  Important: if you access the attribute instance name using @attribute_name= you loose the type check feature. You need always set/get the attribute value using the acessors generated by MooseX.
143
177
 
144
- ### default
178
+ ### default => Constant|lambda
145
179
 
146
180
  You can specify an optional default value to one attribute. If we don't specify in the constructor, we will initialize the attribute with this value. You also can specify one lambda to force object creation.
147
181
 
@@ -153,7 +187,7 @@ or
153
187
  default: lambda{ MyObject.new },
154
188
  ```
155
189
 
156
- ### required
190
+ ### required => true|false
157
191
 
158
192
  if true, the constructor will raise error if this attribute was not present.
159
193
 
@@ -165,7 +199,7 @@ if this attribute has a default value, we will initialize with this value and no
165
199
 
166
200
  Optional.
167
201
 
168
- ### coerce
202
+ ### coerce => method name|lambda
169
203
 
170
204
  You can try to coerce the attribute value by a lambda/method before the type check phase. For example you can do
171
205
 
@@ -181,7 +215,7 @@ or just
181
215
 
182
216
  to force a convertion to integer. Or flatten one array, convert to symbol, etc. Optional.
183
217
 
184
- ### handles
218
+ ### handles => Array|Hash|Class|Module
185
219
 
186
220
  One of the greatest features in MooseX: you can inject methods and delegate the method calling to the attribute. For example, instead do this:
187
221
 
@@ -264,7 +298,7 @@ obj.my_method_1(2,3)
264
298
  ```
265
299
  are equivalent.
266
300
 
267
- ### trigger
301
+ ### trigger => method name|lambda
268
302
 
269
303
  You can specify one lambda or method name to be executed in each writter ( if coerce and type check does not raise any exception ). The trigger will be called in each setter and in the constructor if we do not use the default value. Useful to add a logging operation or some complex validation.
270
304
 
@@ -290,15 +324,15 @@ has b: {
290
324
 
291
325
  Optional.
292
326
 
293
- ### writter
327
+ ### writter => true|method name
294
328
 
295
- You can specify the name of the attribute acessor, default is "#{attribute_name}=".
329
+ You can specify the name of the attribute acessor, default is "#{attribute_name}=" (if true).
296
330
 
297
- ### reader
331
+ ### reader => true|method name
298
332
 
299
- You can specify the name of the attribute acessor, default is "attribute_name".
333
+ You can specify the name of the attribute acessor, default is "attribute_name" (if true).
300
334
 
301
- ### predicate
335
+ ### predicate => true|method name
302
336
 
303
337
  Creates a method who returns a boolean value if the attribute is defined. If true, will create one public "has_#{attribute_name}?" method by default.
304
338
 
@@ -322,7 +356,7 @@ Important: nil is different than undefined. If you do not initialize one attribu
322
356
 
323
357
  Optional.
324
358
 
325
- ### clearer
359
+ ### clearer => true| attribute name
326
360
 
327
361
  Creates a method who will unset the attribute. If true, will create one public "clear_#{attribute_name}!" method by default. Unset in this case is not 'nil', we will remove the instance variable. For example:
328
362
 
@@ -346,7 +380,7 @@ foo.has_x? # returns false
346
380
  ```
347
381
  Optional.
348
382
 
349
- ### init_arg
383
+ ### init_arg => "new attribute name"
350
384
 
351
385
  You can rename the attribute name in the constructor. For example:
352
386
 
@@ -367,7 +401,7 @@ foo.x # return 1
367
401
  foo.x= 2 # will set 'secret' to 2
368
402
  ```
369
403
 
370
- ### lazy
404
+ ### lazy => true|false
371
405
 
372
406
  Another great feature: lazy attributes. If you this to true, we will wait until the first reader accessor be called to create the object using the builder method, then store the value. For example:
373
407
 
@@ -400,7 +434,7 @@ A lazy attribute needs a builder method or lambda. By default you should impleme
400
434
 
401
435
  Optional.
402
436
 
403
- ### builder
437
+ ### builder => method name|lambda
404
438
 
405
439
  You can specify the builder name if the attribute is lazy, or you can specity one lambda. If true, the default name of the builder will be "builder_#{attribute_name}". This attribute will be ignored if the attribute is not lazy.
406
440
 
@@ -417,6 +451,37 @@ end
417
451
  ```
418
452
  Optional.
419
453
 
454
+ ### weak => true|false
455
+
456
+ If true, we will always coerce the value from default, constructor or writter to a WeakRef. Weak Reference class that allows a referenced object to be garbage-collected.
457
+
458
+ ```ruby
459
+ class Foo
460
+ include MooseX
461
+ has x: { is: :rw, weak: true }
462
+ end
463
+
464
+ f = Foo.new(x: Object.new)
465
+
466
+ puts f.x.class # will be WeakRef
467
+ GC.start
468
+ puts f.x # may raise exception (recycled)
469
+ ```
470
+
471
+ You should verify with `weakref_alive?` method to avoid exceptions.
472
+
473
+ Optional.
474
+
475
+ ## doc => String
476
+
477
+ You can add a string metadata about the attribute. It will be stored, and you can use this in a near future.
478
+
479
+ Optional.
480
+
481
+ ## override => true|false
482
+
483
+ If you need override one attribute, you should use `override: true`, or MooseX will raise one exception.
484
+
420
485
  ## Hooks: after/before/around
421
486
 
422
487
  Another great feature imported from Moose are the hooks after/before/around one method. You can run an arbitrary code, for example:
@@ -451,11 +516,11 @@ Roles and Hooks
451
516
 
452
517
  If you try to add one role to a method who does not exists yet, this will be added in the next class. BE CAREFUL, THIS IS EXPERIMENTAL! PLEASE REPORT ANY BUG IF YOU FIND!!!
453
518
 
454
- ### after
519
+ ### after (method| ARRAY) => lambda
455
520
 
456
521
  The after hook should receive the name of the method as a Symbol and a lambda. This lambda will, in the argument list, one reference for the object (self) and the rest of the arguments. This will redefine the the original method, add the code to run after the method. The after does not affect the return value of the original method, if you need this, use the 'around' hook.
457
522
 
458
- ### before
523
+ ### before (method| ARRAY) => lambda
459
524
 
460
525
  The before hook should receive the name of the method as a Symbol and a lambda. This lambda will, in the argument list, one reference for the object (self) and the rest of the arguments. This will redefine the the original method, add the code to run before the method.
461
526
 
@@ -478,7 +543,7 @@ class Point
478
543
  end
479
544
  ```
480
545
 
481
- ### around
546
+ ### around (method| ARRAY) => lambda
482
547
 
483
548
  The around hook is agressive: it will substitute the original method for a lambda. This lambda will receive the original method as a lambda, a reference for the object and the argument list, you shuld call the method_lambda using object + arguments
484
549
 
@@ -691,9 +756,9 @@ Roles can support has to describe attributes, and you can reuse code easily.
691
756
  You can also mark one or more methods as 'required'. When you do this, we will raise one exception if you try to create a new instance and the class does not implement it. It is a safe way to create interfaces or abstract classes. It uses respond_to? to verify.
692
757
 
693
758
 
694
- ## Parametric Roles
759
+ ## Parameterized Roles
695
760
 
696
- Parametric roles is a good way of reuse code based on roles. For example, to create one or more attributes in the class who includes our role, we just add the code to be executed in the on_init hook.
761
+ Parameterized roles is a good way of reuse code based on roles. For example, to create one or more attributes in the class who includes our role, we just add the code to be executed in the on_init hook.
697
762
 
698
763
  ```ruby
699
764
  module EasyCrud
@@ -713,9 +778,9 @@ class LogTest
713
778
 
714
779
  when we call `init` with arguments, we will call all on_init blocks defined in the role. In this example we inject attributes 'a' and 'b' with reader/writter and a predicate based on the name ex: `has_attr_a_or_not?`
715
780
 
716
- ### composable parametric roles
781
+ ### composable parameterized roles
717
782
 
718
- To combine one or more parametric roles to another parametric role you should do something like this:
783
+ To combine one or more parameterized roles to another parameterized role you should do something like this:
719
784
 
720
785
  ```ruby
721
786
  module Logabble2
@@ -889,7 +954,7 @@ ep.ping # will print "receive ping!"
889
954
  ep.pong 1 # will print "receive pong with 1!"
890
955
  ```
891
956
 
892
- Now, imagine what you can do with a Parametrized Role: we can create all handles based on event names!
957
+ Now, imagine what you can do with a parameterized role: we can create all handles based on event names!
893
958
 
894
959
  ## IMPORTANT
895
960
 
@@ -899,12 +964,16 @@ Until the first 0.1 version I can change anything without warning.
899
964
 
900
965
  I am open to suggestions too.
901
966
 
967
+ ## Mailing List
968
+
969
+ https://groups.google.com/d/forum/moosex-ruby-dev-list
970
+
902
971
  ## TODO
903
972
 
904
- 1. Support to Roles ( it is a Module on Steroids )
905
- 2. Support to after/before/around
906
- 3. Improve the typecheck system (we should specify: we need an array of positive integers)
907
- 4. Improve the exception and warning system
973
+ 1. Support to Roles ( it is a Module on Steroids ) [done]
974
+ 2. Support to after/before/around [done]
975
+ 3. Improve the typecheck system (we should specify: we need an array of positive integers) [done]
976
+ 4. Improve the exception and warning system [in progress]
908
977
  5. Profit!
909
978
 
910
979
  ## Limitations
@@ -920,3 +989,9 @@ Now has limited support to subclassing.
920
989
  3. Commit your changes (`git commit -am 'Add some feature'`)
921
990
  4. Push to the branch (`git push origin my-new-feature`)
922
991
  5. Create new Pull Request
992
+
993
+ ## About the Author
994
+
995
+ [@pac_man](https://twitter.com/pac_man)
996
+
997
+ [about.me](http://about.me/peczenyj)
@@ -7,410 +7,417 @@
7
7
  #
8
8
  require "moosex/version"
9
9
  require "moosex/types"
10
+ require "weakref"
10
11
 
11
12
  module MooseX
12
- @@MOOSEX_WARNINGS = true
13
- @@MOOSEX_FATAL = false
14
-
15
- class FatalError < StandardError
16
- end
17
-
18
- def self.warn(x, *c)
19
- raise FatalError, "[MooseX] exception: #{x}",*c if @@MOOSEX_FATAL
13
+ @@MOOSEX_WARNINGS = true
14
+ @@MOOSEX_FATAL = false
15
+
16
+ class FatalError < StandardError
17
+ end
18
+
19
+ def self.warn(x, *c)
20
+ raise FatalError, "[MooseX] exception: #{x}",*c if @@MOOSEX_FATAL
20
21
  Kernel.warn("[MooseX] warning: #{x}") if @@MOOSEX_WARNINGS
21
- end
22
-
23
- def self.init(args={})
24
- if args.has_key? :warnings
25
- @@MOOSEX_WARNINGS = !! args[:warnings]
26
- end
27
-
28
- if args.has_key? :fatal
29
- @@MOOSEX_FATAL = !! args[:fatal]
30
- end
31
-
32
- self
22
+ end
23
+
24
+ def self.init(args={})
25
+ if args.has_key? :warnings
26
+ @@MOOSEX_WARNINGS = !! args[:warnings]
27
+ end
28
+
29
+ if args.has_key? :fatal
30
+ @@MOOSEX_FATAL = !! args[:fatal]
31
+ end
32
+
33
+ self
33
34
  end
34
35
 
35
- class RequiredMethodNotFoundError < NameError
36
- end
36
+ class RequiredMethodNotFoundError < NameError
37
+ end
37
38
 
38
- def MooseX.included(c)
39
-
40
- c.extend(MooseX::Core)
39
+ def MooseX.included(c)
40
+
41
+ c.extend(MooseX::Core)
41
42
 
42
43
  def c.init(*args)
43
44
  __meta.roles.each{|role| role.call(*args)}
44
45
 
45
46
  self
46
47
  end
47
-
48
- def c.included(x)
49
-
50
- MooseX.included(x)
51
- x.__meta.load_from(self.__meta)
52
-
53
- return unless x.is_a? Class
54
-
55
- x.__meta.load_hooks(self.__meta)
56
- self.__meta.init_klass(x)
57
-
58
- x.__meta.requires.each do |method|
59
- unless x.public_instance_methods.include? method
60
- MooseX.warn "you must implement method '#{method}' in #{x} #{x.class}: required"# if $MOOSEX_DEBUG
61
- end
62
- end
63
- end
64
-
65
- meta = MooseX::Meta.new
66
-
67
- unless c.respond_to? :__meta
68
- c.class_exec do
69
- define_singleton_method(:__meta) { meta }
70
- define_singleton_method(:__meta_define_method) do |method_name, &proc|
71
- define_method(method_name, proc)
72
- end
73
- end
74
- end
75
-
76
- def initialize(*args)
77
- if self.respond_to? :BUILDARGS
78
- args = self.BUILDARGS(*args)
79
- else
80
- args = args[0]
81
- end
82
-
83
- self.class.__meta().init(self, args || {})
84
-
85
- self.BUILD() if self.respond_to? :BUILD
86
- end
87
-
88
- def c.inherited(subclass)
89
- subclass.class_exec do
90
- old_meta = subclass.__meta
91
-
92
- meta = MooseX::Meta.new(old_meta)
93
-
94
- define_singleton_method(:__meta) { meta }
95
- end
96
- end
97
-
98
- end
99
-
100
- class Meta
101
- attr_reader :attrs, :requires, :before, :after, :around, :roles
102
-
103
- def initialize(old_meta=nil)
104
- @initialized = false
105
- @attrs = {}
106
- @requires = []
107
- @roles = []
108
- @before = Hash.new { |hash, key| hash[key] = [] }
109
- @after = Hash.new { |hash, key| hash[key] = [] }
110
- @around = Hash.new { |hash, key| hash[key] = [] }
111
-
112
- if old_meta
113
- old_meta.attrs.each_pair do |key, value|
114
- @attrs[key] = value.clone
115
- end
116
- @requires = old_meta.requires.clone
117
- end
118
- end
119
-
120
- def load_from(other_meta)
121
- other_meta.attrs.each_pair do |key, value|
122
- @attrs[key] = value.clone
123
- end
124
- @requires += other_meta.requires
125
- end
126
-
127
- def load_hooks(other_meta)
128
- other_meta.before.each_pair do |m, b|
129
- @before[m] += b.clone
130
- end
131
- other_meta.after.each_pair do |m, b|
132
- @after[m] += b.clone
133
- end
134
- other_meta.around.each_pair do |m, b|
135
- @around[m] += b.clone
136
- end
137
- end
138
-
139
- def add(attr)
140
- @attrs[attr.attr_symbol] = attr
141
- end
142
-
143
- def add_requires(method)
144
- @requires << method
145
- end
146
-
147
- def add_before(method_name, block)
148
- @before[method_name] << block.clone
149
- end
150
-
151
- def add_after(method_name, block)
152
- @after[method_name] << block.clone
153
- end
154
-
155
- def add_around(method_name, block)
156
- @around[method_name] << block.clone
157
- end
158
-
159
- def add_role(block)
160
- @roles << block
48
+
49
+ def c.included(x)
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
161
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
162
92
 
163
- def init_klass(klass)
164
- #return if @initialized
93
+ meta = MooseX::Meta.new(old_meta)
165
94
 
166
- [@before.keys + @after.keys + @around.keys].flatten.uniq.each do |method_name|
167
- begin
168
- method = klass.instance_method method_name
95
+ define_singleton_method(:__meta) { meta }
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ class Meta
102
+ attr_reader :attrs, :requires, :before, :after, :around, :roles
103
+
104
+ def initialize(old_meta=nil)
105
+ @initialized = false
106
+ @attrs = {}
107
+ @requires = []
108
+ @roles = []
109
+ @before = Hash.new { |hash, key| hash[key] = [] }
110
+ @after = Hash.new { |hash, key| hash[key] = [] }
111
+ @around = Hash.new { |hash, key| hash[key] = [] }
112
+
113
+ if old_meta
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
120
+
121
+ def load_from(other_meta)
122
+ other_meta.attrs.each_pair do |key, value|
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
139
+
140
+ def add(attr)
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
146
+
147
+ def add_requires(method)
148
+ @requires << method
149
+ end
150
+
151
+ def add_before(method_name, block)
152
+ @before[method_name] << block.clone
153
+ end
154
+
155
+ def add_after(method_name, block)
156
+ @after[method_name] << block.clone
157
+ end
158
+
159
+ def add_around(method_name, block)
160
+ @around[method_name] << block.clone
161
+ end
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
169
173
  rescue => e
170
174
  MooseX.warn "Unable to apply hooks (after/before/around) in #{klass}::#{method_name} : #{e}" # if $MOOSEX_DEBUG
171
175
  next
172
176
  end
173
177
 
174
- before = @before[method_name]
175
- after = @after[method_name]
176
- around = @around[method_name]
177
-
178
- klass.__meta_define_method(method_name) do |*args, &proc|
179
- before.each{|b| b.call(self,*args, &proc)}
180
-
181
- original = lambda do |object, *args, &proc|
182
- method.bind(object).call(*args, &proc)
183
- end
184
-
185
- result = around.inject(original) do |lambda1, lambda2|
186
- lambda2.curry[lambda1]
187
- end.call(self, *args, &proc)
188
-
189
- after.each{|b| b.call(self,*args, &proc)}
190
-
191
- result
192
- end
193
- end
194
- end
195
-
196
- def init(object, args)
197
- @attrs.each_pair{ |symbol, attr| attr.init(object, args) }
198
-
199
- MooseX.warn "unused attributes #{args} for #{object.class}", caller unless args.empty?
200
-
201
- @requires.each do |method|
202
- unless object.respond_to? method
203
- raise RequiredMethodNotFoundError,
204
- "you must implement method '#{method}' in #{object.class}: required"
205
- end
206
- end
207
- end
208
- end
209
-
210
- module Core
211
- def on_init(&block)
212
- __meta.add_role(block)
213
- end
214
-
215
- def after(*methods_name, &block)
216
- methods_name.each do |method_name|
217
- begin
218
- method = instance_method method_name
219
-
220
- define_method method_name do |*args, &proc|
221
- result = method.bind(self).call(*args, &proc)
222
- block.call(self,*args,&proc)
223
- result
224
- end
225
- rescue => e
226
- MooseX.warn "unable to apply hook after in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
227
- __meta.add_after(method_name, block)
228
- end
229
- end
230
- end
231
-
232
- def before(*methods_name, &block)
233
- methods_name.each do |method_name|
234
- begin
235
- method = instance_method method_name
236
-
237
- define_method method_name do |*args, &proc|
238
- block.call(self,*args, &proc)
239
- method.bind(self).call(*args, &proc)
240
- end
241
- rescue => e
242
- MooseX.warn "unable to apply hook before in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
243
- __meta.add_before(method_name, block)
244
- end
245
- end
246
- end
247
-
248
- def around(*methods_name, &block)
249
- methods_name.each do |method_name|
250
- begin
251
-
252
- method = instance_method method_name
253
-
254
- code = Proc.new do | o, *a, &proc|
255
- method.bind(o).call(*a,&proc)
256
- end
257
-
258
- define_method method_name do |*args, &proc|
259
- block.call(code, self,*args, &proc)
260
- end
261
-
262
- rescue => e
263
- MooseX.warn "unable to apply hook around in #{method_name} @ #{self}: #{e}", caller() if self.is_a?(Class)
264
- __meta.add_around(method_name, block)
265
- end
266
- end
267
- end
268
-
269
- def requires(*methods)
270
-
271
- methods.each do |method_name|
272
- __meta.add_requires(method_name)
273
- end
274
- end
275
-
276
- def has(attr_name, attr_options = {})
277
- if attr_name.is_a? Array
278
- attr_name.each do |attr|
279
- has(attr, attr_options)
280
- end
281
- elsif attr_name.is_a? Hash
282
- attr_name.each_pair do |attr, options |
283
- has(attr, options)
284
- end
285
- else
286
-
287
- attr = MooseX::Attribute.new(attr_name, attr_options, self)
288
-
289
- attr.methods.each_pair do |method, proc|
290
- define_method method, &proc
291
- end
292
-
293
- if attr.is.eql?(:rwp)
294
- private attr.writter
295
- elsif attr.is.eql?(:private)
296
- private attr.writter
297
- private attr.reader
298
- end
299
-
300
- __meta.add(attr)
301
- end
302
- end
303
- end
304
-
305
- class InvalidAttributeError < TypeError
306
-
307
- end
308
-
309
- class Attribute
310
- include MooseX::Types
311
-
312
- attr_reader :attr_symbol, :is, :reader, :writter, :lazy, :builder, :methods
313
- DEFAULTS= {
314
- lazy: false,
315
- clearer: false,
316
- required: false,
317
- predicate: false,
318
- isa: isAny,
319
- handles: {},
320
- trigger: lambda {|object,value|}, # TODO: implement
321
- coerce: lambda {|object| object}, # TODO: implement
322
- }
323
-
324
- REQUIRED = [ :is ]
325
-
326
- VALIDATE = {
327
- is: lambda do |is, field_name|
328
- unless [:rw, :rwp, :ro, :lazy, :private].include?(is)
329
- raise InvalidAttributeError, "invalid value for field '#{field_name}' is '#{is}', must be one of :private, :rw, :rwp, :ro or :lazy"
330
- end
331
- end,
332
- };
333
-
334
- COERCE = {
335
- is: lambda do |is, field_name|
336
- is.to_sym
337
- end,
338
- isa: lambda do |isa, field_name|
339
- isType(isa)
340
- end,
341
- default: lambda do |default, field_name|
342
- return default if default.is_a? Proc
343
-
344
- return lambda { default }
345
- end,
346
- required: lambda do |required, field_name|
347
- !!required
348
- end,
349
- lazy: lambda do |lazy, field_name|
350
- !!lazy
351
- end,
352
- predicate: lambda do |predicate, field_name|
353
- if ! predicate
354
- return false
355
- elsif predicate.is_a? TrueClass
356
- return "has_#{field_name}?".to_sym
357
- end
358
-
359
- begin
360
- predicate.to_sym
361
- rescue => e
362
- # create a nested exception here
363
- raise InvalidAttributeError, "cannot coerce field predicate to a symbol for #{field_name}: #{e}"
364
- end
365
- end,
366
- clearer: lambda do|clearer, field_name|
367
- if ! clearer
368
- return false
369
- elsif clearer.is_a? TrueClass
370
- return "clear_#{field_name}!".to_sym
371
- end
372
-
373
- begin
374
- clearer.to_sym
375
- rescue => e
376
- # create a nested exception here
377
- raise InvalidAttributeError, "cannot coerce field clearer to a symbol for #{field_name}: #{e}"
378
- end
379
- end,
380
- handles: lambda do |handles, field_name|
381
-
382
- unless handles.is_a? Hash
383
-
384
- array_of_handles = handles
385
-
386
- unless array_of_handles.is_a? Array
387
- array_of_handles = [ array_of_handles ]
388
- end
389
-
390
- handles = array_of_handles.map do |handle|
391
-
392
- if handle == BasicObject
393
-
394
- raise InvalidAttributeError, "ops, should not use BasicObject for handles in #{field_name}"
395
-
396
- elsif handle.is_a? Class
397
-
398
- handle = handle.public_instance_methods - handle.superclass.public_instance_methods
399
-
400
- elsif handle.is_a? Module
401
-
402
- handle = handle.public_instance_methods
403
-
404
- end
405
-
406
- handle
407
-
408
- end.flatten.reduce({}) do |hash, method_name|
409
- hash.merge({ method_name => method_name })
410
- end
411
- end
412
-
413
- handles.map do |key,value|
178
+ before = @before[method_name]
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)
217
+ end
218
+
219
+ def after(*methods_name, &block)
220
+ methods_name.each do |method_name|
221
+ begin
222
+ method = instance_method method_name
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
305
+ end
306
+ end
307
+
308
+ class InvalidAttributeError < TypeError
309
+
310
+ end
311
+
312
+ class Attribute
313
+ include MooseX::Types
314
+
315
+ attr_reader :attr_symbol, :is, :reader, :writter, :lazy, :builder, :methods, :override
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
+ }
330
+
331
+ REQUIRED = []
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
379
+
380
+ begin
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|
414
421
  if value.is_a? Hash
415
422
  raise "ops! Handle should accept only one map / currying" unless value.count == 1
416
423
 
@@ -418,233 +425,252 @@ module MooseX
418
425
 
419
426
  { key.to_sym => [original.to_sym, currying] }
420
427
  else
421
- { key.to_sym => value.to_sym }
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
422
568
  end
423
- end.reduce({}) do |hash,e|
424
- hash.merge(e)
425
- end
426
- end,
427
- reader: lambda do |reader, field_name|
428
- reader.to_sym
429
- end,
430
- writter: lambda do |writter, field_name|
431
- writter.to_sym
432
- end,
433
- builder: lambda do |builder, field_name|
434
- unless builder.is_a? Proc
435
- builder_method_name = builder.to_sym
436
- builder = lambda do |object|
437
- object.send(builder_method_name)
438
- end
439
- end
440
-
441
- builder
442
- end,
443
- init_arg: lambda do |init_arg, field_name|
444
- init_arg.to_sym
445
- end,
446
- trigger: lambda do |trigger, field_name|
447
- unless trigger.is_a? Proc
448
- trigger_method_name = trigger.to_sym
449
- trigger = lambda do |object, value|
450
- object.send(trigger_method_name,value)
451
- end
452
- end
453
-
454
- trigger
455
- end,
456
- coerce: lambda do |coerce, field_name|
457
- unless coerce.is_a? Proc
458
- coerce_method_name = coerce.to_sym
459
- coerce = lambda do |object|
460
- object.send(coerce_method_name)
461
- end
462
- end
463
-
464
- coerce
465
- end,
466
- };
467
-
468
- def initialize(a, o ,x)
469
- #o ||= {}
470
- # todo extract this to a framework, see issue #21 on facebook
471
- o = DEFAULTS.merge({
472
- reader: a,
473
- writter: a.to_s.concat("=").to_sym,
474
- builder: "build_#{a}".to_sym,
475
- init_arg: a,
476
- }).merge(o)
477
-
478
- REQUIRED.each { |field|
479
- unless o.has_key?(field)
480
- raise InvalidAttributeError, "field #{field} is required for Attribute #{a}"
481
- end
482
- }
483
- COERCE.each_pair do |field, coerce|
484
- if o.has_key? field
485
- o[field] = coerce.call(o[field], a)
486
- end
487
- end
488
- VALIDATE.each_pair do |field, validate|
489
- return if ! o.has_key? field
490
-
491
- validate.call(o[field], a)
492
- end
493
-
494
- if o[:is].eql? :ro
495
- o[:writter] = nil
496
- elsif o[:is].eql? :lazy
497
- o[:lazy] = true
498
- o[:writter] = nil
499
- end
500
-
501
- unless o[:lazy]
502
- o[:builder] = nil
503
- end
504
-
505
- @attr_symbol = a
506
- @is = o.delete(:is)
507
- @isa = o.delete(:isa)
508
- @default = o.delete(:default)
509
- @required = o.delete(:required)
510
- @predicate = o.delete(:predicate)
511
- @clearer = o.delete(:clearer)
512
- @handles = o.delete(:handles)
513
- @lazy = o.delete(:lazy)
514
- @reader = o.delete(:reader)
515
- @writter = o.delete(:writter)
516
- @builder = o.delete(:builder)
517
- @init_arg = o.delete(:init_arg)
518
- @trigger = o.delete(:trigger)
519
- @coerce = o.delete(:coerce)
520
- @methods = {}
521
-
522
- MooseX.warn "Unused attributes #{o} for attribute #{a} @ #{x} #{x.class}",caller() if ! o.empty?
523
-
524
- if @reader
525
- @methods[@reader] = generate_reader
526
- end
527
-
528
- if @writter
529
- @methods[@writter] = generate_writter
530
- end
531
- inst_variable_name = "@#{@attr_symbol}".to_sym
532
- if @predicate
533
- @methods[@predicate] = Proc.new do
534
- instance_variable_defined? inst_variable_name
535
- end
536
- end
537
-
538
- if @clearer
539
- @methods[@clearer] = Proc.new do
540
- if instance_variable_defined? inst_variable_name
541
- remove_instance_variable inst_variable_name
542
- end
543
- end
544
- end
545
-
546
- attr_symbol = @attr_symbol
547
- @handles.each_pair do | method, target_method |
548
- if target_method.is_a? Array
549
- original, currying = target_method
550
-
551
- @methods[method] = Proc.new do |*args, &proc|
552
-
553
- a1 = [ currying ]
554
-
555
- if currying.is_a?Proc
556
- a1 = currying.call()
557
- elsif currying.is_a? Array
558
- a1 = currying.map{|c| (c.is_a?(Proc)) ? c.call : c }
559
- end
560
-
561
- self.send(attr_symbol).send(original, *a1, *args, &proc)
562
- end
563
- else
564
- @methods[method] = Proc.new do |*args, &proc|
565
- self.send(attr_symbol).send(target_method, *args, &proc)
566
- end
567
- end
568
- end
569
- end
570
-
571
- def init(object, args)
572
- value = nil
573
- value_from_default = false
574
-
575
- if args.has_key? @init_arg
576
- value = args.delete(@init_arg)
577
- elsif @default
578
- value = @default.call
579
- value_from_default = true
580
- elsif @required
581
- raise InvalidAttributeError, "attr \"#{@attr_symbol}\" is required"
582
- else
583
- return
584
- end
585
-
586
- value = @coerce.call(value)
587
- begin
588
- @isa.call( value )
589
- rescue MooseX::Types::TypeCheckError => e
590
- raise MooseX::Types::TypeCheckError, "isa check for field #{attr_symbol}: #{e}"
591
- end
592
- unless value_from_default
593
- @trigger.call(object, value)
594
- end
595
- inst_variable_name = "@#{@attr_symbol}".to_sym
596
- object.instance_variable_set inst_variable_name, value
597
- end
598
-
599
- private
600
- def generate_reader
601
- inst_variable_name = "@#{@attr_symbol}".to_sym
602
-
603
- builder = @builder
604
- before_get = lambda {|object| }
605
-
606
- if @lazy
607
- type_check = @isa
608
- coerce = @coerce
609
- trigger = @trigger
610
- before_get = lambda do |object|
611
- return if object.instance_variable_defined? inst_variable_name
612
-
613
- value = builder.call(object)
614
- value = coerce.call(value)
615
- begin
616
- type_check.call( value )
617
- rescue MooseX::Types::TypeCheckError => e
618
- raise MooseX::Types::TypeCheckError, "isa check for #{inst_variable_name} from builder: #{e}"
619
- end
620
-
621
- trigger.call(object, value)
622
- object.instance_variable_set(inst_variable_name, value)
623
- end
624
- end
625
-
626
- Proc.new do
627
- before_get.call(self)
628
- instance_variable_get inst_variable_name
629
- end
630
- end
631
-
632
- def generate_writter
633
- writter_name = @writter
634
- inst_variable_name = "@#{@attr_symbol}".to_sym
635
- coerce = @coerce
636
- type_check = @isa
637
- trigger = @trigger
638
- Proc.new do |value|
639
- value = coerce.call(value)
640
- begin
641
- type_check.call( value )
642
- rescue MooseX::Types::TypeCheckError => e
643
- raise MooseX::Types::TypeCheckError, "isa check for #{writter_name}: #{e}"
644
- end
645
- trigger.call(self,value)
646
- instance_variable_set inst_variable_name, value
647
- end
648
- end
649
- end
650
- 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
600
+
601
+ if args.has_key? @init_arg
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
611
+
612
+ value = @coerce.call(value)
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
624
+
625
+ private
626
+ def generate_reader
627
+ inst_variable_name = "@#{@attr_symbol}".to_sym
628
+
629
+ builder = @builder
630
+ before_get = lambda {|object| }
631
+
632
+ if @lazy
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
638
+
639
+ value = builder.call(object)
640
+ value = coerce.call(value)
641
+ begin
642
+ type_check.call( value )
643
+ rescue MooseX::Types::TypeCheckError => e
644
+ raise MooseX::Types::TypeCheckError, "isa check for #{inst_variable_name} from builder: #{e}"
645
+ end
646
+
647
+ trigger.call(object, value)
648
+ object.instance_variable_set(inst_variable_name, value)
649
+ end
650
+ end
651
+
652
+ Proc.new do
653
+ before_get.call(self)
654
+ instance_variable_get inst_variable_name
655
+ end
656
+ 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
+ end
676
+ end