maintain 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.2
@@ -7,7 +7,7 @@ module Maintain
7
7
  end
8
8
 
9
9
  def read(instance, attribute)
10
- instance.attributes[attribute.to_s]
10
+ instance.read_attribute(attribute)
11
11
  end
12
12
 
13
13
  def state(maintainee, name, attribute, value)
@@ -0,0 +1,29 @@
1
+ module Maintain
2
+ module Backend
3
+ class DataMapper < Maintain::Backend::Base
4
+ def aggregate(maintainee, name, attribute, states)
5
+ # named_scope will handle the array of states as "IN" in SQL
6
+ state(maintainee, name, attribute, states)
7
+ end
8
+
9
+ def read(instance, attribute)
10
+ instance.attributes[attribute.to_s]
11
+ end
12
+
13
+ def state(maintainee, name, attribute, value)
14
+ conditions = {:conditions => {attribute => value}}
15
+ maintainee.class_eval <<-scope
16
+ def self.#{name}
17
+ all(#{conditions.inspect})
18
+ end
19
+ scope
20
+ end
21
+
22
+ def write(instance, attribute, value)
23
+ property = instance.send(:properties)[attribute]
24
+ instance.persisted_state = instance.persisted_state.set(property, value)
25
+ instance.persisted_state.get(property)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,8 +3,27 @@ require 'maintain/backend/base'
3
3
  module Maintain
4
4
  module Backend
5
5
  class << self
6
+ def add(name, owner)
7
+ classes[name.to_sym] = owner
8
+ modules = owner.split('::')
9
+ if Object.const_defined?(modules.first) && owner = Object.const_get(modules.shift)
10
+ while modules.length > 0
11
+ owner = owner.const_get(modules.shift)
12
+ 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
22
+ end
23
+ end
24
+
6
25
  def build(back_end, maintainer)
7
- back_end = back_end.split('_').map(&:capitalize).join('')
26
+ back_end = back_end.to_s.split('_').map(&:capitalize).join('')
8
27
  if constants.include? back_end.to_s
9
28
  const_get(back_end.to_sym).new
10
29
  else
@@ -16,6 +35,10 @@ module Maintain
16
35
  end
17
36
  end
18
37
 
38
+ def classes
39
+ @classes ||= {}
40
+ end
41
+
19
42
  def const_missing(constant)
20
43
  underscore_constant = constant.to_s.dup
21
44
  underscore_constant.gsub!(/::/, '/')
@@ -30,6 +53,23 @@ module Maintain
30
53
  super
31
54
  end
32
55
  end
56
+
57
+ # Detect if we've loaded a backend for this class - that means if its ancestors or
58
+ # parent classes include any of our back-end classes.
59
+ def detect(owner)
60
+ ancestors = owner.ancestors.map(&:to_s)
61
+ # While owner does not refer to "Object"
62
+ while owner.superclass
63
+ ancestors.push(owner.to_s)
64
+ owner = owner.superclass
65
+ end
66
+ classes.each do |back_end, class_name|
67
+ if ancestors.include? class_name
68
+ return back_end
69
+ end
70
+ end
71
+ nil
72
+ end
33
73
  end
34
74
  end
35
75
  end
data/lib/maintain.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  # encoding: UTF-8
2
2
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__))
3
+ require 'maintain/backend'
3
4
 
4
5
  module Maintain
5
6
  # We're not really interested in loading anything into memory if we don't need to,
6
7
  # so Maintainer, Value, and the Value subclasses are ignored until they're needed.
7
8
  autoload(:Maintainer, 'maintain/maintainer')
8
- autoload(:Backend, 'maintain/backend')
9
9
  autoload(:Value, 'maintain/value')
10
10
  autoload(:BitmaskValue, 'maintain/bitmask_value')
11
11
  autoload(:IntegerValue, 'maintain/integer_value')
@@ -31,17 +31,7 @@ module Maintain
31
31
  # This method is aliased as `maintains` with the intention of allowing developers
32
32
  # to code imperatively ("maintain, damn you!") or descriptively ("it maintains, man")
33
33
  def maintain(attribute, options = {}, &block)
