moosex 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ module MooseX
2
+ module Core
3
+ def on_init(&block)
4
+ __moosex__meta.add_role(block)
5
+ end
6
+
7
+ def after(*methods_name, &block)
8
+ __moosex__add_hooks(:after, methods_name, block, caller())
9
+ end
10
+
11
+ def before(*methods_name, &block)
12
+ __moosex__add_hooks(:before, methods_name, block, caller())
13
+ end
14
+
15
+ def around(*methods_name, &block)
16
+ __moosex__add_hooks(:around, methods_name, block, caller())
17
+ end
18
+
19
+ def requires(*methods)
20
+ methods.each do |method_name|
21
+ __moosex__meta.add_requires(method_name)
22
+ end
23
+ end
24
+
25
+ def has(attr_name, attr_options = {})
26
+ if attr_name.is_a? Array
27
+ attr_name.each do |attr|
28
+ __moosex__has(attr, attr_options)
29
+ end
30
+ elsif attr_name.is_a? Hash
31
+ attr_name.each_pair do |attr, options |
32
+ has(attr, options)
33
+ end
34
+ else
35
+ __moosex__has(attr_name, attr_options)
36
+ end
37
+ end
38
+
39
+ private
40
+ def __moosex__add_hooks(hook, methods_name, block, c)
41
+ methods_name.each do |method_name|
42
+ begin
43
+ __moosex__try_to_add_hook_now(hook, method_name, block)
44
+ rescue => e
45
+ MooseX.warn "unable to apply hook #{hook} in #{method_name} @ #{self}: #{e}", c if self.is_a?(Class)
46
+ __moosex__meta.add_hook(hook, method_name, block)
47
+ end
48
+ end
49
+ end
50
+
51
+ def __moosex__try_to_add_hook_now(hook, method_name, block)
52
+ case hook
53
+ when :before
54
+ __moosex__try_to_add_before_now(method_name, block)
55
+ when :after
56
+ __moosex__try_to_add_after_now(method_name, block)
57
+ when :around
58
+ __moosex__try_to_add_around_now(method_name, block)
59
+ end
60
+ end
61
+
62
+ def __moosex__try_to_add_before_now(method_name, block)
63
+ method = instance_method method_name
64
+
65
+ define_method method_name do |*args, &proc|
66
+ block.call(self,*args, &proc)
67
+ method.bind(self).call(*args, &proc)
68
+ end
69
+ end
70
+
71
+ def __moosex__try_to_add_after_now(method_name, block)
72
+ method = instance_method method_name
73
+
74
+ define_method method_name do |*args, &proc|
75
+ result = method.bind(self).call(*args, &proc)
76
+ block.call(self,*args,&proc)
77
+ result
78
+ end
79
+ end
80
+
81
+ def __moosex__try_to_add_around_now(method_name, block)
82
+ method = instance_method method_name
83
+
84
+ code = Proc.new do | o, *a, &proc|
85
+ method.bind(o).call(*a,&proc)
86
+ end
87
+
88
+ define_method method_name do |*args, &proc|
89
+ block.call(code, self,*args, &proc)
90
+ end
91
+ end
92
+
93
+ def __moosex__has(attr_name, attr_options)
94
+ attr = MooseX::Attribute.new(attr_name, attr_options, self)
95
+
96
+ __moosex__meta.add(attr)
97
+
98
+ __moosex__create_methods(attr)
99
+ end
100
+
101
+ def __moosex__create_methods(attr)
102
+ attr.methods.each_pair do |method, proc|
103
+ define_method method, &proc
104
+ end
105
+
106
+ if attr.is.eql?(:rwp)
107
+ private attr.writter
108
+ elsif attr.is.eql?(:private)
109
+ private attr.writter
110
+ private attr.reader
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,11 @@
1
+ module MooseX
2
+
3
+ class FatalError < StandardError
4
+ end
5
+
6
+ class RequiredMethodNotFoundError < NameError
7
+ end
8
+
9
+ class InvalidAttributeError < TypeError
10
+ end
11
+ end
@@ -0,0 +1,139 @@
1
+ module MooseX
2
+
3
+ class Meta
4
+ attr_reader :attrs, :requires, :hooks
5
+
6
+ def initialize(old_meta=nil)
7
+ @initialized = false
8
+ @attrs = {}
9
+ @requires = []
10
+ @roles = []
11
+
12
+ @hooks = {
13
+ before: Hash.new { |hash, key| hash[key] = [] },
14
+ after: Hash.new { |hash, key| hash[key] = [] },
15
+ around: Hash.new { |hash, key| hash[key] = [] },
16
+ }
17
+
18
+ if old_meta
19
+ old_meta.attrs.each_pair do |key, value|
20
+ @attrs[key] = value.clone
21
+ end
22
+ @requires = old_meta.requires.clone
23
+ end
24
+ end
25
+
26
+ def info
27
+ @attrs.map{|attr_symbol, attr| {attr_symbol => attr.doc }}.reduce(:merge)
28
+ end
29
+
30
+ def init_roles(*args)
31
+ @roles.each do|role|
32
+ role.call(*args)
33
+ end
34
+ end
35
+
36
+ def load_from(module_or_class)
37
+ other_meta = module_or_class.__moosex__meta
38
+ other_meta.attrs.each_pair do |key, value|
39
+ @attrs[key] = value.clone
40
+ end
41
+ @requires += other_meta.requires
42
+ end
43
+
44
+ def load_from_klass(klass)
45
+ other_meta = klass.__moosex__meta
46
+
47
+ other_meta.hooks.each_pair do |hook, data|
48
+ data.each_pair do |m, b|
49
+ @hooks[hook][m] += b.clone
50
+ end
51
+ end
52
+ end
53
+
54
+ def add(attr)
55
+ if @attrs.has_key?(attr.attr_symbol) && ! attr.override
56
+ raise FatalError, "#{attr.attr_symbol} already exists, you should specify override: true"
57
+ end
58
+ @attrs[attr.attr_symbol] = attr
59
+ end
60
+
61
+ def add_requires(method)
62
+ @requires << method
63
+ end
64
+
65
+ def add_hook(hook, method, block)
66
+ @hooks[hook][method] << block.clone
67
+ end
68
+
69
+ def add_role(block)
70
+ @roles << block
71
+ end
72
+
73
+ def init(object, args)
74
+ @attrs.each_pair do |symbol, attr|
75
+ attr.init(object, args)
76
+ end
77
+
78
+ MooseX.warn "unused attributes #{args} for #{object.class}", caller unless args.empty?
79
+
80
+ @requires.each do |method|
81
+ unless object.respond_to? method
82
+ raise RequiredMethodNotFoundError,
83
+ "you must implement method '#{method}' in #{object.class}: required"
84
+ end
85
+ end
86
+ end
87
+
88
+ def init_klass(klass)
89
+ @hooks.values.map {|h| h.keys }.flatten.uniq.map do |method_name|
90
+ begin
91
+ [ klass.instance_method(method_name), method_name ]
92
+ rescue => e
93
+ MooseX.warn "Unable to apply hooks (after/before/around) in #{klass}::#{method_name} : #{e}" # if $MOOSEX_DEBUG
94
+ nil
95
+ end
96
+ end.select do |value|
97
+ !value.nil?
98
+ end.reduce({}) do |hash, tuple|
99
+ method, method_name = tuple
100
+
101
+ hash[method_name] = __moosex__init_hooks(method_name, method)
102
+
103
+ hash
104
+ end
105
+ end
106
+
107
+ def verify_requires_for(x)
108
+ @requires.each do |method|
109
+ unless x.public_instance_methods.include? method
110
+ MooseX.warn "you must implement method '#{method}' in #{x} #{x.class}: required"# if $MOOSEX_DEBUG
111
+ end
112
+ end
113
+ end
114
+ private
115
+ def __moosex__init_hooks(method_name, method)
116
+
117
+ before = @hooks[:before][method_name]
118
+ after = @hooks[:after][method_name]
119
+ around = @hooks[:around][method_name]
120
+
121
+ original = lambda do |object, *args, &proc|
122
+ method.bind(object).call(*args, &proc)
123
+ end
124
+
125
+ lambda do |*args, &proc|
126
+ before.each{|b| b.call(self,*args, &proc)}
127
+
128
+ result = around.inject(original) do |lambda1, lambda2|
129
+ lambda2.curry[lambda1]
130
+ end.call(self, *args, &proc)
131
+
132
+ after.each{|b| b.call(self,*args, &proc)}
133
+
134
+ result
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -6,6 +6,7 @@ module MooseX
6
6
  end
