moosex 0.0.2 → 0.0.3

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: 853ceb9084c1f328f72ef7a1c8c5ccfac1047afd
4
- data.tar.gz: 4577ed6c0db01b29bc735e7e1dfcdf9ffdffb001
3
+ metadata.gz: 14b97eb6c1912038ced4647e89e4ebe2c10092d7
4
+ data.tar.gz: 1d1bcca3b65ea98b49b04bde2769edc5f2ffe676
5
5
  SHA512:
6
- metadata.gz: 61ae609b6b0c19053e78941b50991e5f88255a4bcf713d443fa48cb171ddbdd4710ea8e592e673eadd163730b97e41be4f79b9bed6bb85b5e651041074b1109c
7
- data.tar.gz: 57c88e944b04b96657f6f7cc07fbc7f5001fc81b33a6584025efc57dba08b16448f019d74c50689e13e05440d501fc67b6eebd303141cfeaa1cc9b8dd036b8ba
6
+ metadata.gz: 85aa1e9641e90f307dc37c29c4dbb8ba1b68dcf7784d7ab688638ac57351e790fc37f6459ebb6ca578dec571952c18f748aaef687159b98f62bc689750d2f672
7
+ data.tar.gz: 46c1e856c2c6f53d56e549ce5e7fb9a12158c1a6bb0019c0ecfa1b3a2b174db47ce02ba29f3dcdf18dbaf13c6cfe5d9016b2ed1cf52a41b6d4d0e46d19aad4bd
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2-p320
data/Changelog CHANGED
@@ -1,3 +1,9 @@
1
+ 0.0.3 - 2014-01-31
2
+ - add support to clearer #26
3
+ - add support to predicate #11
4
+ - add support to handle more than one attribute #2
5
+ - small improvements
6
+
1
7
  0.0.2 - 2014-01-31
2
8
  - fix one important bug, not it is possible use in more than one class
3
9
  - add require option
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MooseX
2
2
 
