moosex 0.0.2 → 0.0.3

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.
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