7
7
 
8
8
  class TypeCheckError < TypeError
9
+
9
10
  end
10
11
 
11
12
  module Core
@@ -181,45 +182,56 @@ module MooseX
181
182
 
182
183
  def isTuple(*types)
183
184
 
184
- createValidator "[Tuple [#{types.map{|t| t.to_s}.join ', '}]]" do |tuple|
185
- isType(Array).call(tuple)
186
-
185
+ size_validation = lambda do |tuple|
187
186
  unless tuple.size == types.size
188
187
  raise TypeCheckError, "Tuple violation: size should be #{types.size} instead #{tuple.size}"
189
- end
190
-
191
- types.each_index do |index|
192
- begin
193
- isType(types[index]).call(tuple[index])
194
- rescue TypeCheckError => e
195
- raise TypeCheckError, "Tuple violation: on position #{index} caused by #{e}"
196
- end
197
- end
188
+ end
198
189
  end
199
190
 
191
+ individual_validations = create_individual_validations_for_tuples(types)
192
+
193
+ proc = isAllOf(
194
+ isType(Array),
195
+ size_validation,
196
+ *individual_validations,
197
+ )
198
+
199
+ createValidator("[Tuple [#{types.map{|t| t.to_s}.join ', '}]]", &proc)
200
200
  end
