maintain 0.2.21 → 0.2.22

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,13 @@
1
+ 0.2.22
2
+ * Added `bang!` method support, so now you can call `@object.awesome!`
3
+ and its state will be set to "awesome." Like all Maintain methods,
4
+ I'm saying f*ck you to convention and letting you go nuts; you can
5
+ achieve the same effect one of three ways:
6
+
7
+ @object.awesome!
8
+ @object.state.awesome!
9
+ @object.state_awesome!
10
+
1
11
  0.2.21
2
12
  * Added Enumerable support to bitmask values, so now you can parse
3
13
  through flags with each, select, map, find, and more! This also
data/README.markdown CHANGED
@@ -19,88 +19,109 @@ Basic Usage
19
19
  **Maintain** is pretty straightforward to use. First, you have to tell a Ruby object to maintain
20
20
  state on an attribute:
21
21
 
22
- class Foo
23
- extend Maintain
24
- maintains :state do
25
- state :new, :default => true
26
- state :old
27
- end
28
- end
22
+ ```ruby
23
+ class Foo
24
+ extend Maintain
25
+ maintains :state do
26
+ state :new, :default => true
27
+ state :old
28
+ end
29
+ end
30
+ ```
29
31
 
30
32
  That's it for basic state maintenance! Check it out:
31
33
 
32
- foo = Foo.new
33
- foo.state #=> :new
34
- foo.new? #=> true
35
- foo.state = :old
36
- foo.old? #=> true
34
+ ```ruby
35
+ foo = Foo.new
36
+ foo.state #=> :new
37
+ foo.new? #=> true
38
+ foo.state = :old
39
+ foo.old? #=> true
40
+ ```
37
41
 
38
42
  But wait! What if you've already defined "new?" on the Foo class? Not to worry, Maintain won't step on your toes. Just use:
39
43
 
40
44
  foo.state.new?
41
45
 
42
- **UPDATE:** what happens when you *want* Maintain to step on your toes? You can add an optionally add:
46
+ And when you *want* Maintain to step on your toes? You can add an optionally add:
43
47
 
44
48
  state :new, :force => true
45
49
 
46
50
  ...and Maintain will make sure your methods get added, even if it overwrites a previous method.
47
51
 
52
+ **UPDATE: Maintain** now supports `bang!` style methods for declaring a state imperatively. It's as simple as calling
53
+
54
+ ```ruby
55
+ foo = Foo.new
56
+ foo.old!
57
+ foo.state #=> :old
58
+ ```
59
+
48
60
  Comparisons
49
61
  -
50
62
 
51
- **Maintain** provides quick and easy comparisons between states. You can specify integer values of states to compare on,
52
- or you can just let it infer what it wants. From our example above:
63
+ **Maintain** provides quick and easy comparisons between states. By default, it uses the order in which you add states to
64
+ rank them. From our example above:
53
65
 
54
- foo.state = :new
55
- foo.state > :old #=> false
56
- foo.state <= :old #=> true
66
+ ```ruby
67
+ foo.state = :new
68
+ foo.state > :old #=> false
69
+ foo.state < :old #=> true
70
+ ```
57
71
 
58
- You could also do:
72
+ As an optional second argument to `state`, you can specify a comparison value. This will allow you to define states in any
73
+ order you want:
59
74
 
60
- class Foo
61
- extend Maintain
62
- maintains :state do
63
- state :new, 12, :default => true
64
- state :old, 5
65
- end
66
- end
75
+ ```ruby
76
+ class Foo
77
+ extend Maintain
78
+ maintains :state do
79
+ state :new, 12, :default => true
80
+ state :old, 5
81
+ end
82
+ end
67
83
 
68
- Foo.new.state > old #=> true
84
+ Foo.new.state > old #=> true
85
+ ```
69
86
 
70
87
  Hooks
71
88
  -
72
89
 
73
90
  **Maintain** can hook into state entry and exit, and provides a number of mechanisms for doing so:
74
91
 
75
- class Foo < ActiveRecord::Base
76
- maintains :state do
77
- state :active, :enter => :activated
78
- state :inactive, :exit => lambda { self.bar.baz! }
79
- end
80
-
81
- def activated
82
- puts "I'm alive!"
83
- end
84
- end
92
+ ```ruby
93
+ class Foo < ActiveRecord::Base
94
+ maintains :state do
95
+ state :active, :enter => :activated
96
+ state :inactive, :exit => lambda { self.bar.baz! }
97
+ end
98
+
99
+ def activated
100
+ puts "I'm alive!"
101
+ end
102
+ end
103
+ ```
85
104
 
