moosex 0.0.9 → 0.0.10

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: bf755e4f809015aa27587ea4009cb8e15d082154
4
- data.tar.gz: ec5ee2722ed7fe31bc88050722a803cc366f5958
3
+ metadata.gz: 2e60a3c50ecdccca330d91d980b5512a4ff2134d
4
+ data.tar.gz: b86c1f9dfdfcf180b05d4c4cde80ccec38c9ad83
5
5
  SHA512:
6
- metadata.gz: 0b5c7d36b5ef46c18cdb83893ce74da0b500c0d11706c08e3f15438c0f3e486d937f73bc2a860993fe1b365559b0c651c84060875f443a04cd1d6c49f07ef9bc
7
- data.tar.gz: 4cf80c9f428fe865904ad8c8e79d37460ac1ca4a8cbaf5305ab26a962fefade283ef10ecabf262a58a30e1e852c9c026e933139c09248d0017dc83c31e07baed
6
+ metadata.gz: 27668b133f039c5bc9f99e8b16f291071a213d9cb1e1bf040828953c0d17f2ece5768365a118c46feb4cba8d5f03e4d3b5af3c0f910dce565f4a32ddec820167
7
+ data.tar.gz: ee19e17a6877e20fb57c54490b109d8aca8cc6abe4a4686e4f69bbc13ad9c4e411373f7293579b49a73e3a170a1be7c9fe45a3493a3f793d45b439a6397a6055
data/Changelog CHANGED
@@ -1,3 +1,10 @@
1
+ 0.0.10 - 2014-02-03
2
+ - improve readme #2014-02-01
3
+ - BUILDARGS should accept different signatures #36
4
+ - clearer create by default clear_#{attr_name}! public method
5
+ - add attr :private #35
6
+ - methods predicate, clearer and handles are no longer singleton methods #34
7
+
1
8
  0.0.9 - 2014-02-02
2
9
  - support to BUILD and BUILDARGS #5 and #6
3
10
  - support to trigger #7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- moosex (0.0.9)
4
+ moosex (0.0.10)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MooseX
2
2
 
