maintain 0.2.21 → 0.2.22

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