moosex 0.0.18 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +13 -8
- data/Gemfile.lock +1 -1
- data/README.md +180 -17
- data/lib/moosex.rb +4 -1
- data/lib/moosex/attribute.rb +94 -72
- data/lib/moosex/attribute/modifiers.rb +31 -32
- data/lib/moosex/event.rb +1 -1
- data/lib/moosex/meta.rb +12 -3
- data/lib/moosex/traits.rb +115 -0
- data/lib/moosex/types.rb +3 -3
- data/lib/moosex/version.rb +1 -1
- data/samples/events.rb +1 -1
- data/samples/point.rb +1 -1
- data/spec/baz_spec.rb +1 -1
- data/spec/coerce_spec.rb +3 -36
- data/spec/lazy_spec.rb +2 -2
- data/spec/meta_spec.rb +14 -0
- data/spec/modifiers_spec.rb +6 -6
- data/spec/parametric_role_spec.rb +2 -2
- data/spec/plugin_spec.rb +74 -0
- data/spec/point_spec.rb +1 -1
- data/spec/proxy_spec.rb +9 -9
- data/spec/traits_spec.rb +324 -0
- data/spec/trigger_spec.rb +3 -3
- data/spec/types_spec.rb +1 -1
- data/spec/weak_spec.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6ba383dda20e87558daa2dd8725a8807ff893c1
|
4
|
+
data.tar.gz: ea4c39f097690433c3961b744c375a9ecd94a6b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9f697fe80bafb7713e5375ac38a08a79de809fff7805cabb10a4e2c7e6f362d9513239b4463deb75cdd19f6d7a52dc3093af03d2567575cdc831ab07fadf12a
|
7
|
+
data.tar.gz: f722f87f1cf89779e44cffdbc89c550a4ffeb8c3b6120dc8f4392d46c9eda901567239f86a6503f913a3ef4bbc6284e717a95b748f2bdddc0ceac3b94a6fea7d
|
data/Changelog
CHANGED
@@ -1,35 +1,40 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.19 - 2014-02-14
|
2
|
+
- improvements in examples and internals
|
3
|
+
- add basic suport to traits #24
|
4
|
+
- basic support to plugin #23
|
5
|
+
|
6
|
+
0.0.18 - 2014-02-12
|
2
7
|
- better meta #58
|
3
8
|
- reduce internal complexity #51, #70
|
4
9
|
- meta now has __moosex__ prefix #55
|
5
10
|
|
6
|
-
0.0.17 -
|
11
|
+
0.0.17 - 2014-02-11
|
7
12
|
- has now support an override option #56
|
8
13
|
- has now support a doc option #57
|
9
14
|
- has now should not require :is => default is :rw #54
|
10
15
|
- add weak ref support #49
|
11
16
|
|
12
|
-
0.0.16 -
|
17
|
+
0.0.16 - 2014-02-07
|
13
18
|
- add currying to handles #46
|
14
19
|
- not after/before/around works well with methods who receive a block
|
15
20
|
|
16
|
-
0.0.15 -
|
21
|
+
0.0.15 - 2014-02-07
|
17
22
|
- after, before, around now accept more than one method name
|
18
23
|
- add basic event support #45
|
19
24
|
- add parametric roles #44
|
20
25
|
- add init method to enable warnings and exceptions #43
|
21
26
|
|
22
|
-
0.0.14 -
|
27
|
+
0.0.14 - 2014-02-05
|
23
28
|
- roles with around/before/after basic support #41
|
24
29
|
|
25
|
-
0.0.13 -
|
30
|
+
0.0.13 - 2014-02-05
|
26
31
|
- change around to receive a lambda #42
|
27
32
|
|
28
|
-
0.0.12 -
|
33
|
+
0.0.12 - 2014-02-05
|
29
34
|
- basic support override attributes #19
|
30
35
|
- basic support to roles #17
|
31
36
|
|
32
|
-
0.0.11 -
|
37
|
+
0.0.11 - 2014-02-04
|
33
38
|
- basic support to after/before/around #12
|
34
39
|
- improve exception #15
|
35
40
|
- improve type system via MooseX::Types #16
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -30,7 +30,7 @@ This rubygem is based on this modules:
|
|
30
30
|
- [MooseX::Role::Parameterized](http://search.cpan.org/~sartak/MooseX-Role-Parameterized-1.02/lib/MooseX/Role/Parameterized/Tutorial.pod)
|
31
31
|
|
32
32
|
See also:
|
33
|
-
|
33
|
+
- [Reindeer](https://github.com/broquaint/reindeer), another Moose port to Ruby (still on 0.0.1 version)
|
34
34
|
- [Joose](https://code.google.com/p/joose-js/), a javascript port of Moose.
|
35
35
|
- [Perl 6](http://en.wikipedia.org/wiki/Perl_6#Object-oriented_programming) Perl 6 OO programming style.
|
36
36
|
|
@@ -55,7 +55,7 @@ class Point
|
|
55
55
|
has y: {
|
56
56
|
is: :rw,
|
57
57
|
isa: Integer,
|
58
|
-
default:
|
58
|
+
default: -> { 0 }, # you should specify a lambda
|
59
59
|
}
|
60
60
|
|
61
61
|
def clear!
|
@@ -131,6 +131,9 @@ class Foo
|
|
131
131
|
isa: Integer,
|
132
132
|
default: 0
|
133
133
|
}
|
134
|
+
|
135
|
+
# you can declare inline too
|
136
|
+
has :another, is: :rw, isa: Integer, default: ->{ Object.new }
|
134
137
|
end
|
135
138
|
```
|
136
139
|
|
@@ -166,8 +169,8 @@ You can specify an optional type check for the attribute. Accepts a lambda, and
|
|
166
169
|
|
167
170
|
You can specify your own kind of type validation.
|
168
171
|
```ruby
|
169
|
-
isa:
|
170
|
-
unless
|
172
|
+
isa: ->(value) do
|
173
|
+
unless value.respond_to? :to_sym
|
171
174
|
raise "bar should respond to to_sym method!"
|
172
175
|
end
|
173
176
|
end,
|
@@ -184,7 +187,7 @@ You can specify an optional default value to one attribute. If we don't specify
|
|
184
187
|
```
|
185
188
|
or
|
186
189
|
```ruby
|
187
|
-
default:
|
190
|
+
default: -> { MyObject.new },
|
188
191
|
```
|
189
192
|
|
190
193
|
### required => true|false
|
@@ -204,7 +207,7 @@ Optional.
|
|
204
207
|
You can try to coerce the attribute value by a lambda/method before the type check phase. For example you can do
|
205
208
|
|
206
209
|
```ruby
|
207
|
-
coerce:
|
210
|
+
coerce: ->(value) { value.to_i },
|
208
211
|
```
|
209
212
|
|
210
213
|
or just
|
@@ -239,6 +242,8 @@ If you need rename the method, you can specify a Hash:
|
|
239
242
|
},
|
240
243
|
```
|
241
244
|
|
245
|
+
handles is similar to [Forwardable](http://ruby-doc.org/stdlib-2.1.0/libdoc/forwardable/rdoc/Forwardable.html) module, the difference is the currying support and it is integrate with the has method/endpoint. If you want to use Forwardable, please use the reader method name instead the attribute name with @.
|
246
|
+
|
242
247
|
Optional.
|
243
248
|
#### Currying
|
244
249
|
|
@@ -264,7 +269,7 @@ are equivalent. You can curry as many arguments as you can.
|
|
264
269
|
```ruby
|
265
270
|
handles: {
|
266
271
|
my_method_2: {
|
267
|
-
method2: [1,
|
272
|
+
method2: [1, ->{ 2 } ]
|
268
273
|
}
|
269
274
|
},
|
270
275
|
```
|
@@ -303,8 +308,8 @@ are equivalent.
|
|
303
308
|
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.
|
304
309
|
|
305
310
|
```ruby
|
306
|
-
trigger:
|
307
|
-
|
311
|
+
trigger: ->(this, new_value) do
|
312
|
+
this.logger.log "change the attribute value to #{new_value}"
|
308
313
|
end
|
309
314
|
```
|
310
315
|
or
|
@@ -445,10 +450,13 @@ class Foo
|
|
445
450
|
has x: {
|
446
451
|
is: :rw,
|
447
452
|
lazy: :true,
|
448
|
-
builder:
|
453
|
+
builder: ->(this) { Some::Class.new } # you can ignore foo, or use it!
|
449
454
|
}
|
450
455
|
end
|
451
456
|
```
|
457
|
+
|
458
|
+
The difference between builder and default is: default is for initialization if you omit the value in the constructor and you can't access the object (it is not "created" yet), builder is for lazy attributes, when you call the reader method for the first time ( or after clear it ) we will initialize the attribute and you have access to the object (in this example using the parameter 'this' in the lambda or using a normal method).
|
459
|
+
|
452
460
|
Optional.
|
453
461
|
|
454
462
|
### weak => true|false
|
@@ -472,16 +480,171 @@ You should verify with `weakref_alive?` method to avoid exceptions.
|
|
472
480
|
|
473
481
|
Optional.
|
474
482
|
|
475
|
-
|
483
|
+
### doc => String
|
476
484
|
|
477
485
|
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
486
|
|
479
487
|
Optional.
|
480
488
|
|
481
|
-
|
489
|
+
### override => true|false
|
482
490
|
|
483
491
|
If you need override one attribute, you should use `override: true`, or MooseX will raise one exception.
|
484
492
|
|
493
|
+
### traits => Trait|[Array of Traits]
|
494
|
+
|
495
|
+
The objective of use traits is extends the original attribute, using delegators. We support few traits at this moment and, **Important**, if you set a list of Traits we will apply each trait in sequence. Have a Suggestion? Open an Issue on Github!
|
496
|
+
|
497
|
+
#### Trait Counter
|
498
|
+
|
499
|
+
```ruby
|
500
|
+
class MyHomePage
|
501
|
+
include MooseX::Types
|
502
|
+
|
503
|
+
has :counter, {
|
504
|
+
is: :ro,
|
505
|
+
isa: Integer,
|
506
|
+
default: 0,
|
507
|
+
traits: MooseX::Traits::Counter,
|
508
|
+
handles: {
|
509
|
+
increase_counter: :inc,
|
510
|
+
decrease_counter: :dec,
|
511
|
+
reset_counter_to_zero!: :reset,
|
512
|
+
}
|
513
|
+
}
|
514
|
+
```
|
515
|
+
|
516
|
+
In this example, the class MyHomePage has one attribute counter, Integer, and you can increase and decrease the counter value (or reset to zero) using `increase_counter`, `decrease_counter` and `reset_counter_to_zero!` methods. Without the trait `Counter` you should create this three methods and set the visibility to `:rwp` to be able to access the value, do some math and save. In this example, `MooseX::Traits::Counter` is a SimpleDelegator who act as a wrapper to the original value and act as a proxy for all methods except `inc`, `dec` and `reset`.
|
517
|
+
|
518
|
+
We apply the traits list on default values, constructor and writter, after type check and coerce, and it is useful to extend the behavior of the attribute. In this case, 0 is a Immutable object, we can't increase the value using some method (different than String, when you use methods like `capitalize!`) but we can use a Delegator to act as a proxy and increase/decrease the value.
|
519
|
+
|
520
|
+
#### Trait Bool
|
521
|
+
|
522
|
+
Another example, using mutable boolean values:
|
523
|
+
|
524
|
+
```ruby
|
525
|
+
has bit: {
|
526
|
+
is: :ro,
|
527
|
+
default: true,
|
528
|
+
traits: MooseX::Traits::Bool,
|
529
|
+
handles: [ :toggle!, :value ],
|
530
|
+
}
|
531
|
+
```
|
532
|
+
|
533
|
+
In this case, we have a bit, and this bit is turn on (true). It is read-only so you can't set the bit. But using `toggle!` we can turn true => false and false => true. To access the original boolean value we should use !! ( to coerce to a true|false value ) or we can access the `value` method. It is mandatory if you want to use in if/unless statements. Of course you can change the name of each method.
|
534
|
+
|
535
|
+
#### Trait Pair
|
536
|
+
|
537
|
+
Sometimes we need store an array of fixed size and each element has an identity. For example, we should store arrays as tuples, or pairs. Of course we can create an object to be more clear, but we have this option and, sometimes, it is useful. Now, consider this example:
|
538
|
+
|
539
|
+
```ruby
|
540
|
+
has array_with_surname_name: {
|
541
|
+
is: :private,
|
542
|
+
isa: Array, # or isTuple(String, String) if you include MooseX::Types
|
543
|
+
traits: [ MooseX::Traits::Pair ],
|
544
|
+
handles: {
|
545
|
+
:surname => :first,
|
546
|
+
:surname= => :first=,
|
547
|
+
:name => :second,
|
548
|
+
:name= => :second=,
|
549
|
+
:surname_and_name => { join: ->{","} }
|
550
|
+
},
|
551
|
+
required: true,
|
552
|
+
}
|
553
|
+
```
|
554
|
+
|
555
|
+
We store surname and name as a tuple of Strings. Instead access `array_with_surname_name[0]` or `array_with_surname_name[1]`, we can apply the Trait `Pair`, and access (or save!) each component of this pair, but you can't change the pair itself. Look the example of currying in `surname_and_name`, calling join with "," as an argument.
|
556
|
+
|
557
|
+
#### Trait RescueToNil
|
558
|
+
|
559
|
+
The objective of `MooseX::Traits::RescueToNil` is avoid `NoMethodError` if, for example, you set nil as value. For example:
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
has important: {
|
563
|
+
is: :rw,
|
564
|
+
default: 0,
|
565
|
+
traits: MooseX::Traits::RescueToNil,
|
566
|
+
handles: {
|
567
|
+
plus: :+,
|
568
|
+
minus: :-,
|
569
|
+
}
|
570
|
+
}
|
571
|
+
```
|
572
|
+
Imagine you can accept nil as a valid value. In this case, you can't use `+` or `-`, right? It will raise a NoMethodError. Well... you can avoid this with RescueToNil trait. Using this, we will return `nil` for each operation in case of NoMethodError, and raise other kinds of exceptions.
|
573
|
+
|
574
|
+
#### Trait RescueToZero
|
575
|
+
|
576
|
+
Similar to RescueToNil, but return 0 in case of `NoMethodError`.
|
577
|
+
|
578
|
+
#### Trait RescueToEmptyString
|
579
|
+
|
580
|
+
Similar to RescueToNil, but return empty string "" in case of `NoMethodError`.
|
581
|
+
|
582
|
+
##### Create your own trait
|
583
|
+
|
584
|
+
You should create a Class with a constructor who will receive a reference to the value. For example, the trait Counter is using SimpleDelegator:
|
585
|
+
|
586
|
+
```ruby
|
587
|
+
require 'delegate' # to use SimpleDelegator
|
588
|
+
|
589
|
+
module MooseX
|
590
|
+
module Traits
|
591
|
+
class Counter < SimpleDelegator
|
592
|
+
def initialize(value)
|
593
|
+
@value = value
|
594
|
+
super(@value)
|
595
|
+
end
|
596
|
+
|
597
|
+
def inc(by=1)
|
598
|
+
@value += by
|
599
|
+
__setobj__(@value)
|
600
|
+
@value
|
601
|
+
end
|
602
|
+
...
|
603
|
+
```
|
604
|
+
You can create or extend your own Traits too. It is easy.
|
605
|
+
|
606
|
+
##### Composable Traits
|
607
|
+
|
608
|
+
It is easy compose traits, for example:
|
609
|
+
|
610
|
+
```ruby
|
611
|
+
class ComplexExample
|
612
|
+
include MooseX
|
613
|
+
include MooseX::Types
|
614
|
+
|
615
|
+
has surname_name: {
|
616
|
+
is: :rw,
|
617
|
+
isa: isMaybe(isTuple(String, String)),
|
618
|
+
default: nil,
|
619
|
+
traits: [ MooseX::Traits::RescueToEmptyString, MooseX::Traits::Pair ],
|
620
|
+
handles: {
|
621
|
+
surname: :first,
|
622
|
+
name: :second,
|
623
|
+
:surname= => :first=,
|
624
|
+
:name= => :second=,
|
625
|
+
surname_and_name: { join: ->{", "} }
|
626
|
+
}
|
627
|
+
}
|
628
|
+
```
|
629
|
+
First, we apply `RescueToEmptyString`, then `Pair`. In this case, if you set `nil`, name and surname will act as empty string values. For example:
|
630
|
+
|
631
|
+
```ruby
|
632
|
+
ce = ComplexExample.new(surname_name: nil)
|
633
|
+
ce.name # => ""
|
634
|
+
ce.surname_and_name # => ", "
|
635
|
+
|
636
|
+
ce.name= "Isaac"
|
637
|
+
ce.surname_and_name # => ", Isaac"
|
638
|
+
ce.surname= "Asimov"
|
639
|
+
ce.surname_and_name # => "Asimov, Isaac"
|
640
|
+
|
641
|
+
ce.surname_name= nil
|
642
|
+
ce.name # => ""
|
643
|
+
ce.surname_and_name # => ", "
|
644
|
+
```
|
645
|
+
|
646
|
+
In this example it is safe set nil to `surname_name`. when we try to create the Pair, the [0] and [1] calls will return "", and we have one pair ["", ""]. This is why we add RescueToEmptyString first. If we use Pair as first trait, Pair expects an array, not a nil. *The Order is Important*.
|
647
|
+
|
485
648
|
## Hooks: after/before/around
|
486
649
|
|
487
650
|
Another great feature imported from Moose are the hooks after/before/around one method. You can run an arbitrary code, for example:
|
@@ -534,10 +697,10 @@ class Point
|
|
534
697
|
# do something
|
535
698
|
end
|
536
699
|
|
537
|
-
before :my_method do |
|
700
|
+
before :my_method do |this, x|
|
538
701
|
puts "#{Time.now} before my_method(#{x})"
|
539
702
|
end
|
540
|
-
after :my_method do |
|
703
|
+
after :my_method do |this, x|
|
541
704
|
puts "#{Time.now} after my_method(#{x})"
|
542
705
|
end
|
543
706
|
end
|
@@ -548,9 +711,9 @@ end
|
|
548
711
|
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
|
549
712
|
|
550
713
|
```ruby
|
551
|
-
around(:sum) do |method_lambda,
|
714
|
+
around(:sum) do |method_lambda, this, a,b,c|
|
552
715
|
c = 0
|
553
|
-
result = method_lambda.call(
|
716
|
+
result = method_lambda.call(this,a,b,c)
|
554
717
|
result + 1
|
555
718
|
end
|
556
719
|
```
|
@@ -972,7 +1135,7 @@ class EventProcessor
|
|
972
1135
|
has event_handler: {
|
973
1136
|
is: :ro,
|
974
1137
|
isa: EventHandler,
|
975
|
-
default:
|
1138
|
+
default: -> { EventHandler.new }, # EventProcessor HAS ONE EventHandler
|
976
1139
|
handles: { # Now, lets start to delegate and currying:
|
977
1140
|
ping: { emit: :pinged }, # ping() is the same of event_handler.emit(:pinged)
|
978
1141
|
pong: { emit: :ponged }, # pong(x) is the same of event_handler.emit(:pinged,x)
|
data/lib/moosex.rb
CHANGED
@@ -11,6 +11,7 @@ require "moosex/exceptions"
|
|
11
11
|
require "moosex/meta"
|
12
12
|
require "moosex/core"
|
13
13
|
require "moosex/attribute"
|
14
|
+
require "moosex/traits"
|
14
15
|
require "weakref"
|
15
16
|
|
16
17
|
module MooseX
|
@@ -65,7 +66,9 @@ module MooseX
|
|
65
66
|
|
66
67
|
if @@ALIAS
|
67
68
|
class_or_module.class_eval do
|
68
|
-
class_or_module.define_singleton_method(@@ALIAS)
|
69
|
+
class_or_module.define_singleton_method(@@ALIAS) do
|
70
|
+
self.__moosex__meta
|
71
|
+
end
|
69
72
|
end
|
70
73
|
@@ALIAS = false
|
71
74
|
end
|
data/lib/moosex/attribute.rb
CHANGED
@@ -2,98 +2,115 @@ require 'moosex/types'
|
|
2
2
|
require 'moosex/attribute/modifiers'
|
3
3
|
|
4
4
|
module MooseX
|
5
|
-
|
5
|
+
class Attribute
|
6
6
|
include MooseX::Types
|
7
7
|
|
8
|
-
attr_reader :attr_symbol, :
|
9
|
-
|
10
|
-
|
8
|
+
attr_reader :attr_symbol, :methods, :attribute_map
|
9
|
+
|
10
|
+
def is ; @attribute_map[:is] ; end
|
11
|
+
def writter ; @attribute_map[:writter] ; end
|
12
|
+
def reader ; @attribute_map[:reader] ; end
|
13
|
+
def override ; @attribute_map[:override] ; end
|
14
|
+
def doc ; @attribute_map[:doc] ; end
|
15
|
+
def default ; @attribute_map[:default] ; end
|
16
|
+
|
17
|
+
@@LIST_OF_PARAMETERS = [
|
18
|
+
[:is, MooseX::AttributeModifiers::Is.new ],
|
19
|
+
[:isa, MooseX::AttributeModifiers::Isa.new ],
|
20
|
+
[:default, MooseX::AttributeModifiers::Default.new ],
|
21
|
+
[:required, MooseX::AttributeModifiers::Required.new ],
|
22
|
+
[:predicate, MooseX::AttributeModifiers::Predicate.new],
|
23
|
+
[:clearer, MooseX::AttributeModifiers::Clearer.new ],
|
24
|
+
[:traits, MooseX::AttributeModifiers::Traits.new ],
|
25
|
+
[:handles, MooseX::AttributeModifiers::Handles.new ],
|
26
|
+
[:lazy, MooseX::AttributeModifiers::Lazy.new ],
|
27
|
+
[:reader, MooseX::AttributeModifiers::Reader.new ],
|
28
|
+
[:writter, MooseX::AttributeModifiers::Writter.new ],
|
29
|
+
[:builder, MooseX::AttributeModifiers::Builder.new ],
|
30
|
+
[:init_arg, MooseX::AttributeModifiers::Init_arg.new ],
|
31
|
+
[:trigger, MooseX::AttributeModifiers::Trigger.new ],
|
32
|
+
[:coerce, MooseX::AttributeModifiers::Coerce.new ],
|
33
|
+
[:weak, MooseX::AttributeModifiers::Weak.new ],
|
34
|
+
[:doc, MooseX::AttributeModifiers::Doc.new ],
|
35
|
+
[:override, MooseX::AttributeModifiers::Override.new ],
|
36
|
+
]
|
11
37
|
|
12
38
|
def initialize(attr_symbol, options ,klass)
|
13
39
|
@attr_symbol = attr_symbol
|
14
|
-
|
15
|
-
|
40
|
+
@attribute_map = {}
|
41
|
+
|
42
|
+
init_internal_modifiers(options.clone, klass.__moosex__meta.plugins, klass)
|
16
43
|
|
17
44
|
generate_all_methods
|
18
45
|
end
|
19
46
|
|
20
|
-
def init_internal_modifiers(options, klass)
|
21
|
-
|
22
|
-
|
47
|
+
def init_internal_modifiers(options, plugins, klass)
|
48
|
+
@@LIST_OF_PARAMETERS.each do |tuple|
|
49
|
+
parameter, obj = tuple
|
50
|
+
@attribute_map[parameter] = obj.process(options, @attr_symbol)
|
51
|
+
end
|
23
52
|
|
24
|
-
|
53
|
+
plugins.sort.uniq.each do |key|
|
54
|
+
begin
|
55
|
+
klass = MooseX::AttributeModifiers::ThirdParty.const_get(key.to_s.capitalize.to_sym)
|
56
|
+
@attribute_map[key.to_sym] = klass.new.process(options, @attr_symbol)
|
57
|
+
rescue NameError => e
|
58
|
+
next
|
59
|
+
rescue => e
|
60
|
+
raise "Unexpected Error in #{key} #{@attr_symbol}: #{e}"
|
61
|
+
end
|
62
|
+
end
|
25
63
|
|
26
64
|
MooseX.warn "Unused attributes #{options} for attribute #{@attr_symbol} @ #{klass} #{klass.class}",caller() if ! options.empty?
|
27
65
|
end
|
28
66
|
|
29
|
-
def init_internal_modifiers_1(options)
|
30
|
-
@is = Is.new.process(options, @attr_symbol)
|
31
|
-
@isa = Isa.new.process(options, @attr_symbol)
|
32
|
-
@default = Default.new.process(options, @attr_symbol)
|
33
|
-
@required = Required.new.process(options, @attr_symbol)
|
34
|
-
@predicate = Predicate.new.process(options, @attr_symbol)
|
35
|
-
@clearer = Clearer.new.process(options, @attr_symbol)
|
36
|
-
@handles = Handles.new.process(options, @attr_symbol)
|
37
|
-
@lazy = Lazy.new.process(options, @attr_symbol)
|
38
|
-
@reader = Reader.new.process(options, @attr_symbol)
|
39
|
-
end
|
40
|
-
|
41
|
-
def init_internal_modifiers_2(options)
|
42
|
-
@writter = Writter.new.process(options, @attr_symbol)
|
43
|
-
@builder = Builder.new.process(options, @attr_symbol) # TODO: warn if has builder and it is not lazy
|
44
|
-
@init_arg = InitArg.new.process(options, @attr_symbol)
|
45
|
-
@trigger = Trigger.new.process(options, @attr_symbol)
|
46
|
-
@coerce = Coerce.new.process(options, @attr_symbol)
|
47
|
-
@weak = Weak.new.process(options, @attr_symbol)
|
48
|
-
@doc = Doc.new.process(options, @attr_symbol)
|
49
|
-
@override = Override.new.process(options, @attr_symbol)
|
50
|
-
end
|
51
|
-
|
52
67
|
def generate_all_methods
|
53
68
|
@methods = {}
|
54
69
|
|
55
|
-
if @reader
|
56
|
-
@methods[@reader] = generate_reader
|
70
|
+
if @attribute_map[:reader]
|
71
|
+
@methods[@attribute_map[:reader]] = generate_reader
|
57
72
|
end
|
58
73
|
|
59
|
-
if @writter
|
60
|
-
@methods[@writter] = generate_writter
|
74
|
+
if @attribute_map[:writter]
|
75
|
+
@methods[@attribute_map[:writter]] = generate_writter
|
61
76
|
end
|
62
77
|
|
63
78
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
64
|
-
if @predicate
|
65
|
-
@methods[@predicate] = Proc.new do
|
79
|
+
if @attribute_map[:predicate]
|
80
|
+
@methods[@attribute_map[:predicate]] = Proc.new do
|
66
81
|
instance_variable_defined? inst_variable_name
|
67
82
|
end
|
68
83
|
end
|
69
84
|
|
70
|
-
|
71
|
-
|
72
|
-
@methods[@clearer] = Proc.new do
|
85
|
+
if @attribute_map[:clearer]
|
86
|
+
@methods[@attribute_map[:clearer]] = Proc.new do
|
73
87
|
if instance_variable_defined? inst_variable_name
|
74
88
|
remove_instance_variable inst_variable_name
|
75
89
|
end
|
76
90
|
end
|
77
91
|
end
|
78
|
-
|
79
|
-
generate_handles @attr_symbol
|
92
|
+
|
93
|
+
generate_handles @attr_symbol
|
80
94
|
end
|
81
95
|
|
82
96
|
def generate_handles(attr_symbol)
|
83
|
-
|
97
|
+
|
98
|
+
delegator = ->(this) { this.__send__(attr_symbol) }
|
99
|
+
|
100
|
+
@attribute_map[:handles].each_pair do | method, target_method |
|
84
101
|
if target_method.is_a? Array
|
85
|
-
|
102
|
+
original_method, currying = target_method
|
86
103
|
|
87
|
-
@methods[method] = generate_handles_with_currying(
|
104
|
+
@methods[method] = generate_handles_with_currying(delegator, original_method, currying)
|
88
105
|
else
|
89
106
|
@methods[method] = Proc.new do |*args, &proc|
|
90
|
-
|
107
|
+
delegator.call(self).__send__(target_method, *args, &proc)
|
91
108
|
end
|
92
109
|
end
|
93
110
|
end
|
94
111
|
end
|
95
112
|
|
96
|
-
def generate_handles_with_currying(
|
113
|
+
def generate_handles_with_currying(delegator, original_method, currying)
|
97
114
|
Proc.new do |*args, &proc|
|
98
115
|
|
99
116
|
a1 = [ currying ]
|
@@ -104,7 +121,7 @@ module MooseX
|
|
104
121
|
a1 = currying.map{|c| (c.is_a?(Proc)) ? c.call : c }
|
105
122
|
end
|
106
123
|
|
107
|
-
|
124
|
+
delegator.call(self).__send__(original_method, *a1, *args, &proc)
|
108
125
|
end
|
109
126
|
end
|
110
127
|
|
@@ -112,26 +129,27 @@ module MooseX
|
|
112
129
|
value = nil
|
113
130
|
value_from_default = false
|
114
131
|
|
115
|
-
if args.has_key? @init_arg
|
116
|
-
value = args.delete(@init_arg)
|
117
|
-
elsif @default
|
118
|
-
value = @default.call
|
132
|
+
if args.has_key? @attribute_map[:init_arg]
|
133
|
+
value = args.delete(@attribute_map[:init_arg])
|
134
|
+
elsif @attribute_map[:default]
|
135
|
+
value = @attribute_map[:default].call
|
119
136
|
value_from_default = true
|
120
|
-
elsif @required
|
137
|
+
elsif @attribute_map[:required]
|
121
138
|
raise InvalidAttributeError, "attr \"#{@attr_symbol}\" is required"
|
122
139
|
else
|
123
140
|
return
|
124
141
|
end
|
125
142
|
|
126
|
-
value = @coerce.call(value)
|
143
|
+
value = @attribute_map[:coerce].call(value)
|
127
144
|
begin
|
128
|
-
@isa.call( value )
|
145
|
+
@attribute_map[:isa].call( value )
|
129
146
|
rescue MooseX::Types::TypeCheckError => e
|
130
147
|
raise MooseX::Types::TypeCheckError, "isa check for field #{attr_symbol}: #{e}"
|
131
148
|
end
|
132
149
|
unless value_from_default
|
133
|
-
@trigger.call(object, value)
|
150
|
+
@attribute_map[:trigger].call(object, value)
|
134
151
|
end
|
152
|
+
value = @attribute_map[:traits].call(value)
|
135
153
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
136
154
|
object.instance_variable_set inst_variable_name, value
|
137
155
|
end
|
@@ -140,14 +158,15 @@ module MooseX
|
|
140
158
|
def generate_reader
|
141
159
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
142
160
|
|
143
|
-
builder = @builder
|
144
|
-
before_get =
|
145
|
-
|
146
|
-
if @lazy
|
147
|
-
type_check = protect_isa(@isa, "isa check for #{inst_variable_name} from builder")
|
148
|
-
coerce = @coerce
|
149
|
-
trigger = @trigger
|
150
|
-
|
161
|
+
builder = @attribute_map[:builder]
|
162
|
+
before_get = ->(object) { }
|
163
|
+
|
164
|
+
if @attribute_map[:lazy]
|
165
|
+
type_check = protect_isa(@attribute_map[:isa], "isa check for #{inst_variable_name} from builder")
|
166
|
+
coerce = @attribute_map[:coerce]
|
167
|
+
trigger = @attribute_map[:trigger]
|
168
|
+
traits = @attribute_map[:traits]
|
169
|
+
before_get = ->(object) do
|
151
170
|
return if object.instance_variable_defined? inst_variable_name
|
152
171
|
|
153
172
|
value = builder.call(object)
|
@@ -155,18 +174,19 @@ module MooseX
|
|
155
174
|
type_check.call( value )
|
156
175
|
|
157
176
|
trigger.call(object, value)
|
177
|
+
value = traits.call(value)
|
158
178
|
object.instance_variable_set(inst_variable_name, value)
|
159
179
|
end
|
160
180
|
end
|
161
181
|
|
162
|
-
|
182
|
+
-> do
|
163
183
|
before_get.call(self)
|
164
184
|
instance_variable_get inst_variable_name
|
165
185
|
end
|
166
186
|
end
|
167
187
|
|
168
188
|
def protect_isa(type_check, message)
|
169
|
-
|
189
|
+
->(value) do
|
170
190
|
begin
|
171
191
|
type_check.call( value )
|
172
192
|
rescue MooseX::Types::TypeCheckError => e
|
@@ -176,15 +196,17 @@ module MooseX
|
|
176
196
|
end
|
177
197
|
|
178
198
|
def generate_writter
|
179
|
-
writter_name = @writter
|
199
|
+
writter_name = @attribute_map[:writter]
|
180
200
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
181
|
-
coerce
|
182
|
-
type_check
|
183
|
-
trigger
|
201
|
+
coerce = @attribute_map[:coerce]
|
202
|
+
type_check = protect_isa(@attribute_map[:isa], "isa check for #{writter_name}")
|
203
|
+
trigger = @attribute_map[:trigger]
|
204
|
+
traits = @attribute_map[:traits]
|
184
205
|
Proc.new do |value|
|
185
206
|
value = coerce.call(value)
|
186
207
|
type_check.call( value )
|
187
208
|
trigger.call(self,value)
|
209
|
+
value = traits.call(value)
|
188
210
|
instance_variable_set inst_variable_name, value
|
189
211
|
end
|
190
212
|
end
|