3
- A postmodern object system for Ruby [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](https://travis-ci.org/peczenyj/MooseX)
3
+ A postmodern object DSL for Ruby [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](https://travis-ci.org/peczenyj/MooseX)
4
4
 
5
5
  THIS MODULE IS EXPERIMENTAL YET! BE CAREFUL!
6
6
 
@@ -24,113 +24,20 @@ class Point
24
24
  default: lambda { 0 }, # you should specify a lambda
25
25
  }
26
26
 
27
- def clear
28
- self.x= 0 # to run with type-check you must
29
- self.y= 0 # use the setter instad @x=
30
- end
31
- end
32
-
33
- class Foo
34
- include MooseX
35
-
36
- has bar: {
37
- is: :rwp, # read-write-private (private setter)
38
- required: true, # you should require in the constructor
39
- }
40
- end
41
-
42
- class Baz
43
- include MooseX
44
-
45
- has bam: {
46
- is: :ro, # read-only, you should specify in new only
47
- isa: lambda do |bam| # you should add your own validator
48
- raise 'bam should be less than 100' if bam > 100
49
- end,
50
- required: true,
51
- }
52
-
53
- has boom: {
54
- is: :rw,
55
- predicate: true, # add has_boom? method, ask if the attribute is unset
56
- clearer: true, # add reset_boom! method, unset the attribute
57
- }
58
- end
59
-
60
- class Lol
61
- include MooseX
62
-
63
- has [:a, :b], { # define attributes a and b
64
- is: :ro, # with same set of properties
65
- default: 0,
66
- }
67
-
68
- has c: { # alternative syntax to be
69
- is: :ro, # more similar to Moo/Moose
70
- default: 1,
71
- predicate: :can_haz_c?, # custom predicate
72
- clearer: "desintegrate_c", # force coerce to symbol
73
- }
74
-
75
- has [:d, :e] => {
76
- is: "ro", # can coerce from strings
77
- default: 2,
78
- }
79
- end
80
-
81
- class Proxy
82
- include MooseX
83
-
84
- has target: {
85
- is: :ro,
86
- default: lambda { Target.new }, # default, new instace of Target
87
- handles: { # handles is for delegation,
88
- my_method_x: :method_x, # inject methods with new names
89
- my_method_y: :method_y, # old => obj.target.method_x
90
- }, # now => obj.my_method_x
91
- }
92
- end
93
-
94
- class Target
95
- def method_x; 1024; end # works with simple methods
96
- def method_y(a,b,c); a + b + c; end # or methods with arguments
97
- end
98
-
99
- class Point3D < Point
100
-
101
- has x: { # override original attr!
102
- is: :rw,
103
- isa: Integer,
104
- default: 1,
105
- }
106
-
107
- has z: {
108
- is: :rw, # read-write (mandatory)
109
- isa: Integer, # should be Integer
110
- default: 0, # default value is 0 (constant)
111
- }
112
-
113
- has color: {
114
- is: :rw, # you should specify the reader/writter
115
- reader: :what_is_the_color_of_this_point,
116
- writter: :set_the_color_of_this_point,
117
- default: :red,
118
- }
119
-
120
- def clear
27
+ def clear!
121
28
  self.x= 0 # to run with type-check you must
122
29
  self.y= 0 # use the setter instad @x=
123
- self.z= 0
124
30
  end
31
+
32
+ def to_s
33
+ "Point[x=#{self.x}, y=#{self.y}]"
34
+ end
125
35
  end
126
36
 
127
37
  # now you have a generic constructor
128
38
  p1 = Point.new # x and y will be 0
129
39
  p2 = Point.new( x: 5 ) # y will be 0
130
40
  p3 = Point.new( x: 5, y: 4)
131
- foo = Foo.new( bar: 123 ) # without bar will raise exception
132
- baz = Baz.new( bam: 99 ) # if bam > 100 will raise exception
133
- Proxy.new.my_method_x # will call method_x in target, return 1024
134
41
  ```
135
42
 
136
43
  ## Installation
@@ -161,42 +68,10 @@ It is fun
161
68
 
162
69
  ## Usage
163
70
 
164
- When you incluse the MooseX module in your class you can declare your attributes. The module provides a new constructor for you, you should not define 'initialize' anymore.
165
-
166
-
167
- ```ruby
168
- require 'moosex'
169
-
170
- class Point
171
- include MooseX
172
-
173
- has x: {
174
- is: :rw, # read-write (mandatory)
175
- isa: Integer, # should be Integer
176
- default: 0, # default value is 0 (constant)
177
- required: false, # if true, will be required in the constructor
178
- predicate: false, # if true, add has_x? method
179
- clearer: false, # if true, add clear_x! method
180
- handles: [ :to_s ],# used for method delegation
181
- }
182
- ...
183
- end
184
- ```
185
-
186
- in this example we add the attribute x, with read-write acessors, a default value and a type check.
187
-
188
- ```ruby
189
- p1 = Point.new # x will be initialize with 0 (default)
190
- p2 = Point.new(x: 50) # initialize x in the constructur
191
- p3 = Point.new(x: "50") # will raise exception
192
- p1.x = "lol" # will raise too
193
- ```
194
-
195
- to use the type check feature you must use the writter method for the attribute.
71
+ You just need include the MooseX module in your class and start to describe the attributes with our DSL. This module will inject one smart constructor, acessor and other necessary methods.
196
72
 
197
- ### Advantages
73
+ Instead the normal way of add accessors, constructor, validation, etc
198
74
 
199
- instead
200
75
  ```ruby
201
76
  class Foo
202
77
  attr_accessor :bar, :baz, :bam
@@ -211,7 +86,8 @@ class Foo
211
86
  end
212
87
  end
213
88
  ```
214
- you can
89
+ you can do this:
90
+
215
91
  ```ruby
216
92
  class Foo
217
93
  include MooseX
@@ -223,77 +99,313 @@ class Foo
223
99
  }
