moosex 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +139 -68
- data/lib/moosex.rb +10 -0
- data/lib/moosex/attribute.rb +47 -49
- data/lib/moosex/attribute/modifiers.rb +4 -2
- data/lib/moosex/core.rb +4 -2
- data/lib/moosex/meta.rb +1 -1
- data/lib/moosex/plugins.rb +63 -0
- data/lib/moosex/traits.rb +16 -38
- data/lib/moosex/version.rb +1 -1
- data/moosex.gemspec +1 -1
- data/samples/plugin.rb +54 -0
- data/spec/modifiers_spec.rb +3 -3
- data/spec/plugin_spec.rb +133 -31
- data/spec/traits_spec.rb +43 -138
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81a1d69a0ea74ca118361a16b549201215fd255e
|
4
|
+
data.tar.gz: 562b285c934e2e1a798c89b4ff02afd9af4c3754
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a68c128bb6fffdcb6c50eb174d486e963919e031c111ffe537f89a517fda547e62180abfcf96a096c310790b48fe5759e2cb2846745ef7267f94f30bf9fd5fbf
|
7
|
+
data.tar.gz: 31fa71baa921d302d7603197e6dca60c0f423064e9e2eccfcab0d47f3bf463a88a2343e28979c5ea89021a40d6815cda02b4218e31c7f317ed375b62006f45ad
|
data/Changelog
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,8 @@ A postmodern object DSL for Ruby [![Build Status](https://travis-ci.org/peczenyj
|
|
5
5
|
|
6
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
7
|
|
8
|
+
Want to help? Install the gem `moosex` and start to use. If you find some issue, please assign to me :)
|
9
|
+
|
8
10
|
Of course, there is few similar projects in ruby like
|
9
11
|
|
10
12
|
- [Virtus](https://github.com/solnic/virtus)
|
@@ -12,9 +14,10 @@ Of course, there is few similar projects in ruby like
|
|
12
14
|
|
13
15
|
But the objetive of MooseX is different: this is a toolbox to create Classes based on DSL, with unique features like
|
14
16
|
|
15
|
-
- method delegation ( see 'handles')
|
17
|
+
- method delegation and currying ( see 'handles')
|
16
18
|
- lazy attributes
|
17
|
-
- roles
|
19
|
+
- roles / abstract classes / interfaces
|
20
|
+
- traits / monads
|
18
21
|
- parameterized roles
|
19
22
|
- composable type check
|
20
23
|
- events
|
@@ -32,7 +35,9 @@ This rubygem is based on this modules:
|
|
32
35
|
See also:
|
33
36
|
- [Reindeer](https://github.com/broquaint/reindeer), another Moose port to Ruby (still on 0.0.1 version)
|
34
37
|
- [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.
|
38
|
+
- [Perl 6](http://en.wikipedia.org/wiki/Perl_6#Object-oriented_programming), Perl 6 OO programming style.
|
39
|
+
- [Elk](http://frasertweedale.github.io/elk/), Elk is an object system for Python inspired by Moose.
|
40
|
+
|
36
41
|
|
37
42
|
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' :/
|
38
43
|
|
@@ -490,9 +495,9 @@ Optional.
|
|
490
495
|
|
491
496
|
If you need override one attribute, you should use `override: true`, or MooseX will raise one exception.
|
492
497
|
|
493
|
-
### traits => Trait|[Array of Traits]
|
498
|
+
### traits => Trait | [Array of Traits]
|
494
499
|
|
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!
|
500
|
+
The objective of use traits is extends the original attribute, using delegators (think in Monads). 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
501
|
|
497
502
|
#### Trait Counter
|
498
503
|
|
@@ -554,30 +559,37 @@ Sometimes we need store an array of fixed size and each element has an identity.
|
|
554
559
|
|
555
560
|
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
561
|
|
557
|
-
#### Trait
|
562
|
+
#### Trait Expires
|
558
563
|
|
559
|
-
|
564
|
+
This trait will wrap the original value and set one expiration time (in seconds, -1 to never expires). Accepts one tuple (array) of [ value, expiration ].
|
560
565
|
|
561
566
|
```ruby
|
562
|
-
|
567
|
+
requires 'moosex'
|
568
|
+
requires 'moosex/traits'
|
569
|
+
|
570
|
+
class MyHomePage
|
571
|
+
include MooseX
|
572
|
+
has session: {
|
563
573
|
is: :rw,
|
564
|
-
default:
|
565
|
-
|
566
|
-
|
567
|
-
plus: :+,
|
568
|
-
minus: :-,
|
569
|
-
}
|
574
|
+
default: -> { {} },
|
575
|
+
coerce: ->(value) { ((value.is_a?(Array))? value : [value, 3]) },
|
576
|
+
traits: MooseX::Traits::Expires,
|
570
577
|
}
|
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`.
|
578
|
+
end
|
577
579
|
|
578
|
-
|
580
|
+
page = MyHomePage.new
|
581
|
+
page.session.valid? # => true
|
582
|
+
page.session # => {}
|
583
|
+
sleep(3)
|
584
|
+
page.session.valid? # => false
|
585
|
+
page.session= { bar: 5 } # will be coerce to [ {bar: 5}, 3 ]
|
586
|
+
page.session.valid? # => true
|
587
|
+
page.session # => { bar: 5 }
|
588
|
+
sleep(3)
|
589
|
+
page.session.valid? # => false
|
590
|
+
```
|
579
591
|
|
580
|
-
|
592
|
+
See plugin "ExpiredAttribute" for a more clean interface (without this ugly coerce)!
|
581
593
|
|
582
594
|
##### Create your own trait
|
583
595
|
|
@@ -603,47 +615,108 @@ module MooseX
|
|
603
615
|
```
|
604
616
|
You can create or extend your own Traits too. It is easy.
|
605
617
|
|
606
|
-
|
618
|
+
#### Traits Removed
|
619
|
+
|
620
|
+
**IMPORTANT** RescueToNil, RescueToZero and RescueToEmptyString traits are removed since 0.0.20 version
|
607
621
|
|
608
|
-
|
622
|
+
## Plugins
|
623
|
+
|
624
|
+
You can extend MooseX using Plugins, and you can create your own plugins. The only kind of plugin supported is `attribute plugin`, but we can accept other kinds of plugins in the future. To enable one ( or more ) plugins you should specify the list of plugins in the init method when you include the MooseX module.
|
625
|
+
|
626
|
+
### Plugin Chained
|
627
|
+
|
628
|
+
The original behavior of the writter method is return the attribute value. If you want return `self` to continue calling other methods - to create one fluent interface, for example - you can use the plugin `MooseX::Plugins::Chained` and avoid writters with = in the end. For example:
|
609
629
|
|
610
630
|
```ruby
|
611
|
-
|
612
|
-
|
613
|
-
|
631
|
+
require 'moosex'
|
632
|
+
require 'moosex/plugins'
|
633
|
+
|
634
|
+
class EmailMessage
|
635
|
+
include MooseX.init(
|
636
|
+
with_plugins: MooseX::Plugins::Chained
|
637
|
+
)
|
638
|
+
has :_from, writter: :from, chained: true
|
639
|
+
has :_to, writter: :to, chained: true
|
640
|
+
has :_subject, writter: :withSubject, chained: true
|
641
|
+
has :_body , writter: :withBody, chained: true
|
642
|
+
|
643
|
+
def send!
|
644
|
+
# add logic
|
645
|
+
end
|
646
|
+
end
|
614
647
|
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
648
|
+
EmailMessage.new.
|
649
|
+
from("foo@bar.com").
|
650
|
+
to("me@baz.com").
|
651
|
+
withSubject("test").
|
652
|
+
withBody("hi!").
|
653
|
+
send!
|
654
|
+
```
|
655
|
+
|
656
|
+
### Plugin ExpiredAttribute
|
657
|
+
|
658
|
+
It is a easy way to apply the trait Expired in lazy attributes!
|
659
|
+
|
660
|
+
```ruby
|
661
|
+
require 'moosex'
|
662
|
+
require 'moosex/plugins'
|
663
|
+
|
664
|
+
class MyClass
|
665
|
+
include MooseX.init(with_plugins: MooseX::Plugins::ExpiredAttribute)
|
666
|
+
|
667
|
+
has config: {
|
668
|
+
is: :lazy,
|
669
|
+
clearer: true, # mandatory
|
670
|
+
expires: 60, # seconds
|
627
671
|
}
|
672
|
+
|
673
|
+
def build_config
|
674
|
+
# read configuration...
|
675
|
+
end
|
676
|
+
end
|
628
677
|
```
|
629
|
-
|
678
|
+
|
679
|
+
Instead force a coerce to a tuple, here we use a `expires` keyword. You need enable the clearer to read the configuration, in this case.
|
680
|
+
|
681
|
+
### Build your own Plugin
|
682
|
+
|
683
|
+
You should create one Class who accepts one parameter in the constructor (it is a reference for the MooseX::Attribute class) and one method 'process' who will be invoked against the argument hash ( in the constructor ). The reference for the attribute can be used to change the original behavoir and you must delete the used arguments from the hash (in process). See the file `lib/moosex/plugins.rb` for more examples.
|
630
684
|
|
631
685
|
```ruby
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
686
|
+
module MooseX
|
687
|
+
module Plugins
|
688
|
+
class Chained
|
689
|
+
def initialize(this)
|
690
|
+
@this = this
|
691
|
+
end
|
692
|
+
def process(options)
|
693
|
+
chained = !! options.delete(:chained)
|
694
|
+
|
695
|
+
if chained
|
696
|
+
writter = @this.attribute_map[:writter]
|
697
|
+
old_proc = @this.methods[ writter ]
|
698
|
+
@this.methods[writter] = ->(this, value) { old_proc.call(this, value); this }
|
699
|
+
end
|
700
|
+
|
701
|
+
@this.attribute_map[:chained] = chained
|
702
|
+
end
|
703
|
+
end
|
704
|
+
...
|
644
705
|
```
|
645
706
|
|
646
|
-
|
707
|
+
**Important** the public API for MooseX::Attribute is under development and can change in any moment. This will be true until the first stable release.
|
708
|
+
|
709
|
+
### Enable more than one plugin
|
710
|
+
|
711
|
+
You can pass the list of plugins as an array.
|
712
|
+
|
713
|
+
```ruby
|
714
|
+
require 'moosex'
|
715
|
+
require 'moosex/plugins'
|
716
|
+
|
717
|
+
class MyClass
|
718
|
+
include MooseX.init(with_plugins: [ MooseX::Plugins::ExpiredAttribute, MooseX::Plugins::Chained ])
|
719
|
+
```
|
647
720
|
|
648
721
|
## Hooks: after/before/around
|
649
722
|
|
@@ -665,9 +738,7 @@ class Point3D < Point
|
|
665
738
|
|
666
739
|
has z: { is: :rw, required: true }
|
667
740
|
|
668
|
-
after :clear!
|
669
|
-
object.z = 0
|
670
|
-
end
|
741
|
+
after :clear! ->(this) { this.z = 0 }
|
671
742
|
end
|
672
743
|
```
|
673
744
|
|
@@ -697,10 +768,10 @@ class Point
|
|
697
768
|
# do something
|
698
769
|
end
|
699
770
|
|
700
|
-
before :my_method
|
771
|
+
before :my_method ->(this, x) do
|
701
772
|
puts "#{Time.now} before my_method(#{x})"
|
702
773
|
end
|
703
|
-
after :my_method
|
774
|
+
after :my_method ->(this, x) do
|
704
775
|
puts "#{Time.now} after my_method(#{x})"
|
705
776
|
end
|
706
777
|
end
|
@@ -711,7 +782,7 @@ end
|
|
711
782
|
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
|
712
783
|
|
713
784
|
```ruby
|
714
|
-
around(:sum)
|
785
|
+
around(:sum) ->(method_lambda, this, a,b,c) do
|
715
786
|
c = 0
|
716
787
|
result = method_lambda.call(this,a,b,c)
|
717
788
|
result + 1
|
@@ -967,8 +1038,8 @@ Parameterized roles is a good way of reuse code based on roles. For example, to
|
|
967
1038
|
module EasyCrud
|
968
1039
|
include MooseX
|
969
1040
|
|
970
|
-
on_init do
|
971
|
-
attributes.each
|
1041
|
+
on_init ->(*attributes) do
|
1042
|
+
attributes.each ->(attr) do
|
972
1043
|
has attr, { is: :rw, predicate: "has_attr_#{attr}_or_not?" }
|
973
1044
|
end
|
974
1045
|
end
|
@@ -989,7 +1060,7 @@ To combine one or more parameterized roles to another parameterized role you sho
|
|
989
1060
|
module Logabble2
|
990
1061
|
include MooseX
|
991
1062
|
|
992
|
-
on_init do
|
1063
|
+
on_init ->(args) do
|
993
1064
|
args[:klass] = self
|
994
1065
|
include Logabble.init(args)
|
995
1066
|
end
|
@@ -998,7 +1069,7 @@ end
|
|
998
1069
|
module Logabble
|
999
1070
|
include MooseX
|
1000
1071
|
|
1001
|
-
on_init
|
1072
|
+
on_init ->(args) do
|
1002
1073
|
|
1003
1074
|
klass = args[:klass] || self # <= THIS will guarantee you will
|
1004
1075
|
methods = args[:methods] || [] # modify the right class
|
@@ -1082,11 +1153,11 @@ end
|
|
1082
1153
|
|
1083
1154
|
e = Example.new
|
1084
1155
|
|
1085
|
-
e.on(:pinged) do
|
1156
|
+
e.on(:pinged) ->(this) do
|
1086
1157
|
puts "Ping!"
|
1087
1158
|
end
|
1088
1159
|
|
1089
|
-
e.once(:pinged) do
|
1160
|
+
e.once(:pinged) ->(this) do
|
1090
1161
|
puts "Ping Once!"
|
1091
1162
|
end
|
1092
1163
|
|
@@ -1099,7 +1170,7 @@ e.ping # will no longer print nothing
|
|
1099
1170
|
|
1100
1171
|
# you can use arguments
|
1101
1172
|
# consider you have one logger attribute in this example
|
1102
|
-
listener = e.on(:error)
|
1173
|
+
listener = e.on(:error) ->(obj, message) do
|
1103
1174
|
obj.logger.fatal("Error: #{message}")
|
1104
1175
|
end
|
1105
1176
|
|
@@ -1147,11 +1218,11 @@ end
|
|
1147
1218
|
|
1148
1219
|
ep = EventProcessor.new()
|
1149
1220
|
|
1150
|
-
ep.on_ping do
|
1221
|
+
ep.on_ping ->(obj) do
|
1151
1222
|
puts "receive ping!"
|
1152
1223
|
end
|
1153
1224
|
|
1154
|
-
ep.on_pong
|
1225
|
+
ep.on_pong ->(obj, message) do
|
1155
1226
|
puts "receive pong with #{message}!"
|
1156
1227
|
end
|
1157
1228
|
|
data/lib/moosex.rb
CHANGED
@@ -15,6 +15,7 @@ require "moosex/traits"
|
|
15
15
|
require "weakref"
|
16
16
|
|
17
17
|
module MooseX
|
18
|
+
@@PLUGINS = []
|
18
19
|
@@ALIAS = nil
|
19
20
|
@@MOOSEX_WARNINGS = true
|
20
21
|
@@MOOSEX_FATAL = false
|
@@ -40,6 +41,10 @@ module MooseX
|
|
40
41
|
@@ALIAS = (args[:meta].is_a?(TrueClass))? :meta : args[:meta]
|
41
42
|
end
|
42
43
|
|
44
|
+
if args.has_key?(:with_plugins)
|
45
|
+
@@PLUGINS = [ args[:with_plugins] ].flatten
|
46
|
+
end
|
47
|
+
|
43
48
|
self
|
44
49
|
end
|
45
50
|
|
@@ -62,6 +67,11 @@ module MooseX
|
|
62
67
|
unless class_or_module.respond_to? :__moosex__meta
|
63
68
|
meta = MooseX::Meta.new
|
64
69
|
|
70
|
+
@@PLUGINS.each do |plugin|
|
71
|
+
meta.add_plugin(plugin)
|
72
|
+
end
|
73
|
+
@@PLUGINS = []
|
74
|
+
|
65
75
|
class_or_module.define_singleton_method(:__moosex__meta) { meta }
|
66
76
|
|
67
77
|
if @@ALIAS
|
data/lib/moosex/attribute.rb
CHANGED
@@ -15,24 +15,24 @@ module MooseX
|
|
15
15
|
def default ; @attribute_map[:default] ; end
|
16
16
|
|
17
17
|
@@LIST_OF_PARAMETERS = [
|
18
|
-
[:is, MooseX::AttributeModifiers::Is
|
19
|
-
[:isa, MooseX::AttributeModifiers::Isa
|
20
|
-
[:default, MooseX::AttributeModifiers::Default
|
21
|
-
[:required, MooseX::AttributeModifiers::Required
|
22
|
-
[:predicate, MooseX::AttributeModifiers::Predicate
|
23
|
-
[:clearer, MooseX::AttributeModifiers::Clearer
|
24
|
-
[:traits, MooseX::AttributeModifiers::Traits
|
25
|
-
[:handles, MooseX::AttributeModifiers::Handles
|
26
|
-
[:lazy, MooseX::AttributeModifiers::Lazy
|
27
|
-
[:reader, MooseX::AttributeModifiers::Reader
|
28
|
-
[:writter, MooseX::AttributeModifiers::Writter
|
29
|
-
[:builder, MooseX::AttributeModifiers::Builder
|
30
|
-
[:init_arg, MooseX::AttributeModifiers::Init_arg
|
31
|
-
[:trigger, MooseX::AttributeModifiers::Trigger
|
32
|
-
[:coerce, MooseX::AttributeModifiers::Coerce
|
33
|
-
[:weak, MooseX::AttributeModifiers::Weak
|
34
|
-
[:doc, MooseX::AttributeModifiers::Doc
|
35
|
-
[:override, MooseX::AttributeModifiers::Override
|
18
|
+
[:is, MooseX::AttributeModifiers::Is ],
|
19
|
+
[:isa, MooseX::AttributeModifiers::Isa ],
|
20
|
+
[:default, MooseX::AttributeModifiers::Default ],
|
21
|
+
[:required, MooseX::AttributeModifiers::Required ],
|
22
|
+
[:predicate, MooseX::AttributeModifiers::Predicate],
|
23
|
+
[:clearer, MooseX::AttributeModifiers::Clearer ],
|
24
|
+
[:traits, MooseX::AttributeModifiers::Traits ],
|
25
|
+
[:handles, MooseX::AttributeModifiers::Handles ],
|
26
|
+
[:lazy, MooseX::AttributeModifiers::Lazy ],
|
27
|
+
[:reader, MooseX::AttributeModifiers::Reader ],
|
28
|
+
[:writter, MooseX::AttributeModifiers::Writter ],
|
29
|
+
[:builder, MooseX::AttributeModifiers::Builder ],
|
30
|
+
[:init_arg, MooseX::AttributeModifiers::Init_arg ],
|
31
|
+
[:trigger, MooseX::AttributeModifiers::Trigger ],
|
32
|
+
[:coerce, MooseX::AttributeModifiers::Coerce ],
|
33
|
+
[:weak, MooseX::AttributeModifiers::Weak ],
|
34
|
+
[:doc, MooseX::AttributeModifiers::Doc ],
|
35
|
+
[:override, MooseX::AttributeModifiers::Override ],
|
36
36
|
]
|
37
37
|
|
38
38
|
def initialize(attr_symbol, options ,klass)
|
@@ -40,24 +40,23 @@ module MooseX
|
|
40
40
|
@attribute_map = {}
|
41
41
|
|
42
42
|
init_internal_modifiers(options.clone, klass.__moosex__meta.plugins, klass)
|
43
|
-
|
44
|
-
generate_all_methods
|
45
43
|
end
|
46
|
-
|
44
|
+
|
47
45
|
def init_internal_modifiers(options, plugins, klass)
|
48
46
|
@@LIST_OF_PARAMETERS.each do |tuple|
|
49
|
-
parameter,
|
50
|
-
@attribute_map[parameter] =
|
47
|
+
parameter, k = tuple
|
48
|
+
@attribute_map[parameter] = k.new(self).process(options, @attr_symbol)
|
51
49
|
end
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
generate_all_methods
|
52
|
+
|
53
|
+
plugins.sort.uniq.each do |plugin_klass|
|
54
|
+
begin
|
55
|
+
plugin_klass.new(self).process(options)
|
57
56
|
rescue NameError => e
|
58
57
|
next
|
59
58
|
rescue => e
|
60
|
-
raise "Unexpected Error in #{key} #{@attr_symbol}: #{e}"
|
59
|
+
raise "Unexpected Error in #{klass} #{key} #{@attr_symbol}: #{e}"
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
@@ -77,15 +76,15 @@ module MooseX
|
|
77
76
|
|
78
77
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
79
78
|
if @attribute_map[:predicate]
|
80
|
-
@methods[@attribute_map[:predicate]] =
|
81
|
-
instance_variable_defined? inst_variable_name
|
79
|
+
@methods[@attribute_map[:predicate]] = ->(this) do
|
80
|
+
this.instance_variable_defined? inst_variable_name
|
82
81
|
end
|
83
82
|
end
|
84
83
|
|
85
84
|
if @attribute_map[:clearer]
|
86
|
-
@methods[@attribute_map[:clearer]] =
|
87
|
-
if instance_variable_defined? inst_variable_name
|
88
|
-
remove_instance_variable inst_variable_name
|
85
|
+
@methods[@attribute_map[:clearer]] = ->(this) do
|
86
|
+
if this.instance_variable_defined? inst_variable_name
|
87
|
+
this.remove_instance_variable inst_variable_name
|
89
88
|
end
|
90
89
|
end
|
91
90
|
end
|
@@ -103,25 +102,25 @@ module MooseX
|
|
103
102
|
|
104
103
|
@methods[method] = generate_handles_with_currying(delegator, original_method, currying)
|
105
104
|
else
|
106
|
-
@methods[method] = Proc.new do
|
107
|
-
delegator.call(
|
105
|
+
@methods[method] = Proc.new do |this, *args, &proc|
|
106
|
+
delegator.call(this).__send__(target_method, *args, &proc)
|
108
107
|
end
|
109
108
|
end
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
113
112
|
def generate_handles_with_currying(delegator, original_method, currying)
|
114
|
-
Proc.new do
|
113
|
+
Proc.new do |this, *args, &proc|
|
115
114
|
|
116
115
|
a1 = [ currying ]
|
117
116
|
|
118
117
|
if currying.is_a?Proc
|
119
|
-
a1 = currying
|
118
|
+
a1 = currying[]
|
120
119
|
elsif currying.is_a? Array
|
121
|
-
a1 = currying.map{|c| (c.is_a?(Proc)) ? c
|
120
|
+
a1 = currying.map{|c| (c.is_a?(Proc)) ? c[] : c }
|
122
121
|
end
|
123
122
|
|
124
|
-
delegator.call(
|
123
|
+
delegator.call(this).__send__(original_method, *a1, *args, &proc)
|
125
124
|
end
|
126
125
|
end
|
127
126
|
|
@@ -153,8 +152,7 @@ module MooseX
|
|
153
152
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
154
153
|
object.instance_variable_set inst_variable_name, value
|
155
154
|
end
|
156
|
-
|
157
|
-
private
|
155
|
+
|
158
156
|
def generate_reader
|
159
157
|
inst_variable_name = "@#{@attr_symbol}".to_sym
|
160
158
|
|
@@ -168,7 +166,7 @@ module MooseX
|
|
168
166
|
traits = @attribute_map[:traits]
|
169
167
|
before_get = ->(object) do
|
170
168
|
return if object.instance_variable_defined? inst_variable_name
|
171
|
-
|
169
|
+
|
172
170
|
value = builder.call(object)
|
173
171
|
value = coerce.call(value)
|
174
172
|
type_check.call( value )
|
@@ -179,9 +177,9 @@ module MooseX
|
|
179
177
|
end
|
180
178
|
end
|
181
179
|
|
182
|
-
-> do
|
183
|
-
before_get.call(
|
184
|
-
instance_variable_get inst_variable_name
|
180
|
+
->(this) do
|
181
|
+
before_get.call(this)
|
182
|
+
this.instance_variable_get inst_variable_name
|
185
183
|
end
|
186
184
|
end
|
187
185
|
|
@@ -202,13 +200,13 @@ module MooseX
|
|
202
200
|
type_check = protect_isa(@attribute_map[:isa], "isa check for #{writter_name}")
|
203
201
|
trigger = @attribute_map[:trigger]
|
204
202
|
traits = @attribute_map[:traits]
|
205
|
-
|
203
|
+
->(this, value) do
|
206
204
|
value = coerce.call(value)
|
207
205
|
type_check.call( value )
|
208
|
-
trigger.call(
|
206
|
+
trigger.call(this,value)
|
209
207
|
value = traits.call(value)
|
210
|
-
instance_variable_set inst_variable_name, value
|
208
|
+
this.instance_variable_set inst_variable_name, value
|
211
209
|
end
|
212
|
-
end
|
210
|
+
end
|
213
211
|
end
|
214
212
|
end
|
@@ -3,6 +3,9 @@ module MooseX
|
|
3
3
|
module ThirdParty
|
4
4
|
end
|
5
5
|
module AttrBaseModifier
|
6
|
+
def initialize(attrs)
|
7
|
+
@attrs=attrs
|
8
|
+
end
|
6
9
|
def process(options, attr_symbol)
|
7
10
|
@attr_symbol = attr_symbol
|
8
11
|
|
@@ -289,10 +292,9 @@ module MooseX
|
|
289
292
|
def coerce(traits,f)
|
290
293
|
original = ->(value) { value }
|
291
294
|
|
292
|
-
[
|
295
|
+
[traits].flatten.inject(original) do |inner, trait|
|
293
296
|
->(value) { trait.new inner.call value }
|
294
297
|
end
|
295
|
-
|
296
298
|
end
|
297
299
|
def validate(traits,f)
|
298
300
|
# TODO
|
data/lib/moosex/core.rb
CHANGED
@@ -99,8 +99,10 @@ module MooseX
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def __moosex__create_methods(attr)
|
102
|
-
attr.methods.each_pair do |method,
|
103
|
-
define_method method
|
102
|
+
attr.methods.each_pair do |method, block|
|
103
|
+
define_method method do |*args|
|
104
|
+
block.call(self, *args)
|
105
|
+
end
|
104
106
|
end
|
105
107
|
|
106
108
|
if attr.is.eql?(:rwp)
|
data/lib/moosex/meta.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
module MooseX
|
2
|
+
module Plugins
|
3
|
+
class Chained
|
4
|
+
def initialize(this)
|
5
|
+
@this = this
|
6
|
+
end
|
7
|
+
def process(options)
|
8
|
+
chained = !! options.delete(:chained)
|
9
|
+
|
10
|
+
if chained
|
11
|
+
writter = @this.attribute_map[:writter]
|
12
|
+
old_proc = @this.methods[ writter ]
|
13
|
+
@this.methods[writter] = ->(this, value) { old_proc.call(this, value); this }
|
14
|
+
end
|
15
|
+
|
16
|
+
@this.attribute_map[:chained] = chained
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ExpiredAttribute
|
21
|
+
def initialize(this)
|
22
|
+
@this = this
|
23
|
+
end
|
24
|
+
def process(options)
|
25
|
+
expires = options.delete(:expires) || nil
|
26
|
+
|
27
|
+
if expires
|
28
|
+
lazy = @this.attribute_map[:lazy]
|
29
|
+
clearer = @this.attribute_map[:clearer]
|
30
|
+
predicate = @this.attribute_map[:predicate]
|
31
|
+
reader = @this.attribute_map[:reader]
|
32
|
+
writter = @this.attribute_map[:writter]
|
33
|
+
|
34
|
+
old_traits= @this.attribute_map[:traits]
|
35
|
+
|
36
|
+
@this.attribute_map[:traits] = ->(this) do
|
37
|
+
MooseX::Traits::Expires.new([ old_traits.call(this), expires ])
|
38
|
+
end
|
39
|
+
|
40
|
+
if reader && clearer && lazy
|
41
|
+
reader_proc = @this.generate_reader
|
42
|
+
@this.methods[reader] = ->(this) do
|
43
|
+
x = reader_proc.call(this)
|
44
|
+
unless x.valid?
|
45
|
+
this.__send__(clearer)
|
46
|
+
x = reader_proc.call(this)
|
47
|
+
end
|
48
|
+
x
|
49
|
+
end
|
50
|
+
elsif reader
|
51
|
+
@this.methods[reader] = @this.generate_reader
|
52
|
+
end
|
53
|
+
if writter
|
54
|
+
@this.methods[writter] = @this.generate_writter
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@this.attribute_map[:expires] = expires
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
data/lib/moosex/traits.rb
CHANGED
@@ -2,6 +2,21 @@ require 'delegate'
|
|
2
2
|
|
3
3
|
module MooseX
|
4
4
|
module Traits
|
5
|
+
class Expires < SimpleDelegator
|
6
|
+
def initialize(args)
|
7
|
+
value, expires = args[0], args[1]
|
8
|
+
@value = value
|
9
|
+
@expires = ((expires >= 0)? Time.now + expires : nil)
|
10
|
+
|
11
|
+
__setobj__(@value)
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
return true if @expires.nil?
|
16
|
+
@expires > Time.now
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
5
20
|
class Counter < SimpleDelegator
|
6
21
|
def initialize(value)
|
7
22
|
@value = value
|
@@ -73,43 +88,6 @@ module MooseX
|
|
73
88
|
def value
|
74
89
|
! self.not
|
75
90
|
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class RescueToNil < SimpleDelegator
|
79
|
-
|
80
|
-
def initialize(value)
|
81
|
-
@value = value
|
82
|
-
@default_value = -> { nil }
|
83
|
-
__setobj__(@value)
|
84
|
-
end
|
85
|
-
|
86
|
-
def method_missing(m, *args, &block)
|
87
|
-
begin
|
88
|
-
super(m, *args, &block)
|
89
|
-
rescue NoMethodError
|
90
|
-
@default_value.call
|
91
|
-
rescue Exception
|
92
|
-
raise
|
93
|
-
end
|
94
|
-
end
|
95
91
|
end
|
96
|
-
|
97
|
-
class RescueToZero < RescueToNil
|
98
|
-
|
99
|
-
def initialize(value)
|
100
|
-
super(value)
|
101
|
-
@default_value = ->{ 0 }
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
class RescueToEmptyString < RescueToNil
|
107
|
-
|
108
|
-
def initialize(value)
|
109
|
-
super(value)
|
110
|
-
@default_value = -> { "" }
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
end
|
92
|
+
end
|
115
93
|
end
|
data/lib/moosex/version.rb
CHANGED
data/moosex.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Tiago Peczenyj"]
|
10
10
|
spec.email = ["tiago.peczenyj@gmail.com"]
|
11
11
|
spec.summary = %q{A postmodern object DSL for Ruby}
|
12
|
-
spec.description = %q{MooseX is an extension of Ruby object DSL. The main goal of MooseX is to make Ruby Object Oriented programming easier, more consistent, and less tedious. With MooseX you can think more about what you want to do and less about the mechanics of OOP. It is a port of Moose/Moo from Perl to Ruby world.}
|
12
|
+
spec.description = %q{MooseX is an extension of Ruby object DSL. The main goal of MooseX is to make Ruby Object Oriented programming easier, more consistent, and less tedious. With MooseX you can think more about what you want to do and less about the mechanics of OOP. It is a port of Moose/Moo from Perl to Ruby world, providing method delegation, type check, monads, lazy attributes, aspects and much more.}
|
13
13
|
spec.homepage = "http://github.com/peczenyj/MooseX"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
data/samples/plugin.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'moosex'
|
2
|
+
require 'moosex/attribute'
|
3
|
+
|
4
|
+
module MooseX
|
5
|
+
module AttributeModifiers
|
6
|
+
module ThirdParty
|
7
|
+
class Chained
|
8
|
+
def initialize(this)
|
9
|
+
@this = this
|
10
|
+
end
|
11
|
+
def process(options, attr_symbol)
|
12
|
+
|
13
|
+
chained = !! options.delete(:chained)
|
14
|
+
|
15
|
+
if chained
|
16
|
+
writter = @this.attribute_map[:writter]
|
17
|
+
old_proc = @this.methods[ writter ]
|
18
|
+
@this.methods[writter] = ->(this, value) { old_proc.call(this, value); this }
|
19
|
+
end
|
20
|
+
|
21
|
+
chained
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module MyPlugin
|
29
|
+
def self.included(x)
|
30
|
+
x.meta.add_plugin(:chained)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Z
|
35
|
+
include MooseX.init(meta: true)
|
36
|
+
include MyPlugin
|
37
|
+
|
38
|
+
has :foo, {
|
39
|
+
writter: :set_foo,
|
40
|
+
chained: true,
|
41
|
+
}
|
42
|
+
|
43
|
+
has :bar, {
|
44
|
+
writter: :set_bar,
|
45
|
+
chained: false,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
a1 = Z.new
|
50
|
+
a2 = Z.new
|
51
|
+
|
52
|
+
puts a1.set_foo(1).foo
|
53
|
+
|
54
|
+
puts a2.set_bar(1)#.bar
|
data/spec/modifiers_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'moosex/attribute/modifiers'
|
|
3
3
|
describe MooseX::AttributeModifiers::Is do
|
4
4
|
it "should accept only valid parameters" do
|
5
5
|
expect {
|
6
|
-
MooseX::AttributeModifiers::Is.new.process({is: :forbidden}, :foo)
|
6
|
+
MooseX::AttributeModifiers::Is.new(nil).process({is: :forbidden}, :foo)
|
7
7
|
}.to raise_error(MooseX::InvalidAttributeError,
|
8
8
|
"invalid value for field 'foo' is 'forbidden', must be one of :private, :rw, :rwp, :ro or :lazy")
|
9
9
|
end
|
@@ -12,7 +12,7 @@ end
|
|
12
12
|
describe MooseX::AttributeModifiers::Predicate do
|
13
13
|
it "should accept only valid parameters" do
|
14
14
|
expect {
|
15
|
-
MooseX::AttributeModifiers::Predicate.new.process({predicate: 0}, :foo)
|
15
|
+
MooseX::AttributeModifiers::Predicate.new(nil).process({predicate: 0}, :foo)
|
16
16
|
}.to raise_error(MooseX::InvalidAttributeError,
|
17
17
|
"cannot coerce field predicate to a symbol for foo: undefined method `to_sym' for 0:Fixnum")
|
18
18
|
end
|
@@ -21,7 +21,7 @@ end
|
|
21
21
|
describe MooseX::AttributeModifiers::Handles do
|
22
22
|
it "should accept only valid parameters" do
|
23
23
|
expect {
|
24
|
-
MooseX::AttributeModifiers::Handles.new.process({handles: BasicObject}, :foo)
|
24
|
+
MooseX::AttributeModifiers::Handles.new(nil).process({handles: BasicObject}, :foo)
|
25
25
|
}.to raise_error(MooseX::InvalidAttributeError,
|
26
26
|
"ops, should not use BasicObject for handles in foo")
|
27
27
|
end
|
data/spec/plugin_spec.rb
CHANGED
@@ -1,47 +1,58 @@
|
|
1
1
|
require 'moosex'
|
2
|
-
require 'moosex/
|
3
|
-
require 'moosex/
|
4
|
-
|
5
|
-
module MooseX
|
6
|
-
module AttributeModifiers
|
7
|
-
module ThirdParty
|
8
|
-
class Bar
|
9
|
-
def process(options, attr_symbol)
|
10
|
-
!! options.delete(:bar)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
2
|
+
require 'moosex/traits'
|
3
|
+
require 'moosex/plugins'
|
16
4
|
|
17
|
-
module
|
18
|
-
|
19
|
-
|
5
|
+
module TestAddAttribute
|
6
|
+
class EmailMessage
|
7
|
+
include MooseX.init(
|
8
|
+
with_plugins: MooseX::Plugins::Chained
|
9
|
+
)
|
10
|
+
has :_from, writter: :from, chained: true
|
11
|
+
has :_to, writter: :to, chained: true
|
12
|
+
has :_subject, writter: :withSubject, chained: true
|
13
|
+
has :_body , writter: :withBody, chained: true
|
14
|
+
|
15
|
+
def send
|
16
|
+
{
|
17
|
+
from: self._from,
|
18
|
+
to: self._to,
|
19
|
+
subject: self._subject,
|
20
|
+
body: self._body,
|
21
|
+
}
|
22
|
+
end
|
20
23
|
end
|
21
|
-
|
22
|
-
|
23
|
-
module TestAddAttribute
|
24
|
+
|
24
25
|
class A
|
25
|
-
include MooseX.init(
|
26
|
-
|
26
|
+
include MooseX.init(
|
27
|
+
meta: true,
|
28
|
+
with_plugins: MooseX::Plugins::Chained
|
29
|
+
)
|
27
30
|
|
28
31
|
has :foo, {
|
29
|
-
|
32
|
+
writter: :set_foo,
|
33
|
+
chained: true
|
30
34
|
}
|
31
35
|
end
|
32
36
|
|
33
37
|
class B < A
|
34
38
|
|
35
39
|
has :foo2, {
|
36
|
-
|
40
|
+
writter: :set_foo2,
|
41
|
+
chained: true
|
37
42
|
}
|
43
|
+
|
44
|
+
has :foo3, {
|
45
|
+
writter: :set_foo3,
|
46
|
+
chained: false
|
47
|
+
}
|
38
48
|
end
|
39
49
|
|
40
50
|
class C
|
41
51
|
include MooseX.init(meta: true)
|
42
52
|
|
43
53
|
has :foo, {
|
44
|
-
|
54
|
+
writter: :set_foo,
|
55
|
+
chained: true, # will warn only!
|
45
56
|
}
|
46
57
|
end
|
47
58
|
end
|
@@ -52,23 +63,114 @@ describe TestAddAttribute do
|
|
52
63
|
end
|
53
64
|
|
54
65
|
it "A should support the new attribute in meta" do
|
55
|
-
TestAddAttribute::A.meta.attrs[:foo].attribute_map[:
|
66
|
+
TestAddAttribute::A.meta.attrs[:foo].attribute_map[:chained].should be_true
|
56
67
|
end
|
57
68
|
|
69
|
+
it "A should return self in writter" do
|
70
|
+
a = TestAddAttribute::A.new(foo: 1)
|
71
|
+
a.foo.should == 1
|
72
|
+
a.set_foo(2).should == a
|
73
|
+
a.foo.should == 2
|
74
|
+
end
|
75
|
+
|
58
76
|
it "B should support the new attribute" do
|
59
77
|
TestAddAttribute::B.new(foo: 1, foo2: 2)
|
60
78
|
end
|
61
79
|
|
62
80
|
it "B should support the new attribute in meta" do
|
63
|
-
TestAddAttribute::B.meta.attrs[:foo].attribute_map[:
|
64
|
-
TestAddAttribute::B.meta.attrs[:foo2].attribute_map[:
|
81
|
+
TestAddAttribute::B.meta.attrs[:foo].attribute_map[:chained].should be_true
|
82
|
+
TestAddAttribute::B.meta.attrs[:foo2].attribute_map[:chained].should be_true
|
65
83
|
end
|
66
84
|
|
85
|
+
it "B should return self in writter" do
|
86
|
+
a = TestAddAttribute::B.new(foo: 1, foo2: 2)
|
87
|
+
a.foo.should == 1
|
88
|
+
a.foo2.should == 2
|
89
|
+
a.set_foo(2).should == a
|
90
|
+
a.set_foo2(4).should == a
|
91
|
+
a.foo.should == 2
|
92
|
+
a.foo2.should == 4
|
93
|
+
|
94
|
+
a.set_foo(5).set_foo2(9).should == a
|
95
|
+
a.foo.should == 5
|
96
|
+
a.foo2.should == 9
|
97
|
+
end
|
98
|
+
|
99
|
+
it "B foo3 should not be chained" do
|
100
|
+
a = TestAddAttribute::B.new(foo3: 4)
|
101
|
+
a.foo3.should == 4
|
102
|
+
a.set_foo3(7).should == 7
|
103
|
+
a.foo3.should == 7
|
104
|
+
end
|
105
|
+
|
67
106
|
it "C should support the new attribute" do
|
68
107
|
TestAddAttribute::C.new(foo: 1)
|
69
108
|
end
|
70
109
|
|
71
110
|
it "C should support the new attribute in meta" do
|
72
|
-
TestAddAttribute::C.meta.attrs[:foo].attribute_map[:
|
73
|
-
end
|
74
|
-
|
111
|
+
TestAddAttribute::C.meta.attrs[:foo].attribute_map[:chained].should be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it "C should return value in writter" do
|
115
|
+
a = TestAddAttribute::C.new(foo: 1)
|
116
|
+
a.foo.should == 1
|
117
|
+
a.set_foo(2).should == 2
|
118
|
+
a.foo.should == 2
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "TestAddAttribute::EmailMessage" do
|
123
|
+
|
124
|
+
it "should return a hash with options" do
|
125
|
+
TestAddAttribute::EmailMessage.new.
|
126
|
+
from("foo@bar.com").
|
127
|
+
to("me@baz.com").
|
128
|
+
withSubject("test").
|
129
|
+
withBody("hi!").
|
130
|
+
send.should == {
|
131
|
+
from: "foo@bar.com",
|
132
|
+
to: "me@baz.com",
|
133
|
+
subject: "test",
|
134
|
+
body: "hi!"
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module TestAddAttribute
|
140
|
+
class MyClass
|
141
|
+
include MooseX.init(meta: true, with_plugins: MooseX::Plugins::ExpiredAttribute)
|
142
|
+
|
143
|
+
has :log
|
144
|
+
|
145
|
+
has config: {
|
146
|
+
is: :lazy,
|
147
|
+
clearer: true,
|
148
|
+
predicate: true,
|
149
|
+
expires: 4, # seconds
|
150
|
+
}
|
151
|
+
|
152
|
+
has :session, default: ->{ {} }, expires: -1
|
153
|
+
|
154
|
+
def build_config
|
155
|
+
log.info(:created)
|
156
|
+
{ foo: 1 }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe TestAddAttribute::MyClass do
|
162
|
+
it "should reload config, but not session" do
|
163
|
+
log = double
|
164
|
+
log.should_receive(:info).with(:created).twice
|
165
|
+
|
166
|
+
c = TestAddAttribute::MyClass.new(log: log)
|
167
|
+
|
168
|
+
c.config.should == { foo: 1}
|
169
|
+
c.session.valid?.should be_true
|
170
|
+
|
171
|
+
sleep(4)
|
172
|
+
|
173
|
+
c.config.should == { foo: 1}
|
174
|
+
c.session.valid?.should be_true
|
175
|
+
end
|
176
|
+
end
|
data/spec/traits_spec.rb
CHANGED
@@ -55,97 +55,47 @@ module TestTrait
|
|
55
55
|
}
|
56
56
|
}
|
57
57
|
|
58
|
-
has
|
59
|
-
is: :
|
58
|
+
has __bit__: {
|
59
|
+
is: :private,
|
60
60
|
default: true,
|
61
61
|
traits: MooseX::Traits::Bool,
|
62
|
-
handles: [ :toggle!, :not, :set!, :unset!, :value ],
|
63
|
-
}
|
64
|
-
|
65
|
-
has important: {
|
66
|
-
is: :rw,
|
67
|
-
default: nil,
|
68
|
-
traits: MooseX::Traits::RescueToNil,
|
69
62
|
handles: {
|
70
|
-
|
71
|
-
|
72
|
-
|
63
|
+
:bit_toggle! => :toggle!,
|
64
|
+
:bit_turn_on! => :set!,
|
65
|
+
:bit_turn_off! => :unset!,
|
66
|
+
:bit_on? => :value,
|
67
|
+
:bit_off? => :not,
|
68
|
+
},
|
69
|
+
init_arg: :bit,
|
73
70
|
}
|
74
|
-
|
75
|
-
has
|
71
|
+
|
72
|
+
has session: {
|
76
73
|
is: :rw,
|
77
|
-
default:
|
78
|
-
|
79
|
-
|
80
|
-
plus2: :+,
|
81
|
-
minus2: :-,
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
has phrase: {
|
86
|
-
is: :rw,
|
87
|
-
default: nil,
|
88
|
-
traits: MooseX::Traits::RescueToEmptyString,
|
89
|
-
handles: {
|
90
|
-
upcase_phrase: :upcase
|
91
|
-
}
|
74
|
+
default: -> { {} },
|
75
|
+
coerce: ->(value) { ((value.is_a?(Array))? value : [value, 3]) },
|
76
|
+
traits: MooseX::Traits::Expires,
|
92
77
|
}
|
93
78
|
|
94
79
|
def build_lazy_counter
|
95
80
|
0
|
96
81
|
end
|
97
82
|
end
|
98
|
-
|
99
|
-
class ComplexExample
|
100
|
-
include MooseX
|
101
|
-
include MooseX::Types
|
102
|
-
|
103
|
-
has surname_name: {
|
104
|
-
is: :rw,
|
105
|
-
isa: isMaybe(isTuple(String, String)),
|
106
|
-
default: nil,
|
107
|
-
traits: [ MooseX::Traits::RescueToEmptyString, MooseX::Traits::Pair ],
|
108
|
-
handles: {
|
109
|
-
surname: :first,
|
110
|
-
name: :second,
|
111
|
-
:surname= => :first=,
|
112
|
-
:name= => :second=,
|
113
|
-
surname_and_name: { join: ->{", "} }
|
114
|
-
}
|
115
|
-
}
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe TestTrait::ComplexExample do
|
120
|
-
it "should be possible call surname_and_name" do
|
121
|
-
ce = TestTrait::ComplexExample.new(surname_name: ["Asimov", "Isaac"])
|
122
|
-
ce.name.should == "Isaac"
|
123
|
-
ce.surname_and_name.should == "Asimov, Isaac"
|
124
|
-
end
|
125
|
-
|
126
|
-
it "should be possible call surname_and_name if nil" do
|
127
|
-
ce = TestTrait::ComplexExample.new(surname_name: nil)
|
128
|
-
ce.name.should == ""
|
129
|
-
ce.surname_and_name.should == ", "
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should be possible call surname_and_name if nil 2" do
|
133
|
-
ce = TestTrait::ComplexExample.new(surname_name: nil)
|
134
|
-
ce.name.should == ""
|
135
|
-
ce.surname_and_name.should == ", "
|
136
|
-
|
137
|
-
ce.name= "Isaac"
|
138
|
-
ce.surname_and_name.should == ", Isaac"
|
139
|
-
ce.surname= "Asimov"
|
140
|
-
ce.surname_and_name.should == "Asimov, Isaac"
|
141
|
-
|
142
|
-
ce.surname_name= nil
|
143
|
-
ce.name.should == ""
|
144
|
-
ce.surname_and_name.should == ", "
|
145
|
-
end
|
146
83
|
end
|
147
84
|
|
148
85
|
describe TestTrait::MyHomePage do
|
86
|
+
it "should accept valid session" do
|
87
|
+
page = TestTrait::MyHomePage.new
|
88
|
+
page.session.valid?.should be_true
|
89
|
+
page.session.should == {}
|
90
|
+
sleep(3)
|
91
|
+
page.session.valid?.should be_false
|
92
|
+
page.session= { bar: 5 }
|
93
|
+
page.session.valid?.should be_true
|
94
|
+
page.session.should == { bar: 5 }
|
95
|
+
sleep(3)
|
96
|
+
page.session.valid?.should be_false
|
97
|
+
end
|
98
|
+
|
149
99
|
it "should increase counter" do
|
150
100
|
page = TestTrait::MyHomePage.new(counter: 0)
|
151
101
|
page.counter.should be_zero
|
@@ -249,76 +199,31 @@ describe TestTrait::MyHomePage do
|
|
249
199
|
page.surname_name.count.should == 2
|
250
200
|
}.to raise_error(NoMethodError)
|
251
201
|
end
|
202
|
+
|
203
|
+
it "bit should act as a boolean in arg list" do
|
204
|
+
page = TestTrait::MyHomePage.new(bit: false)
|
205
|
+
page.bit_on?.should be_false
|
206
|
+
end
|
252
207
|
|
253
208
|
it "bit should act as a boolean" do
|
254
209
|
page = TestTrait::MyHomePage.new
|
255
|
-
page.
|
256
|
-
page.
|
257
|
-
page.
|
258
|
-
page.
|
210
|
+
page.bit_on?.should be_true
|
211
|
+
page.bit_toggle!
|
212
|
+
page.bit_on?.should be_false
|
213
|
+
page.bit_off?.should be_true
|
259
214
|
|
260
|
-
page.
|
261
|
-
page.
|
215
|
+
page.bit_turn_on!
|
216
|
+
page.bit_on?.should be_true
|
262
217
|
|
263
|
-
unless page.
|
218
|
+
unless page.bit_on?
|
264
219
|
raise "should act as a true value"
|
265
220
|
end
|
266
|
-
page.
|
267
|
-
page.
|
221
|
+
page.bit_turn_off!
|
222
|
+
page.bit_on?.should be_false
|
268
223
|
|
269
|
-
if
|
224
|
+
if page.bit_on?
|
270
225
|
raise "should act as a false value"
|
271
226
|
end
|
272
|
-
|
273
|
-
if page.value
|
274
|
-
raise "should act as a false value"
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
it "important should be converted to integer" do
|
279
|
-
page = TestTrait::MyHomePage.new(important: 1)
|
280
|
-
page.important.should == 1
|
281
|
-
page.plus(1).should == 2
|
282
|
-
page.minus(4).should == -3
|
283
|
-
(page.important + 5).should == 6
|
284
|
-
end
|
285
|
-
|
286
|
-
it "important should be converted to integer returning nil" do
|
287
|
-
page = TestTrait::MyHomePage.new(important: nil)
|
288
|
-
page.important.should == nil
|
289
|
-
page.plus(1).should == nil
|
290
|
-
page.minus(4).should == nil
|
291
|
-
(page.important + 5).should == nil
|
292
|
-
end
|
293
|
-
|
294
|
-
it "important2 should be converted to integer" do
|
295
|
-
page = TestTrait::MyHomePage.new(important2: 1)
|
296
|
-
page.important2.should == 1
|
297
|
-
page.plus2(1).should == 2
|
298
|
-
page.minus2(4).should == -3
|
299
|
-
(page.important2 + 5).should == 6
|
300
|
-
end
|
301
|
-
|
302
|
-
it "important2 should be converted to integer returning 0" do
|
303
|
-
page = TestTrait::MyHomePage.new(important2: nil)
|
304
|
-
page.important2.should be_zero
|
305
|
-
page.plus2(1).should == 0
|
306
|
-
page.minus2(4).should == 0
|
307
|
-
(page.important2 + 5).should == 0
|
308
|
-
end
|
309
|
-
|
310
|
-
it "phrase should be converted to String" do
|
311
|
-
page = TestTrait::MyHomePage.new(phrase: "hello")
|
312
|
-
page.phrase.should == "hello"
|
313
|
-
page.upcase_phrase == "HELLO"
|
314
|
-
(page.upcase_phrase.concat ", WORLD").should == "HELLO, WORLD"
|
315
|
-
end
|
316
|
-
|
317
|
-
it "phrase should returning empty string" do
|
318
|
-
page = TestTrait::MyHomePage.new(phrase: nil)
|
319
|
-
page.phrase.should be_eql? ""
|
320
|
-
page.upcase_phrase == ""
|
321
|
-
(page.upcase_phrase.concat ", WORLD").should == ", WORLD"
|
322
|
-
end
|
227
|
+
end
|
323
228
|
end
|
324
229
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moosex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Peczenyj
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,7 +83,8 @@ dependencies:
|
|
83
83
|
description: MooseX is an extension of Ruby object DSL. The main goal of MooseX is
|
84
84
|
to make Ruby Object Oriented programming easier, more consistent, and less tedious.
|
85
85
|
With MooseX you can think more about what you want to do and less about the mechanics
|
86
|
-
of OOP. It is a port of Moose/Moo from Perl to Ruby world
|
86
|
+
of OOP. It is a port of Moose/Moo from Perl to Ruby world, providing method delegation,
|
87
|
+
type check, monads, lazy attributes, aspects and much more.
|
87
88
|
email:
|
88
89
|
- tiago.peczenyj@gmail.com
|
89
90
|
executables: []
|
@@ -107,6 +108,7 @@ files:
|
|
107
108
|
- lib/moosex/event.rb
|
108
109
|
- lib/moosex/exceptions.rb
|
109
110
|
- lib/moosex/meta.rb
|
111
|
+
- lib/moosex/plugins.rb
|
110
112
|
- lib/moosex/traits.rb
|
111
113
|
- lib/moosex/types.rb
|
112
114
|
- lib/moosex/version.rb
|
@@ -115,6 +117,7 @@ files:
|
|
115
117
|
- samples/binary_tree.rb
|
116
118
|
- samples/events.rb
|
117
119
|
- samples/human.rb
|
120
|
+
- samples/plugin.rb
|
118
121
|
- samples/point.rb
|
119
122
|
- samples/roles.rb
|
120
123
|
- spec/baserole_spec.rb
|
@@ -190,4 +193,3 @@ test_files:
|
|
190
193
|
- spec/trigger_spec.rb
|
191
194
|
- spec/types_spec.rb
|
192
195
|
- spec/weak_spec.rb
|
193
|
-
has_rdoc:
|