201
201
 
202
202
  def isSet(type=nil)
203
203
  type = isAny if type.nil?
204
204
 
205
- createValidator "[Set #{type.to_s}]" do |set|
206
- isType(Array).call(set)
205
+ proc = isAllOf(
206
+ isArray(type),
207
+ lambda do |set|
208
+ if set.uniq.size != set.size
209
+ raise TypeCheckError, "Set violation: has one or more non unique elements"
210
+ end
211
+ end,
212
+ )
207
213
 
208
- if set.uniq.size != set.size
209
- duplicated = set.inject(Hash.new(0)) {|h,i| h[i] += 1; h }.select{|k,v| v > 1 }
210
- raise TypeCheckError, "Set violation: has one or more non unique elements: #{duplicated} (value => count)"
211
- end
214
+ createValidator("[Set #{type.to_s}]", &proc)
215
+ end
212
216
 
213
- set.each do |item|
217
+ private
218
+ def create_individual_validations_for_tuples(types)
219
+ individual_validations = []
220
+ types.each_index do |index|
221
+ individual_validations << lambda do |tuple|
214
222
  begin
215
- isType(type).call(item)
223
+ isType(types[index]).call(tuple[index])
216
224
  rescue TypeCheckError => e
217
- raise TypeCheckError, "Set violation: caused by #{e}"
225
+ raise TypeCheckError, "Tuple violation: on position #{index} caused by #{e}"
218
226
  end
219
227
  end
220
228
  end
221
- end
229
+
230
+ individual_validations
231
+ end
222
232
 
223
233
  end
234
+
224
235
  end
225
- end
236
+
237
+ end
@@ -1,3 +1,3 @@
1
1
  module MooseX
2
- VERSION = "0.0.17"
2
+ VERSION = "0.0.18"
3
3
  end
@@ -13,6 +13,7 @@ class Lol
13
13
  default: 1,
14
14
  predicate: :can_haz_c?, # custom predicate
15
15
  clearer: "desintegrate_c", # force coerce to symbol
16
+ xxx: 1,
16
17
  }
17
18
 
18
19
  has [:d, :e] => {
@@ -0,0 +1,90 @@
1
+ require 'moosex'
2
+
3
+ class TestMeta
4
+ include MooseX.init(meta: true)
5
+
6
+ has :foo
7
+ has :bar, {
8
+ is: :ro,
9
+ default: 1,
10
+ doc: "etc",
11
+ }
12
+ end
13
+
14
+ class TestMeta2
15
+ include MooseX.init(meta: :mymeta)
16
+
17
+ has :foo
18
+ has :bar, {
19
+ is: :ro,
20
+ default: 1,
21
+ doc: "etc",
22
+ }
23
+ end
24
+
25
+ describe TestMeta do
26
+ it "should has 'meta'" do
27
+ TestMeta.respond_to?(:meta).should be_true
28
+ end
29
+
30
+ it "meta should return list of attributes" do
31
+ attrs = TestMeta.meta.attrs
32
+
33
+ attributes = attrs.keys
34
+ attributes[0].should == :foo
35
+ attributes[1].should == :bar
36
+
37
+ attrs[:foo].is.should == :rw
38
+
39
+ attrs[:bar].is.should == :ro
40
+ attrs[:bar].default.call.should == 1
41
+ attrs[:bar].doc.should == "etc"
42
+ end
43
+
44
+ it "meta should return list of documentations" do
45
+ docs = TestMeta.meta.info
46
+
47
+ docs[:foo].should == ""
48
+ docs[:bar].should == "etc"
49
+ end
50
+
51
+ it "TestMeta shuold be possible add an attribute on the fly" do
52
+ TestMeta.has :baz, { required: true}
53
+ tm = TestMeta.new(baz: 1)
54
+ tm.baz.should == 1
55
+
56
+ expect{
57
+ TestMeta.new
58
+ }.to raise_error(MooseX::InvalidAttributeError,
59
+ 'attr "baz" is required')
60
+ end
61
+ end
62
+
63
+ describe TestMeta2 do
64
+ it "should has 'mymeta'" do
65
+ TestMeta2.respond_to?(:meta).should be_false
66
+ TestMeta2.respond_to?(:mymeta).should be_true
67
+ end
68
+
69
+ it "meta should return list of attributes" do
70
+ attrs = TestMeta2.mymeta.attrs
71
+
72
+ attributes = attrs.keys
73
+ attributes[0].should == :foo
74
+ attributes[1].should == :bar
75
+
76
+ attrs[:foo].is.should == :rw
77
+
78
+ attrs[:bar].is.should == :ro
79
+ attrs[:bar].default.call.should == 1
80
+ attrs[:bar].doc.should == "etc"
81
+ end
82
+
83
+ it "meta should return list of documentations" do
84
+ docs = TestMeta2.mymeta.info
85
+
86
+ docs[:foo].should == ""
87
+ docs[:bar].should == "etc"
88
+ end
89
+
90
+ end