224
100
  end
225
101
  ```
226
- instead
102
+
103
+ ## DSL: the 'has' method
104
+
105
+ The key of the DSL is the 'has' method injected in your class. You should use this method do describe your class and define the behavior like this:
106
+
107
+ ```ruby
108
+ has :attribute_name, { hash of properties }
109
+ ```
110
+
111
+ to describe one new attribute you shoud specify some properties inside a Hash. The only mandatory property is the ':is', to specify how we should create the acessors (if public or private).
112
+
113
+ The options for "has" are as follows:
114
+
115
+ ### is
116
+
117
+ **Required**, may be :ro, :rw, :rwp, :private or :lazy.
118
+
119
+ "ro" specify a read-only attribute - generate only the reader method - you should specify the value in the constructor or using "default".
120
+
121
+ "rw" specify a read-write attribute - generate both reader and writter methods.
122
+
123
+ "rwp" specify a read-write private attribute. Similar to "rw" but the writter is a private method.
124
+
125
+ "private" will generate both reader and writter as private methods
126
+
127
+ "lazy" similar to "ro", but also sets "lazy" to true and "builder" to "build_#{attribute_name}".
128
+
129
+ ### isa
130
+
131
+ You can specify an optional type check for the attribute. Accepts a lambda, and it must raise one exception if the type check fails. If you provides a Class or Module, we will call the 'is_a?' method in the new value againt the Class/Module. We call the type check routine on the constructor and in each call of the writter method.
132
+
133
+ You can specify your own kind of type validation.
134
+ ```ruby
135
+ isa: lambda do |new_value|
136
+ unless new_value.respond_to? :to_sym
137
+ raise "bar should respond to to_sym method!"
138
+ end
139
+ end,
140
+ end
141
+ ```
142
+
143
+ Important: if you access the attribute instance name using @attribute_name= you loose the type check feature. You need always set/get the attribute value using the acessors generated by MooseX.
144
+
145
+ ### default
146
+
147
+ You can specify an optional default value to one attribute. If we don't specify in the constructor, we will initialize the attribute with this value. You also can specify one lambda to force object creation.
148
+
149
+ ```ruby
150
+ default: 0,
151
+ ```
152
+ or
153
+ ```ruby
154
+ default: lambda{ MyObject.new },
155
+ ```
156
+
157
+ ### coerce
158
+
159
+ You can try to coerce the attribute value by a lambda before the type check phase. For example you can do
160
+
161
+ ```ruby
162
+ coerce: lambda{ |new_value| new_value.to_i },
163
+ ```
164
+
165
+ to force a convertion to integer. Or flatten one array, convert to symbol, etc. Optional.
166
+
167
+ ### handles
168
+
169
+ One of the greatest features in MooseX: you can inject methods and delegate the method calling to the attribute. For example, instead do this:
170
+
227
171
  ```ruby
228
- class Proxy
229
- def initialize(target)
230
- @target=target
172
+ def some_method(a,b,c)
173
+ @attribute.some_method(a,b,c)
231
174
  end
175
+ ```
176
+
177
+ you simply specify one or more methods to inject.
178
+ ```ruby
179
+ handles: [:some_method],
180
+ ```
181
+ If you specify one Module or Class, we will handle all public instance methods defined in that Module or Class ( if Class, we will consider all methods except the methods declared in the superclass). The only limitation is BasicObject (forbidden).
232
182
 
233
- def method_x(a,b,c)
234
- @target.method_x(a,b,c)
183
+ If you need rename the method, you can specify a Hash:
184
+ ```ruby
185
+ handles: {
186
+ my_method_1: :method1,
187
+ my_method_2: :method2,
188
+ },
189
+ ```
190
+
191
+ Optional.
192
+
193
+ ### trigger
194
+
195
+ 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.
196
+
197
+ ```ruby
198
+ trigger: lambda {|object, new_value| object.logger.log "change the attribute value to #{new_value}" }
199
+ ```
200
+ or
201
+ ```ruby
202
+ has a: { is: :rw }
203
+ has b: {
204
+ is: :rw,
205
+ trigger: :verify_if_a_and_b_are_different,
206
+ }
207
+ ...
208
+ def verify_if_a_and_b_are_different(new_value_of_b)
209
+ if self.a.eql? new_value_of_b
210
+ raise "a and b should be different!"
211
+ end
235
212
  end