86
105
  Of course, maybe that's not your style. Why not try this?
87
106
 
88
- class Foo
89
- extend Maintain
90
- maintains :state do
91
- state :active
92
- state :inactive
107
+ ```ruby
108
+ class Foo
109
+ extend Maintain
110
+ maintains :state do
111
+ state :active
112
+ state :inactive
93
113
 
94
- on :enter, :active, :activated
95
- on :exit, :inactive do
96
- bar.baz!
97
- end
98
- end
114
+ on :enter, :active, :activated
115
+ on :exit, :inactive do
116
+ bar.baz!
117
+ end
118
+ end
99
119
 
100
- def activated
101
- puts "I'm alive!"
102
- end
103
- end
120
+ def activated
121
+ puts "I'm alive!"
122
+ end
123
+ end
124
+ ```
104
125
 
105
126
 
106
127
  Aggregates
@@ -109,21 +130,23 @@ Aggregates
109
130
  What about when a group of states is needed? Yeah, you could write `foo.bar? || foo.baz?`. You could even make that a method!
110
131
  But why not just add the following?
111
132
 
112
- class Foo
113
- extend Maintain
114
- maintains :state do
115
- state :new
116
- state :old
117
- state :borrowed
118
- state :blue
119
-
120
- aggregate :starts_with_b, [:borrowed, :blue]
121
- end
122
- end
123
-
124
- foo = Foo.new
125
- foo.status = :borrowed
126
- foo.starts_with_b? #=> true
133
+ ```ruby
134
+ class Foo
135
+ extend Maintain
136
+ maintains :state do
137
+ state :new
138
+ state :old
139
+ state :borrowed
140
+ state :blue
141
+
142
+ aggregate :starts_with_b, [:borrowed, :blue]
143
+ end
144
+ end
145
+
146
+ foo = Foo.new
147
+ foo.status = :borrowed
148
+ foo.starts_with_b? #=> true
149
+ ```
127
150
 
128
151
  Bitmasking
129
152
  -
@@ -131,48 +154,52 @@ Bitmasking
131
154
  Sometimes you need to store a simple combination of values. Sure, you could add individual columns for each value to your
132
155
  relational database - or you could implement a single bitmask column:
133
156
 
134
- class Foo
135
- extend Maintain
136
- maintains :state, :bitmask => true do
137
- # NOTE: Maintain will try to infer a bitmask value if you do not provide an integer here,
138
- # but if you don't -- and you re-order your state calls later -- all stored bitmasks will
139
- # be invalidated. You have been warned.
140
- state :new, 1
141
- state :old, 2
142
- state :borrowed, 3
143
- state :blue, 4
144
- end
145
- end
146
-
147
- foo = Foo.new
148
- foo.state #=> nil
149
- foo.state = [:new, :borrowed]
150
- foo.state #=> [:new, :borrowed]
151
- foo.new? #=> true
152
- foo.borrowed? #=> true
153
- foo.blue? #=> false
154
- foo.blue!
155
- foo.blue? #=> true
156
-
157
- # foo.state will boil happily down to an integer when you store it.
158
-
159
- You can also set multiple defaults on bitmasks, just in case you're defaults involve some complicated mix of options:
160
-
161
- class Foo
162
- extend Maintain
163
- maintains :state, :bitmask => true do
164
- state :new, 1, :default => true
165
- state :old, 2
166
- state :borrowed, 3, :default => true
167
- state :blue, 4
168
- end
157
+ ```ruby
158
+ class Foo
159
+ extend Maintain
160
+ maintains :state, :bitmask => true do
161
+ # NOTE: Maintain will try to infer a bitmask value if you do not provide an integer here,
162
+ # but if you don't -- and you re-order your state calls later -- all stored bitmasks will
163
+ # be invalidated. You have been warned.
164
+ state :new, 1
165
+ state :old, 2
166
+ state :borrowed, 3
167
+ state :blue, 4
169
168
  end
170
-
171
- foo = Foo.new
172
- foo.new? #=> true
173
- foo.old? #=> false
174
- foo.borrowed? #=> true
175
- foo.blue? #=> false
169
+ end
170
+
171
+ foo = Foo.new
172
+ foo.state #=> nil
173
+ foo.state = [:new, :borrowed]
174
+ foo.state #=> [:new, :borrowed]
175
+ foo.new? #=> true
176
+ foo.borrowed? #=> true
177
+ foo.blue? #=> false
178
+ foo.blue!
179
+ foo.blue? #=> true
180
+
181
+ # foo.state will boil happily down to an integer when you store it.
182
+ ```
183
+
184
+ You can also set multiple defaults on bitmasks, just in case your defaults involve some complicated mix of options:
185
+
186
+ ```ruby
187
+ class Foo
188
+ extend Maintain
189
+ maintains :state, :bitmask => true do
190
+ state :new, 1, :default => true
191
+ state :old, 2
192
+ state :borrowed, 3, :default => true
193
+ state :blue, 4
194
+ end
195
+ end
196
+
197
+ foo = Foo.new
198
+ foo.new? #=> true
199
+ foo.old? #=> false
200
+ foo.borrowed? #=> true
201
+ foo.blue? #=> false
202
+ ```
176
203
 
