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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae6315aba6d50e3443b6e0b184a3d51a4ef676d2
4
- data.tar.gz: e80dc006c7d9ba38c22130007416ca5f29650fec
3
+ metadata.gz: f6ba383dda20e87558daa2dd8725a8807ff893c1
4
+ data.tar.gz: ea4c39f097690433c3961b744c375a9ecd94a6b0
5
5
  SHA512:
6
- metadata.gz: 660d4f1f3a542d7e21f66d7d8b335bfe6acb1cde810c65cf8ee37aa82227fe74185b9614a726b0e97f7d5db415182560a710cb072264cae6c7b7dce4b60b9d4c
7
- data.tar.gz: 0f8071f6fcec75b2ef8d95e0dc2e02388abb45b7fd82aad2aec5b8eeb7be5b759ac02a2862d8f939fe7fe51c81afc326c0843772422bbc0ba577cf9d93fb68b6
6
+ metadata.gz: d9f697fe80bafb7713e5375ac38a08a79de809fff7805cabb10a4e2c7e6f362d9513239b4463deb75cdd19f6d7a52dc3093af03d2567575cdc831ab07fadf12a
7
+ data.tar.gz: f722f87f1cf89779e44cffdbc89c550a4ffeb8c3b6120dc8f4392d46c9eda901567239f86a6503f913a3ef4bbc6284e717a95b748f2bdddc0ceac3b94a6fea7d
data/Changelog CHANGED
@@ -1,35 +1,40 @@
1
- 0.0.18 - 2013-02-12
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 - 2013-02-11
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 - 2013-02-07
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 - 2013-02-07
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 - 2013-02-05
27
+ 0.0.14 - 2014-02-05
23
28
  - roles with around/before/after basic support #41
24
29
 
25
- 0.0.13 - 2013-02-05
30
+ 0.0.13 - 2014-02-05
26
31
  - change around to receive a lambda #42
27
32
 
28
- 0.0.12 - 2013-02-05
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 - 2013-02-04
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- moosex (0.0.18)
4
+ moosex (0.0.19)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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: lambda { 0 }, # you should specify a lambda
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: lambda do |new_value|
170
- unless new_value.respond_to? :to_sym
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: lambda{ MyObject.new },
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: lambda{ |new_value| new_value.to_i },
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, lambda{ 2 } ]
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: lambda do |object, new_value|
307
- object.logger.log "change the attribute value to #{new_value}"
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: lambda{ |foo| Some::Class.new } # you can ignore foo, or use it!
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
- ## doc => String
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
- ## override => true|false
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 |object, x|
700
+ before :my_method do |this, x|
538
701
  puts "#{Time.now} before my_method(#{x})"
539
702
  end
540
- after :my_method do |object, x|
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, object, a,b,c|
714
+ around(:sum) do |method_lambda, this, a,b,c|
552
715
  c = 0
553
- result = method_lambda.call(object,a,b,c)
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: lambda{ EventHandler.new }, # EventProcessor HAS ONE EventHandler
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)
@@ -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) { meta }
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
@@ -2,98 +2,115 @@ require 'moosex/types'
2
2
  require 'moosex/attribute/modifiers'
3
3
 
4
4
  module MooseX
5
- class Attribute
5
+ class Attribute
6
6
  include MooseX::Types
7
7
 
8
- attr_reader :attr_symbol, :is, :isa, :default, :required, :predicate,
9
- :clearer, :handles, :lazy, :reader, :writter, :builder, :init_arg, :trigger,
10
- :coerce, :weak, :doc, :methods, :override
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
- init_internal_modifiers(options.clone, klass)
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
- init_internal_modifiers_1(options)
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
- init_internal_modifiers_2(options)
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
- inst_variable_name = "@#{@attr_symbol}".to_sym
71
- if @clearer
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
- @handles.each_pair do | method, target_method |
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
- original, currying = target_method
102
+ original_method, currying = target_method
86
103
 
87
- @methods[method] = generate_handles_with_currying(attr_symbol,original, 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
- self.send(attr_symbol).send(target_method, *args, &proc)
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(attr_symbol,original, 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
- self.send(attr_symbol).send(original, *a1, *args, &proc)
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 = lambda {|object| }
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
- before_get = lambda do |object|
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
- Proc.new do
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
- lambda do |value|
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 = @coerce
182
- type_check = protect_isa(@isa, "isa check for #{writter_name}")
183
- trigger = @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