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 +1 -1
- data/lib/maintain/backend/active_record.rb +1 -1
- data/lib/maintain/backend/data_mapper.rb +29 -0
- data/lib/maintain/backend.rb +41 -1
- data/lib/maintain.rb +8 -21
- data/maintain.gemspec +47 -18
- data/spec/active_record_spec.rb +39 -1
- data/spec/data_mapper_spec.rb +86 -87
- metadata +6 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.2
|
@@ -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
|
data/lib/maintain/backend.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
111
|
-
|
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
|
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.
|
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-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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.
|
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 =
|
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::
|
75
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
76
|
else
|
48
77
|
end
|
49
78
|
else
|
data/spec/active_record_spec.rb
CHANGED
@@ -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
|
data/spec/data_mapper_spec.rb
CHANGED
@@ -1,87 +1,86 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
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-
|
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: []
|