34
- # Detect if this is ActiveRecord::Base or a subclass of it
35
- # TODO: Make this not suck
36
- if defined?(ActiveRecord::Base)
37
- active_record = self == ActiveRecord::Base
38
- superclass = self
39
- while !active_record && superclass.superclass
40
- active_record = superclass == ActiveRecord::Base
41
- superclass = superclass.superclass
42
- end
43
- options[:back_end] = 'active_record' if active_record
44
- end
34
+ options[:back_end] ||= Maintain::Backend.detect(self)
45
35
 
46
36
  # Create an instance of the maintainer class. It handles all of the state
47
37
  # configuration, hooking, aggregation, named_scoping, etc.
@@ -67,22 +57,20 @@ module Maintain
67
57
 
68
58
  # Allow the back end to write values in an ORM-specific way
69
59
  if maintainer.back_end
70
- maintainer.back_end.write(self, :#{attribute}, value)
60
+ maintainer.back_end.write(self, :#{attribute}, #{attribute}.value)
71
61
  end
72
62
 
73
63
  # Last but not least, run the enter hooks for the new value - cause that's how
74
64
  # we do.
75
65
  maintainer.hook(:enter, #{attribute}.name, self) if changed
66
+ else
67
+ @#{attribute} = value
76
68
  end
77
69
  end
78
70
 
79
71
  def #{attribute}
80
72
  if maintainer = self.class.maintainers[:#{attribute}]
81
- if @#{attribute}
82
- @#{attribute}
83
- else
84
- @#{attribute} = maintainer.value(self)
85
- end
73
+ @#{attribute} ||= maintainer.value(self)
86
74
  else
87
75
  @#{attribute}
88
76
  end
@@ -107,6 +95,5 @@ module Maintain
107
95
  end
108
96
  end
109
97
 
110
- if defined?(ActiveRecord::Base)
111
- ActiveRecord::Base.extend Maintain
112
- end
98
+ Maintain::Backend.add(:active_record, 'ActiveRecord::Base')
99
+ Maintain::Backend.add(:data_mapper, 'DataMapper::Resource')
data/maintain.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{maintain}
8
- s.version = "0.2.0"
8
+ s.version = "0.2.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Flip Sasser"]
12
- s.date = %q{2011-02-03}
12
+ s.date = %q{2011-02-23}
13
13
  s.description = %q{
14
14
  Maintain is a simple state machine mixin for Ruby objects. It supports comparisons, bitmasks,
15
15
  and hooks that really work. It can be used for multiple attributes and will always do its best to
@@ -20,30 +20,59 @@ Gem::Specification.new do |s|
20
20
  "README.markdown"
21
21
  ]
22
22
  s.files = [
23
- "Rakefile",
24
- "VERSION",
25
- "lib/maintain.rb",
26
- "lib/maintain/backend.rb",
27
- "lib/maintain/backend/active_record.rb",
28
- "lib/maintain/backend/base.rb",
29
- "lib/maintain/bitmask_value.rb",
30
- "lib/maintain/integer_value.rb",
31
- "lib/maintain/maintainer.rb",
32
- "lib/maintain/value.rb",
33
- "maintain.gemspec"
23
+ ".rspec",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "autotest/discover.rb",
28
+ "lib/maintain.rb",
29
+ "lib/maintain/backend.rb",
30
+ "lib/maintain/backend/active_record.rb",
31
+ "lib/maintain/backend/base.rb",
32
+ "lib/maintain/backend/data_mapper.rb",
33
+ "lib/maintain/bitmask_value.rb",
34
+ "lib/maintain/integer_value.rb",
35
+ "lib/maintain/maintainer.rb",
36
+ "lib/maintain/value.rb",
37
+ "maintain.gemspec",
38
+ "spec/active_record_spec.rb",
39
+ "spec/aggregates_spec.rb",
40
+ "spec/bitwise_spec.rb",
41
+ "spec/comparing_state_spec.rb",
42
+ "spec/data_mapper_spec.rb",
43
+ "spec/defining_states_spec.rb",
44
+ "spec/hooks_spec.rb",
45
+ "spec/integer_spec.rb",
46
+ "spec/maintain_spec.rb",
47
+ "spec/object_spec.rb",
48
+ "spec/proxy_spec.rb",
49
+ "spec/setting_state_spec.rb",
50
+ "spec/spec.opts"
34
51
  ]
35
52
  s.homepage = %q{http://github.com/flipsasser/maintain}
36
- s.rdoc_options = ["--charset=UTF-8"]
37
53
  s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.6}
54
+ s.rubygems_version = %q{1.3.7}
39
55
  s.summary = %q{A Ruby state machine that lets your code do the driving}
40
- s.test_files = Dir["spec/**/*"]
56
+ s.test_files = [
57
+ "spec/active_record_spec.rb",
58
+ "spec/aggregates_spec.rb",
59
+ "spec/bitwise_spec.rb",
60
+ "spec/comparing_state_spec.rb",
61
+ "spec/data_mapper_spec.rb",
62
+ "spec/defining_states_spec.rb",
63
+ "spec/hooks_spec.rb",
64
+ "spec/integer_spec.rb",
65
+ "spec/maintain_spec.rb",
66
+ "spec/object_spec.rb",
67
+ "spec/proxy_spec.rb",
68
+ "spec/setting_state_spec.rb"
69
+ ]
41
70
 
42
71
  if s.respond_to? :specification_version then
43
72
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
73
  s.specification_version = 3
45
74
 
46
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
75
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
76
  else
48
77
  end
49
78
  else
@@ -27,6 +27,7 @@ if proceed
27
27
  ActiveRecord::Schema.define do
28
28
  create_table :active_maintain_tests, :force => true do |t|
29
29
  t.string :status
30
+ t.integer :permissions
30
31
  end
31
32
  end
32
33
  end
@@ -55,12 +56,49 @@ if proceed
55
56
  ActiveMaintainTest.first.status.should == 'old'
56
57
  end
57
58
 
59
+ it "should allow us to update statuses using update_attributes" do
60
+ active_maintain_test = ActiveMaintainTest.new
61
+ active_maintain_test.update_attributes(:status => :bar)
62
+ ActiveMaintainTest.first.status.should == :bar
63
+ end
64
+
65
+ it "should allow us to update statuses using update_attribute" do
66
+ active_maintain_test = ActiveMaintainTest.new
67
+ active_maintain_test.update_attribute(:status, :bar)
68
+ ActiveMaintainTest.first.status.should == :bar
69
+ end
70
+
58
71
  it "should return the correct name when told to" do
59
72
  active_maintain_test = ActiveMaintainTest.create(:status => 'old')
60
73
  ActiveMaintainTest.first.status.name.should == 'old'
61
74
  end
62
75
  end
63
76
 
77
+ describe "bitmasks" do
78
+ before :each do
79
+ ActiveMaintainTest.maintain :permissions, :bitmask => true do
80
+ state :add, 0
81
+ state :delete, 1
82
+ state :foo, 2
83
+ state :bar, 3
84
+
85
+ aggregate :everything, :as => [:new, :old, :foo, :bar]
86
+ aggregate :fakes, :as => [:foo, :bar]
87
+ end
88
+ end
89
+
90
+ it "should allow me to set a bitmask value" do
91
+ active_maintain_test = ActiveMaintainTest.create(:permissions => 'add')
92
+ ActiveMaintainTest.last.permissions.add?.should be_true
93
+ end
94
+
95
+ it "should allow me to set multiple bitmask values" do
96
+ active_maintain_test = ActiveMaintainTest.create(:permissions => ['add', 'delete'])
97
+ ActiveMaintainTest.last.permissions.add?.should be_true
98
+ ActiveMaintainTest.last.permissions.delete?.should be_true
99
+ end
100
+ end
101
+
64
102
  describe "named_scopes" do
65
103
  it "should create named_scopes for all states" do
66
104
  ActiveMaintainTest.should respond_to(:old)
@@ -84,4 +122,4 @@ if proceed
84
122
 
85
123
  end
86
124
  end
87
- end
125
+ end
@@ -1,87 +1,86 @@
1
- # # Some specs to check against ActiveRecord conflicts. Rails tends to blow
2
- # # shit up when you build it outside of Rails. We'll see how this goes...
3
- #
4
- # proceed = false
5
- # begin
6
- # require 'rubygems'
7
- # gem 'datamapper'
8
- # require 'datamapper'
9
- # proceed = true
10
- # rescue Gem::LoadError, LoadError
11
- # puts 'Not testing DataMapper (unavailable)'
12
- # end
13
- #
14
- # if proceed = false
15
- # # Use load to have it evaluate the ActiveRecord::Base extension logic again, in the event
16
- # # that we've already done that with a previous test.
17
- # load 'lib/maintain.rb'
18
- # describe Maintain, "Datamapper::Resource" do
19
- # it "should automatically be extended" do
20
- # ActiveRecord::Base.should respond_to(:maintain)
21
- # end
22
- # describe "accessors" do
23
- # before :each do
24
- # ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:', :pool => 5, :timeout => 5000})
25
- # class ::ActiveMaintainTest < ActiveRecord::Base; end
26
- # silence_stream(STDOUT) do
27
- # ActiveRecord::Schema.define do
28
- # create_table :active_maintain_tests, :force => true do |t|
29
- # t.string :status
30
- # end
31
- # end
32
- # end
33
- #
34
- # ActiveMaintainTest.maintain :status do
35
- # state :new, :default => true
36
- # state :old
37
- # state :foo
38
- # state :bar
39
- # aggregate :everything, :as => [:new, :old, :foo, :bar]
40
- # aggregate :fakes, :as => [:foo, :bar]
41
- # end
42
- # end
43
- #
44
- # it "should default to 'new'" do
45
- # ActiveMaintainTest.new.status.should == 'new'
46
- # ActiveMaintainTest.new.status.should == :new
47
- # end
48
- #
49
- # it "should allow us to update its status to 'old'" do
50
- # active_maintain_test = ActiveMaintainTest.new(:status => 'old')
51
- # active_maintain_test.status.should == 'old'
52
- # lambda {
53
- # active_maintain_test.save!
54
- # }.should_not raise_error
55
- # ActiveMaintainTest.first.status.should == 'old'
56
- # end
57
- #
58
- # it "should return the correct name when told to" do
59
- # active_maintain_test = ActiveMaintainTest.create(:status => 'old')
60
- # ActiveMaintainTest.first.status.name.should == 'old'
61
- # end
62
- # end
63
- #
64
- # describe "named_scopes" do
65
- # it "should create named_scopes for all states" do
66
- # ActiveMaintainTest.should respond_to(:old)
67
- # ActiveMaintainTest.old.should respond_to(:each)
68
- # end
69
- #
70
- # it "should create named_scopes for all aggregates" do
71
- # ActiveMaintainTest.should respond_to(:everything)
72
- # ActiveMaintainTest.everything.should respond_to(:each)
73
- # end
74
- #
75
- # it "should return the correct collections on aggregates" do
76
- # ActiveMaintainTest.destroy_all
77
- # one = ActiveMaintainTest.create(:status => :foo)
78
- # two = ActiveMaintainTest.create(:status => :bar)
79
- # three = ActiveMaintainTest.create(:status => :new)
80
- # four = ActiveMaintainTest.create(:status => :old)
81
- # ActiveMaintainTest.fakes.should == [one, two]
82
- # ActiveMaintainTest.everything.should == [one, two, three, four]
83
- # end
84
- #
85
- # end
86
- # end
87
- # end
1
+ proceed = false
2
+ begin
3
+ require 'rubygems'
4
+ gem 'datamapper'
5
+ require 'datamapper'
6
+ proceed = true
7
+ rescue Gem::LoadError, LoadError
8
+ puts 'Not testing DataMapper (unavailable)'
9
+ end
10
+
11
+ if proceed
12
+ # Use load to have it evaluate the DataMapper extension logic again, in the event
13
+ # that we've already done that with a previous test.
14
+ load 'lib/maintain.rb'
15
+
16
+ DataMapper.setup(:default, "sqlite3::memory:")
17
+
18
+ class ::DataMapperMaintainTest
19
+ include DataMapper::Resource
20
+ extend Maintain
21
+
22
+ property :id, Serial
23
+ property :status, String
24
+
25
+ maintain :status do
26
+ state :new, :default => true
27
+ state :old
28
+ state :foo
29
+ state :bar
30
+ aggregate :everything, :as => [:new, :old, :foo, :bar]
31
+ aggregate :fakes, :as => [:foo, :bar]
32
+ end
33
+ end
34
+
35
+ DataMapper.auto_upgrade!
36
+
37
+ describe Maintain, "Datamapper::Resource" do
38
+ it "should automatically be extended" do
39
+ DataMapper::Resource.instance_methods.should include('maintain')
40
+ end
41
+
42
+ describe "accessors" do
43
+ it "should default to 'new'" do
44
+ DataMapperMaintainTest.new.status.should == 'new'
45
+ DataMapperMaintainTest.new.status.should == :new
46
+ end
47
+
48
+ it "should allow us to update its status to 'old'" do
49
+ active_maintain_test = DataMapperMaintainTest.new(:status => 'old')
50
+ active_maintain_test.status.should == 'old'
51
+ lambda {
52
+ active_maintain_test.save!
53
+ }.should_not raise_error
54
+ DataMapperMaintainTest.first.status.should == 'old'
55
+ end
56
+
57
+ it "should return the correct name when told to" do
58
+ active_maintain_test = DataMapperMaintainTest.create(:status => 'old')
59
+ DataMapperMaintainTest.first.status.name.should == 'old'
60
+ end
61
+ end
62
+
63
+ describe "named_scopes" do
64
+ it "should create named_scopes for all states" do
65
+ DataMapperMaintainTest.should respond_to(:old)
66
+ DataMapperMaintainTest.old.should respond_to(:each)
67
+ end
68
+
69
+ it "should create named_scopes for all aggregates" do
70
+ DataMapperMaintainTest.should respond_to(:everything)
71
+ DataMapperMaintainTest.everything.should respond_to(:each)
72
+ end
73
+
74
+ it "should return the correct collections on aggregates" do
75
+ DataMapperMaintainTest.all.destroy!
76
+ one = DataMapperMaintainTest.create(:status => :foo)
77
+ two = DataMapperMaintainTest.create(:status => :bar)
78
+ three = DataMapperMaintainTest.create(:status => :new)
79
+ four = DataMapperMaintainTest.create(:status => :old)
80
+ DataMapperMaintainTest.fakes.should == [one, two]
81
+ DataMapperMaintainTest.everything.should == [one, two, three, four]
82
+ end
83
+
84
+ end
85
+ end
86
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintain
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 1
10
- version: 0.2.1
9
+ - 2
10
+ version: 0.2.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Flip Sasser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-03 00:00:00 -05:00
18
+ date: 2011-02-23 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -37,6 +37,7 @@ files:
37
37
  - lib/maintain/backend.rb
38
38
  - lib/maintain/backend/active_record.rb
39
39
  - lib/maintain/backend/base.rb
40
+ - lib/maintain/backend/data_mapper.rb
40
41
  - lib/maintain/bitmask_value.rb
41
42
  - lib/maintain/integer_value.rb
42
43
  - lib/maintain/maintainer.rb
@@ -46,6 +47,7 @@ files:
46
47
  - spec/aggregates_spec.rb
47
48
  - spec/bitwise_spec.rb
48
49
  - spec/comparing_state_spec.rb
50
+ - spec/data_mapper_spec.rb
49
51
  - spec/defining_states_spec.rb
50
52
  - spec/hooks_spec.rb
51
53
  - spec/integer_spec.rb
@@ -54,7 +56,6 @@ files:
54
56
  - spec/proxy_spec.rb
55
57
  - spec/setting_state_spec.rb
56
58
  - spec/spec.opts
57
- - spec/data_mapper_spec.rb
58
59
  has_rdoc: true
59
60
  homepage: http://github.com/flipsasser/maintain
60
61
  licenses: []