236
- def method_y(a,b,c)
237
- @target.method_y(a,b,c)
238
- end
213
+ ```
214
+
215
+ Optional.
216
+
217
+ ### writter
218
+
219
+ You can specify the name of the attribute acessor, default is "#{attribute_name}=".
220
+
221
+ ### reader
222
+
223
+ You can specify the name of the attribute acessor, default is "attribute_name".
224
+
225
+ ### predicate
226
+
227
+ Creates a method who returns a boolean value if the attribute is defined. If true, will create one public "has_#{attribute_name}?" method by default.
228
+
229
+ For example
230
+ ```ruby
231
+ class Foo
232
+ include MooseX
233
+
234
+ has x: {
235
+ is: :rw,
236
+ predicate: true,
237
+ }
239
238
  end
239
+
240
+ foo = Foo.new
241
+ foo.has_x? # returns false
242
+ foo.x= 10
243
+ foo.has_x? # returns true
240
244
  ```
241
- you can
245
+ Important: nil is different than undefined. If you do not initialize one attribute, you will receive one 'nil' if you try to fetch the value, but the state of this attribute is 'undefined'. If you set any value (even nil), the attribute will be considered 'defined' and the predicate will return true.
246
+
247
+ Optional.
248
+
249
+ ### clearer
250
+
251
+ Creates a method who will unset the attribute. If true, will create one public "clear_#{attribute_name}!" method by default. Unset in this case is not 'nil', we will remove the instance variable. For example:
252
+
242
253
  ```ruby
243
- class Proxy
254
+ class Foo
244
255
  include MooseX
245
256
 