177
204
  Named Scopes
178
205
  -
@@ -180,12 +207,14 @@ Named Scopes
180
207
  **Maintain** knows all about ActiveRecord - it even extends ActiveRecord::Base by default. So it stands to reason that adding states
181
208
  and aggregates will automatically create named scopes on ActiveRecord::Base subclasses for those states! Check it:
182
209
 
183
- class Foo < ActiveRecord::Base
184
- maintains :state do
185
- state :active
186
- state :inactive
187
- end
188
- end
189
-
190
- Foo.active #=> []
191
- Foo.inactive #=> []
210
+ ```ruby
211
+ class Foo < ActiveRecord::Base
212
+ maintains :state do
213
+ state :active
214
+ state :inactive
215
+ end
216
+ end
217
+
218
+ Foo.active #=> []
219
+ Foo.inactive #=> []
220
+ ```
data/Rakefile CHANGED
@@ -1,25 +1,5 @@
1
1
  require 'rake'
2
2
 
3
- task :default => :spec
4
-
5
- begin
6
- require 'spec/rake/spectask'
7
-
8
- desc "Run all examples"
9
- Spec::Rake::SpecTask.new('spec') do |t|
10
- t.spec_files = FileList['spec/**/*.rb']
11
- end
12
-
13
- desc "Run all examples with RCov"
14
- Spec::Rake::SpecTask.new('spec:rcov') do |t|
15
- t.spec_files = FileList['spec/**/*.rb']
16
- t.rcov = true
17
- t.rcov_opts = ['--exclude', 'spec,gem']
18
- end
19
- rescue LoadError
20
- puts "Could not load Rspec. To run tests, use `gem install rspec`"
21
- end
22
-
23
3
  begin
24
4
  require 'jeweler'
25
5
  Jeweler::Tasks.new do |gemspec|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.21
1
+ 0.2.22
data/autotest/discover.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'autotest/fsevent'
2
- require 'autotest/growl'
3
2
 
4
3
  Autotest.add_discovery { "rspec2" }
5
4
 
@@ -5,20 +5,24 @@ module Maintain
5
5
  class << self
6
6
  def add(name, owner)
7
7
  classes[name.to_sym] = owner
8
+ # Dig through the constant name to find if it exists
8
9
  modules = owner.split('::')
9
10
  if Object.const_defined?(modules.first) && owner = Object.const_get(modules.shift)
10
11
  while modules.length > 0
11
12
  owner = owner.const_get(modules.shift)
12
13
  end
13
- if owner.is_a? Module
14
- owner.class_eval do
15
- class << self
16
- include Maintain
17
- end
18
- end
19
- else
20
- owner.extend Maintain
21
- end
14
+ # If it exists, extend it with Maintain methods automatically
15
+ owner.extend Maintain
16
+ # TODO: Try and remember why I did this
17
+ # if owner.is_a? Module
18
+ # owner.class_eval do
19
+ # class << self
20
+ # include Maintain
21
+ # end
22
+ # end
23
+ # else
24
+ # owner.extend Maintain
25
+ # end
22
26
  end
23
27
  end
24
28
 
@@ -24,7 +24,7 @@ module Maintain
24
24
  end
25
25
  # Now define the state
26
26
  if back_end
27
- back_end.aggregate(maintainee, name, @attribute, conditions.map{|value| states[value][:value].is_a?(Symbol) ? states[value][:value].to_s : states[value][:value] }, {:force => options[:force]})
27
+ back_end.aggregate(maintainee, name, @attribute, conditions.select{|value| states.has_key?(value) }.map{|value| states[value][:value].is_a?(Symbol) ? states[value][:value].to_s : states[value][:value] }.compact, {:force => options[:force]})
28
28
  end