3
- A postmodern object system for Ruby
3
+ A postmodern object system for Ruby [![Build Status](https://travis-ci.org/peczenyj/MooseX.png)](https://travis-ci.org/peczenyj/MooseX)
4
4
 
5
5
  ```ruby
6
6
  require 'moosex'
@@ -32,7 +32,7 @@ A postmodern object system for Ruby
32
32
  has :bar, {
33
33
  :is => :rwp, # read-write-private (private setter)
34
34
  :isa => Integer,
35
- :required => true # you should require in the constructor
35
+ :required => true, # you should require in the constructor
36
36
  }
37
37
  end
38
38
 
@@ -44,11 +44,29 @@ A postmodern object system for Ruby
44
44
  :isa => lambda {|x| # you should add your own validator
45
45
  raise 'x should be less than 100' if x > 100
46
46
  },
47
- :required => true
47
+ :required => true,
48
+ :predicate => true, # add has_bam? method, ask if the attribute is unset
49
+ :clearer => true, # add reset_bam! method, unset the attribute
48
50
  }
49
51
 
50
52
  end
51
53
 
54
+ class Lol
55
+ include MooseX
56
+
57
+ has [:a, :b], { # define attributes a and b
58
+ :is => :ro, # with same set of properties
59
+ :default => 0,
60
+ }
61
+
62
+ has :c => { # alternative syntax to be
63
+ :is => :ro, # more similar to Moo/Moose
64
+ :default => 1,
65
+ :predicate => :can_haz_c?, # custom predicate
66
+ :clearer => "desintegrate_c", # force coerce to symbol
67
+ }
68
+ end
69
+
52
70
  # now you have a generic constructor
53
71
  p1 = Point.new # x and y will be 0
54
72
  p2 = Point.new( :x => 5 ) # y will be 0
@@ -0,0 +1,3 @@
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new(:spec)
3
+ task :default => :spec
@@ -12,14 +12,9 @@ module MooseX
12
12
  o.extend(MooseX::Core)
13
13
 
14
14
  o.class_exec do
15
- meta = MooseX::Meta.new()
16
-
17
- #class_variable_set "@@meta".to_sym, meta
15
+ meta = MooseX::Meta.new
18
16
 
19
- define_singleton_method :__meta do
20
- meta
21
- # class_variable_get "@@meta".to_sym
22
- end
17
+ define_singleton_method(:__meta) { meta }
23
18
  end
24
19
 
25
20
  def initialize(args={})
@@ -32,56 +27,175 @@ module MooseX
32
27
 
33
28
  module Core
34
29
 
35
- def has(attr_name, attr_options)
36
- attr = MooseX::Attribute.new(attr_name, attr_options)
30
+ def has(attr_name, attr_options = {})
31
+ if attr_name.is_a? Array
32
+ attr_name.each do |attr|
33
+ has(attr, attr_options)
34
+ end
35
+ elsif attr_name.is_a? Hash
36
+ attr_name.each_pair do |attr, options |
37
+ has(attr, options)
38
+ end
39
+ else
37
40
 
38
- g = attr.generate_getter
39
-
40
- define_method attr_name, &g
41
-
42
- s = attr.generate_setter
43
-
44
- if attr_options[:is].eql? :rw
45
-
46
- define_method "#{attr_name}=", &s
47
-
48
- elsif attr_options[:is].eql? :rwp
41
+ attr = MooseX::Attribute.new(attr_name, attr_options)
49
42
 
50
- define_method "#{attr_name}=", &s
43
+ g = attr.generate_getter
44
+
45
+ define_method attr.attr_symbol, &g
46
+
47
+ s = attr.generate_setter
48
+
49
+ case attr.is
50
+ when :rw
51
+ define_method "#{attr.attr_symbol}=", &s
51
52
 
52
- private "#{attr_name}="
53
+ when :rwp
54
+ define_method "#{attr.attr_symbol}=", &s
55
+
56
+ private "#{attr.attr_symbol}="
57
+ end
53
58
 
59
+ __meta.add(attr)
54
60
  end
55
-
56
- __meta.add(attr)
57
61
  end
58
-
59
62
  end
60
63
 
61
64
  class Attribute
62
65
 
66
+ attr_reader :attr_symbol, :is, :isa, :default, :required
67
+
68
+ DEFAULTS= {
69
+ :clearer => false,
70
+ :required => false,
71
+ :predicate => false,
72
+ :isa => lambda { |x| true },
73
+ }
74
+
75
+ REQUIRED = [ :is ]
76
+
77
+ VALIDATE = {
78
+ :is => lambda do |is, field_name|
79
+ unless [:rw, :rwp, :ro, :lazy].include?(is)
80
+ raise "invalid value for field '#{field_name}' is '#{is}', must be one of :rw, :rwp, :ro or :lazy"
81
+ end
82
+ end,
83
+ };
84
+
85
+ COERCE = {
86
+ :is => lambda do |is, field_name|
87
+ is.to_sym
88
+ end,
89
+ :isa => lambda do |isa, field_name|
90
+ return isa if isa.is_a? Proc
91
+
92
+ return lambda do |new_value|
93
+ unless new_value.is_a?(isa)
94
+ raise "isa check for \"#{field_name}\" failed: is not instance of #{isa}!"
95
+ end
96
+ end
97
+ end,
98
+ :default => lambda do |default, field_name|
99
+ return default if default.is_a? Proc
100
+
101
+ return lambda { default }
102
+ end,
103
+ :required => lambda do |required, field_name|
104
+ !!required
105
+ end,
106
+ :predicate => lambda do |predicate, field_name|
107
+ begin
108
+ if ! predicate
109
+ return false
110
+ elsif predicate.is_a? TrueClass
111
+ return "has_#{field_name}?".to_sym,
112
+ end
113
+
114
+ return predicate.to_sym
115
+ rescue e
116
+ # create a nested exception here
117
+ raise "cannot coerce field predicate to a symbol for #{field_name}: #{e}"
118
+ end
119
+ end,
120
+ :clearer => lambda do |clearer, field_name|
121
+ begin
122
+ if ! clearer
123
+ return false
124
+ elsif clearer.is_a? TrueClass
125
+ return "reset_#{field_name}!".to_sym,
126
+ end
127
+
128
+ return clearer.to_sym
129
+ rescue e
130
+ # create a nested exception here
131
+ raise "cannot coerce field clearer to a symbol for #{field_name}: #{e}"
132
+ end
133
+ end,
134
+ };
135
+
63
136
  def initialize(a, o)
137
+ # todo extract this to a framework, see issue #21 on facebook
138
+ o = DEFAULTS.merge(o)
139
+
140
+ REQUIRED.each { |field|
141
+ unless o.has_key?(field)
142
+ raise "field #{field} is required for Attribute #{a}"
143
+ end
144
+ }
145
+ COERCE.each_pair do |field, coerce|
146
+ if o.has_key? field
147
+ o[field] = coerce.call(o[field], a)
148
+ end
149
+ end
150
+ VALIDATE.each_pair do |field, validate|
151
+ return if ! o.has_key? field
152
+
153
+ validate.call(o[field], a)
154
+ end
155
+
64
156
  @attr_symbol = a
65
- @options = o
157
+ @is = o[:is]
158
+ @isa = o[:isa]
159
+ @default = o[:default]
160
+ @required = o[:required]
161
+ @predicate = o[:predicate]
162
+ @clearer = o[:clearer]
66
163
  end
67
164
 
68
165
  def init(object, args)
166
+ inst_variable_name = "@#{@attr_symbol}".to_sym
167
+
69
168
  setter = @attr_symbol.to_s.concat("=").to_sym
70
169
  value = nil
71
-
170
+
171
+ if @predicate
172
+ object.define_singleton_method @predicate do
173
+ instance_variable_defined? inst_variable_name
174
+ end
175
+ end
176
+
177
+ if @clearer
178
+ object.define_singleton_method @clearer do
179
+ if instance_variable_defined? inst_variable_name
180
+ remove_instance_variable inst_variable_name
181
+ end
182
+ end
183
+ end
184
+
72
185
  if args.has_key? @attr_symbol
73
186
  value = args[ @attr_symbol ]
74
- elsif @options[:required]
187
+ elsif @required
75
188
  raise "attr \"#{@attr_symbol}\" is required"
189
+ elsif @default
190
+ value = @default.call
76
191
  else
77
- value = (@options[:default].is_a? Proc) ? @options[:default].call : @options[:default]
192
+ return
78
193
  end
79
194
 
80
- if @options[:is].eql? :ro
195
+ if @is.eql? :ro
81
196
 
82
197
  # TODO: remove redundancy
83
198
 
84
- inst_variable_name = "@#{@attr_symbol}".to_sym
85
199
  type_check = generate_type_check
86
200
  type_check.call(value)
87
201
  object.instance_variable_set inst_variable_name, value
@@ -108,17 +222,8 @@ module MooseX
108
222
  end
109
223
 
110
224
  def generate_type_check
111
- if @options.has_key? :isa
112
- isa = @options[:isa]
113
-
114
- return isa if isa.is_a? Proc
115
225
 
116
- return lambda do |new_value|
117
- raise "isa check for \"#{@attr_symbol}\" failed: lol is not #{isa}!" unless new_value.is_a? isa
118
- end
119
- end
120
-
121
- lambda { |new_value| }
226
+ return @isa
122
227
  end
123
228
  end
124
229
 
@@ -1,3 +1,3 @@
1
1
  module Moosex
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -37,13 +37,38 @@ class Baz
37
37
 
38
38
  has :bam, {
39
39
  :is => :ro,
40
- :isa => lambda {|x| raise 'x should be less than 100' if x > 100},
40
+ :isa => lambda {|bam| raise 'bam should be less than 100' if bam > 100},
41
41
  :required => true
42
42
  }
43
+ has :boom, {
44
+ :is => :rw,
45
+ :predicate => true,
46
+ :clearer => true,
47
+ }
48
+ end
49
+
50
+ class Lol
51
+ include MooseX
52
+
53
+ has [:a, :b], {
54
+ :is => :ro,
55
+ :default => 0,
56
+ }
57
+
58
+ has :c => {
59
+ :is => :ro,
60
+ :default => 1,
61
+ :predicate => :has_option_c?,
62
+ :clearer => "reset_option_c", # force coerce
63
+ }
43
64
 
65
+ has [:d, :e] => {
66
+ :is => "ro",
67
+ :default => 2,
68
+ }
44
69
  end
45
70
 
46
- describe "MooseX" do
71
+ describe "Point" do
47
72
  describe "should has an intelligent constructor" do
48
73
  it "without arguments, should initialize with default values" do
49
74
  p = Point.new
@@ -62,42 +87,6 @@ describe "MooseX" do
62
87
  p.x.should == 5
63
88
  p.y.should == 4
64
89
  end
65
-
66
- it "should require bar if necessary" do
67
- expect {
68
- Foo.new
69
- }.to raise_error("attr \"bar\" is required")
70
- end
71
-
72
- it "should require bar if necessary" do
73
- foo = Foo.new( :bar => 123 )
74
- foo.bar.should == 123
75
- end
76
-
77
- it "should not be possible update bar (setter private)" do
78
- foo = Foo.new( :bar => 123 )
79
- expect {
80
- foo.bar = 1024
81
- }.to raise_error(NoMethodError)
82
- end
83
-
84
- it "should require bam if necessary" do
85
- baz = Baz.new( :bam => 99 )
86
- baz.bam.should == 99
87
- end
88
-
89
- it "should not be possible update baz (read only)" do
90
- baz = Baz.new( :bam => 99 )
91
- expect {
92
- baz.bam = 1024
93
- }.to raise_error(NoMethodError)
94
- end
95
-
96
- it "should run the lambda isa" do
97
- expect {
98
- baz = Baz.new( :bam => 199 )
99
- }.to raise_error(/x should be less than 100/)
100
- end
101
90
  end
102
91
 
103
92
  describe "should create a getter and a setter" do
@@ -111,13 +100,13 @@ describe "MooseX" do
111
100
  p = Point.new
112
101
  expect {
113
102
  p.x = "lol"
114
- }.to raise_error('isa check for "x" failed: lol is not Integer!')
103
+ }.to raise_error('isa check for "x" failed: is not instance of Integer!')
115
104
  end
116
105
 
117
106
  it "for x, with type check" do
118
107
  expect {
119
108
  Point.new(:x => "lol")
120
- }.to raise_error('isa check for "x" failed: lol is not Integer!')
109
+ }.to raise_error('isa check for "x" failed: is not instance of Integer!')
121
110
  end
122
111
 
123
112
  it "clear should clean attributes" do
@@ -127,4 +116,95 @@ describe "MooseX" do
127
116
  p.y.should be_zero
128
117
  end
129
118
  end
119
+ end
120
+
121
+ describe "Foo" do
122
+ it "should require bar if necessary" do
123
+ expect {
124
+ Foo.new
125
+ }.to raise_error("attr \"bar\" is required")
126
+ end
127
+
128
+ it "should require bar if necessary" do
129
+ foo = Foo.new( :bar => 123 )
130
+ foo.bar.should == 123
131
+ end
132
+
133
+ it "should not be possible update bar (setter private)" do
134
+ foo = Foo.new( :bar => 123 )
135
+ expect {
136
+ foo.bar = 1024
137
+ }.to raise_error(NoMethodError)
138
+ end
139
+ end
140
+
141
+ describe "Baz" do
142
+ it "should require bam if necessary" do
143
+ baz = Baz.new( :bam => 99 )
144
+ baz.bam.should == 99
145
+ end
146
+
147
+ it "should not be possible update baz (read only)" do
148
+ baz = Baz.new( :bam => 99 )
149
+ expect {
150
+ baz.bam = 1024
151
+ }.to raise_error(NoMethodError)
152
+ end
153
+
154
+ it "should run the lambda isa" do
155
+ expect {
156
+ Baz.new( :bam => 199 )
157
+ }.to raise_error(/bam should be less than 100/)
158
+ end
159
+
160
+ it "rw acessor should has nil value, supports predicate" do
161
+ baz = Baz.new( :bam => 99 )
162
+
163
+ baz.has_boom?.should be_false
164
+ baz.boom.should be_nil
165
+ baz.boom= 0
166
+ baz.has_boom?.should be_true
167
+ baz.boom.should be_zero
168
+ end
169
+
170
+ it "rw acessor should has nil value, supports clearer" do
171
+ baz = Baz.new( :bam => 99, :boom => 0 )
172
+
173
+ baz.has_boom?.should be_true
174
+ baz.boom.should be_zero
175
+
176
+ baz.reset_boom!
177
+
178
+ baz.has_boom?.should be_false
179
+ baz.boom.should be_nil
180
+ end
181
+
182
+ it "should be possible call the clearer twice" do
183
+ baz = Baz.new( :bam => 99, :boom => 0 )
184
+
185
+ baz.reset_boom!
186
+ baz.reset_boom!
187
+
188
+ baz.has_boom?.should be_false
189
+ baz.boom.should be_nil
190
+ end
191
+ end
192
+
193
+ describe "Lol" do
194
+ it "Lol should has five arguments" do
195
+ lol = Lol.new(:a => 5, :d => -1)
196
+ lol.a.should == 5
197
+ lol.b.should be_zero
198
+ lol.c.should == 1
199
+ lol.d.should == -1
200
+ lol.e.should == 2
201
+ end
202
+
203
+ it "Lol should support custom predicate and clearer" do
204
+ lol = Lol.new(:a => 5, :d => -1)
205
+
206
+ lol.has_option_c?.should be_true
207
+ lol.reset_option_c
208
+ lol.has_option_c?.should be_false
209
+ end
130
210
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moosex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Peczenyj
@@ -63,11 +63,13 @@ extensions: []
63
63
  extra_rdoc_files: []
64
64
  files:
65
65
  - .gitignore
66
+ - .travis.yml
66
67
  - Changelog
67
68
  - Gemfile
68
69
  - Gemfile.lock
69
70
  - LICENSE
70
71
  - README.md
72
+ - Rakefile
71
73
  - lib/moosex.rb
72
74
  - lib/moosex/version.rb
73
75
  - moosex.gemspec