246
- has :target, {
247
- is: :ro,
248
- handles => [ :method_x, :method_y ]
257
+ has x: {
258
+ is: :rw,
259
+ predicate: true,
260
+ clearer: true,
249
261
  }
250
262
  end
263
+
264
+ foo = Foo.new
265
+ foo.has_x? # returns false
266
+ foo.x= 10
267
+ foo.has_x? # returns true
268
+ foo.clear_x! # will unset the attribute x
269
+ foo.has_x? # returns false
251
270
  ```
252
- and much more
271
+ Optional.
272
+
273
+ ### init_arg
253
274
 
254
- ## Lazy Attributes
275
+ You can rename the attribute name in the constructor. For example:
255
276
 
256
277
  ```ruby
257
- class LazyFox
278
+ class Foo
258
279
  include MooseX
259
280
 
260
- has something: {
261
- is: :lazy
281
+ has secret: {
282
+ is: :rw,
283
+ writter: :x=,
284
+ reader: :x,
285
+ init_art: :x,
262
286
  }
287
+ end
288
+
289
+ foo = Foo.new(x: 1) # specify the value of secret in the constructor
290
+ foo.x # return 1
291
+ foo.x= 2 # will set 'secret' to 2
292
+ ```
293
+
294
+ ### lazy
295
+
296
+ Another great feature: lazy attributes. If you this to true, we will wait until the first reader accessor be called to create the object using the builder method, then store the value. For example:
263
297
 
264
- has other_thing: {
298
+ ```ruby
299
+ class Foo
300
+ include MooseX
301
+
302
+ has x: {
265
303
  is: :rw,
266
- lazy: true,
267
- predicate: true,
268
- clearer: true,
269
- builder: :my_build_other_thing,
304
+ lazy: :true,
305
+ predicate: true, # predicate and clearer are just
306
+ clearer: true, # to show a better example
270
307
  }
271
308
 
272
- has lazy_attr_who_accepts_lambda: {
273
- is: :lazy,
274
- builder: lambda{ |object| object.something }
309
+ def builder_x
310
+ Some::Class.new
311
+ end
312
+ end
313
+
314
+ foo = Foo.new # normal...
315
+ foo.has_x? # returns false
316
+ foo.x # will call the builder_x method and store the value
317
+ foo.has_x? # returns true
318
+ foo.x # returns the stored value
319
+ foo.clear_x! # will unset the attribute x
320
+ foo.has_x? # returns false
321
+ foo.x # will call the builder again and store the value
322
+ ```
323
+ A lazy attribute needs a builder method or lambda. By default you should implement the "builder_#{attribute_name}" method. Using lazy you should initialize one attribute when you really need.
324
+
325
+ Optional.
326
+
327
+ ### builder
328
+
329
+ You can specify the builder name if the attribute is lazy, or you can specity one lambda. If true, the default name of the builder will be "builder_#{attribute_name}". This attribute will be ignored if the attribute is not lazy.
330
+
331
+ ```ruby
332
+ class Foo
333
+ include MooseX
334
+
335
+ has x: {
336
+ is: :rw,
337
+ lazy: :true,
338
+ builder: lambda{ |foo| Some::Class.new } # you can ignore foo, or use it!
275
339
  }
340
+ end
341
+ ```
342
+ Optional.
343
+
344
+ ## BUILD
276
345
 
277
- def build_something
278
- 1024
346
+ If you need run some code after the creation of the object (like some extra validation), you should override the BUILD method.
347
+
348
+ ```ruby
349
+ class BuildExample
350
+ include MooseX
351
+
352
+ has [:x, :y], {
353
+ is: :rw,
354
+ required: true,
355
+ }
356
+ def BUILD
357
+ if self.x == self.y
358
+ raise "invalid: you should use x != y"
359
+ end
279
360
  end
361
+ end
362
+
363
+ b1 = BuildExample.new(x: 1, y: 2) # will create the object
364
+ b2 = BuildExample.new(x: 1, y: 1) # will raise the exception!
365
+ ```ruby
280
366
 
281
- private
282
- def my_build_other_thing
283
- 128
367
+ ### BUILDARGS
368
+
369
+ If you need manupulate the constructor argument list, you should implement the method BUILDARGS. You MUST return one Hash of attribute_name => value.
370
+
371
+ ```ruby
372
+ class BuildArgsExample2
373
+ include MooseX
374
+
375
+ has [:x, :y], {
376
+ is: :rw,
377
+ required: true,
378
+ }
379
+
380
+ def BUILDARGS(x=4,y=8)
381
+ args = {}
382
+ args[:x] = x
383
+ args[:y] = y
384
+
385
+ args
284
386
  end
285
387
  end
388
+
389
+ ex1 = BuildArgsExample2.new(1,2) # x == 1, y == 2
390
+ ex2 = BuildArgsExample2.new(1) # x == 1, y == 8
391
+ ex3 = BuildArgsExample2.new() # x == 4, y == 8
286
392
  ```
287
393
 
394
+ ## IMPORTANT
395
+
396
+ This module is experimental. I should test more and more to be possible consider this "production ready". If you find some issue/bug please add here: https://github.com/peczenyj/MooseX/issues
397
+
398
+ Until the first 0.1 version I can change anything without warning.
399
+
400
+ I am open to suggestions too.
401
+
288
402
  ## TODO
289
403
 
290
- 1. Support to lazy attributes [done]
291
- 2. Support to BUILD and BUILDARGS hook
292
- 3. Support to Roles ( it is a Module on Steroids )
293
- 4. Support to after/before/around
294
- 5. Improve the typecheck system (we should specify: we need an array of positive integers)
295
- 6. Improve the exception and warning system
296
- 7. Profit!
404
+ 1. Support to Roles ( it is a Module on Steroids )
405
+ 2. Support to after/before/around
406
+ 3. Improve the typecheck system (we should specify: we need an array of positive integers)
407
+ 4. Improve the exception and warning system
408
+ 5. Profit!
297
409
 
298
410
  ## Limitations
299
411