29
29
  end
30
30
 
@@ -157,6 +157,17 @@ module Maintain
157
157
  end
158
158
  #{"alias :#{boolean_method} :#{@attribute}_#{boolean_method}" if method_free?(boolean_method) || options[:force]}
159
159
  EOC
160
+
161
+ # Last but not least, add bang methods to automatically convert to state. Like boolean
162
+ # methods above, these only get added if they're not already things that are things.
163
+ bang_method = "#{name}!"
164
+ # Override any attribute_state! methods
165
+ maintainee.class_eval <<-EOC
166
+ def #{@attribute}_#{bang_method}
167
+ #{@attribute}.#{bang_method}
168
+ end
169
+ #{"alias :#{bang_method} :#{@attribute}_#{bang_method}" if method_free?(bang_method) || options[:force]}
170
+ EOC
160
171
  end
161
172
 
162
173
  def states
@@ -78,20 +78,31 @@ module Maintain
78
78
 
79
79
  # TODO: Sweet god, this is hideous and needs to be cleaned up!
80
80
  def method_missing(method, *args)
81
- if (method.to_s =~ /^(.+)\?$/)
82
- check = $1.to_sym
83
- if @state.states.has_key?(check)
81
+ if (method.to_s =~ /^(.+)(\?|\!)$/)
82
+ value_name = $1.to_sym
83
+ if @state.states.has_key?(value_name)
84
+ case $2
85
+ when '?'
86
+ self.class.class_eval <<-EOC
87
+ def #{method}
88
+ self == #{value_name.inspect}
89
+ end
90
+ EOC
91
+ # Calling `method` on ourselves fails. Something to do w/subclasses. Meh.
92
+ return self == value_name
93
+ when '!'
94
+ self.class.class_eval <<-EOC
95
+ def #{method}
96
+ self.set_value(#{value_name.inspect})
97
+ end
98
+ EOC
99
+ # Calling `method` on ourselves fails. Something to do w/subclasses. Meh.
100
+ return self.set_value(value_name)
101
+ end
102
+ elsif $2 == '?' && aggregates = @state.aggregates[value_name]
84
103
  self.class.class_eval <<-EOC
85
104
  def #{method}
86
- self == #{check.inspect}
87
- end
88
- EOC
89
- # Calling `method` on ourselves fails. Something to do w/subclasses. Meh.
90
- return self == check.to_sym
91
- elsif aggregates = @state.aggregates[check]
92
- self.class.class_eval <<-EOC
93
- def #{method}
94
- @state.aggregates[#{check.inspect}].include?(@value)
105
+ @state.aggregates[#{value_name.inspect}].include?(@value)
95
106
  end
96
107
  EOC
97
108
  return aggregates.include?(@value)
@@ -6,7 +6,10 @@ begin
6
6
  require 'rubygems'
7
7
  gem 'activerecord', '>= 2.3.5'
8
8
  require 'active_record'
9
+ require 'logger'
9
10
  proceed = true
11
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
12
+ ActiveRecord::Base.logger.level = Logger::Severity::UNKNOWN
10
13
  rescue Gem::LoadError, LoadError
11
14
  puts 'Not testing ActiveRecord (unavailable)'
12
15
  end
@@ -33,6 +33,15 @@ describe Maintain do
33
33
  @maintainer.state = 'nada'
34
34
  @maintainer.state.should be_nil
35
35
  end
36
+
37
+ it "should support a `state!` bang method, too!" do
38
+ @maintainer.new!
39
+ @maintainer.state.should == :new
40
+ @maintainer.overdue!
41
+ @maintainer.state.should == :overdue
42
+ @maintainer.closed!
43
+ @maintainer.state.should == :closed
44
+ end
36
45
  end
37
46
 
38
47
  describe "integer states" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.21
4
+ version: 0.2.22
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-20 00:00:00.000000000 Z
12
+ date: 2012-08-10 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! "\n Maintain is a simple state machine mixin for Ruby objects.
15
15
  It supports comparisons, bitmasks,\n and hooks that really work. It can be
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  version: '0'
74
74
  requirements: []
75
75
  rubyforge_project:
76
- rubygems_version: 1.8.10
76
+ rubygems_version: 1.8.24
77
77
  signing_key:
78
78
  specification_version: 3
79
79
  summary: A Ruby state machine that lets your code do the driving