attribute_changer 0.0.1
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/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/attribute_changer.gemspec +31 -0
- data/lib/attribute_changer.rb +12 -0
- data/lib/attribute_changer/allower.rb +15 -0
- data/lib/attribute_changer/attribute_change.rb +76 -0
- data/lib/attribute_changer/committer.rb +69 -0
- data/lib/attribute_changer/performer.rb +82 -0
- data/lib/attribute_changer/utils/result.rb +3 -0
- data/lib/attribute_changer/version.rb +3 -0
- data/lib/generators/attribute_changer_generator.rb +9 -0
- data/lib/generators/templates/create_attribute_changer_attribute_changes.rb +13 -0
- data/spec/factories/attribute_change.rb +7 -0
- data/spec/fixtures/dummy.rb +84 -0
- data/spec/lib/attribute_changer/allower_spec.rb +8 -0
- data/spec/lib/attribute_changer/attribute_change_spec.rb +42 -0
- data/spec/lib/attribute_changer/committer_spec.rb +123 -0
- data/spec/lib/attribute_changer/performer_spec.rb +62 -0
- data/spec/lib/attribute_changer/version_spec.rb +5 -0
- data/spec/spec_helper.rb +47 -0
- metadata +222 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Zbigniew Humeniuk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# AttributeChanger
|
2
|
+
|
3
|
+
Allows step change for attributes.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'attribute_changer'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install attribute_changer
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
class Dummy < ActiveRecord::Base
|
22
|
+
include AttributeChanger::Allower
|
23
|
+
|
24
|
+
attributes_allowed_for_step_change [:attr1, :attr2]
|
25
|
+
|
26
|
+
validates :attr1, presence: true, inclusion:{in: %w(foo bar)}
|
27
|
+
validates :attr2, presence: true, if: ->(obj){ obj.attr1 == 'bar' }
|
28
|
+
validates :attr3, presence: true
|
29
|
+
end
|
30
|
+
|
31
|
+
dummy = Dummy.create do |obj|
|
32
|
+
obj.attr1 = 'foo'
|
33
|
+
obj.attr3 = false
|
34
|
+
end
|
35
|
+
|
36
|
+
performer = AttributeChanger::Performer.new obj: dummy, attrib: :attr3, value: true
|
37
|
+
performer.save # false
|
38
|
+
performer.result.message # :not_allowed
|
39
|
+
|
40
|
+
performer = AttributeChanger::Performer.new obj: dummy, attrib: :attr1, value: 'bar'
|
41
|
+
performer.save # true
|
42
|
+
|
43
|
+
AttributeChanger::AttributeChange.from_obj(dummy).pending.count # 1
|
44
|
+
|
45
|
+
committer = AttributeChanger::Committer.new performer.attrib_change
|
46
|
+
committer.accept # false
|
47
|
+
committer.result.message # :invalid_obj
|
48
|
+
committer.invalid_attribs # [:attr2]
|
49
|
+
|
50
|
+
AttributeChanger::AttributeChange.from_obj(dummy).waiting.count # 1
|
51
|
+
|
52
|
+
performer = AttributeChanger::Performer.new obj: dummy, attrib: :attr2, value: true
|
53
|
+
performer.save # true
|
54
|
+
|
55
|
+
dummy.reload
|
56
|
+
dummy.attr1 # 'foo'
|
57
|
+
dummy.attr2 # nil
|
58
|
+
|
59
|
+
AttributeChanger::Committer.new(performer.attrib_change).accept # true
|
60
|
+
|
61
|
+
dummy.reload
|
62
|
+
dummy.attr1 # bar
|
63
|
+
dummy.attr2 # true
|
64
|
+
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
1. Fork it
|
69
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
70
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
71
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
72
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'attribute_changer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "attribute_changer"
|
8
|
+
spec.version = AttributeChanger::VERSION
|
9
|
+
spec.authors = ["Zbigniew Humeniuk"]
|
10
|
+
spec.email = ["zbigniew.humeniuk@gmail.com"]
|
11
|
+
spec.description = %q{Allows step change for attributes.}
|
12
|
+
spec.summary = %q{Allows step change for attributes.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'activesupport', '>= 3.0.0'
|
22
|
+
spec.add_dependency 'activerecord', '>= 3.0.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.13.0"
|
27
|
+
spec.add_development_dependency "activemodel", "~> 3.2.13"
|
28
|
+
spec.add_development_dependency "sqlite3", "~> 1.3.7"
|
29
|
+
spec.add_development_dependency 'database_cleaner', '~> 0.9.1'
|
30
|
+
spec.add_development_dependency 'factory_girl', '~> 4.2.0'
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AttributeChanger
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'active_record' unless defined?(ActiveRecord)
|
5
|
+
require 'active_support/concern' unless defined?(ActiveSupport::Concern)
|
6
|
+
|
7
|
+
require "attribute_changer/version"
|
8
|
+
require 'attribute_changer/committer'
|
9
|
+
require 'attribute_changer/performer'
|
10
|
+
require 'attribute_changer/allower'
|
11
|
+
require 'attribute_changer/attribute_change'
|
12
|
+
require 'attribute_changer/utils/result'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AttributeChanger::Allower
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
class_attribute :_attributes_allowed_for_step_change
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def attributes_allowed_for_step_change(attribs = nil)
|
10
|
+
self._attributes_allowed_for_step_change ||= []
|
11
|
+
self._attributes_allowed_for_step_change = *attribs if attribs
|
12
|
+
_attributes_allowed_for_step_change
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class AttributeChanger::AttributeChange < ::ActiveRecord::Base
|
2
|
+
self.table_name_prefix = 'attribute_changer_'
|
3
|
+
|
4
|
+
attr_accessible nil
|
5
|
+
|
6
|
+
validates :obj_id, presence: true
|
7
|
+
validates :obj_type, presence: true
|
8
|
+
validates :attrib, presence: true
|
9
|
+
validates :status, presence: true, inclusion:{in: %w(obsolete pending rejected accepted waiting)}
|
10
|
+
|
11
|
+
after_initialize :set_default_values
|
12
|
+
after_create :change_status
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def from_obj(obj); where("obj_type = ? AND obj_id = ?", obj.class.name, obj.id) end
|
16
|
+
def pending; where("status = ?", 'pending') end
|
17
|
+
def waiting; where("status = ?", 'waiting') end
|
18
|
+
end
|
19
|
+
|
20
|
+
def obj_type
|
21
|
+
if val = read_attribute(:obj_type)
|
22
|
+
val.constantize
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def obj
|
27
|
+
if obj_type && obj_id
|
28
|
+
@obj ||= obj_type.find_by_id obj_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def obj=(val)
|
33
|
+
self.obj_id = val.id
|
34
|
+
self.obj_type = val.class.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def value=(val)
|
38
|
+
write_attribute :value, serialize(val)
|
39
|
+
end
|
40
|
+
|
41
|
+
def value
|
42
|
+
deserialize
|
43
|
+
end
|
44
|
+
|
45
|
+
def dependent_attribs(o = obj)
|
46
|
+
attrib_was = o.send "#{attrib}"
|
47
|
+
o.send "#{attrib}=", value
|
48
|
+
o.valid?
|
49
|
+
da = o.errors.messages.map{|attr, errors| attr if errors.any?}.compact
|
50
|
+
o.send "#{attrib}=", attrib_was
|
51
|
+
o.valid?
|
52
|
+
da
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def set_default_values
|
59
|
+
self.status ||= 'pending'
|
60
|
+
end
|
61
|
+
|
62
|
+
def change_status
|
63
|
+
self.class.where('obj_id = ?', obj_id).where('obj_type = ?', obj_type.to_s).where('attrib = ?', attrib).
|
64
|
+
where('status = ?', 'pending').where('created_at < ?', created_at).each do |o|
|
65
|
+
o.update_attribute :status, 'obsolete'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def serialize(val)
|
70
|
+
Base64.encode64(Marshal.dump(val))
|
71
|
+
end
|
72
|
+
|
73
|
+
def deserialize
|
74
|
+
Marshal.load Base64.decode64(read_attribute(:value))
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class AttributeChanger::Committer
|
2
|
+
attr_reader :result, :error, :invalid_attribs
|
3
|
+
|
4
|
+
def initialize(attribute_change)
|
5
|
+
@result = AttributeChanger::Utils::Result.new nil, nil
|
6
|
+
@attribute_change = attribute_change
|
7
|
+
end
|
8
|
+
|
9
|
+
def attrib_change; @attribute_change end
|
10
|
+
|
11
|
+
def accept
|
12
|
+
if update_obj
|
13
|
+
@result.success = true
|
14
|
+
else
|
15
|
+
@result.success = false
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def reject
|
21
|
+
update_status :rejected
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def update_obj
|
28
|
+
@obj = @attribute_change.obj
|
29
|
+
@obj.send "#{@attribute_change.attrib}=", @attribute_change.value
|
30
|
+
return update_status(:accepted) if @obj.save
|
31
|
+
assign_error
|
32
|
+
assign_invalid_attribs
|
33
|
+
if @obj.errors[@attribute_change.attrib].any?
|
34
|
+
return update_status(:accepted) if take_into_consideration_waiting_changes
|
35
|
+
@result.message = :invalid_attrib
|
36
|
+
else
|
37
|
+
return update_status(:accepted) if take_into_consideration_waiting_changes
|
38
|
+
@result.message = :invalid_obj
|
39
|
+
update_status :waiting
|
40
|
+
end
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_status(status)
|
45
|
+
@attribute_change.status = status.to_s
|
46
|
+
@attribute_change.save!
|
47
|
+
end
|
48
|
+
|
49
|
+
def assign_error
|
50
|
+
@error = @obj.class.human_attribute_name(@obj.errors.first.first) + ' ' + @obj.errors.first.last
|
51
|
+
end
|
52
|
+
|
53
|
+
def assign_invalid_attribs
|
54
|
+
@invalid_attribs = @obj.errors.messages.map{ |attr, errors| attr if errors.any? }.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
def take_into_consideration_waiting_changes
|
58
|
+
attrib_changes = AttributeChanger::AttributeChange.from_obj(@obj).waiting.all
|
59
|
+
attrib_changes.each{ |ac| @obj.send "#{ac.attrib}=", ac.value }
|
60
|
+
saved = @obj.save
|
61
|
+
if saved
|
62
|
+
attrib_changes.each do |ac|
|
63
|
+
ac.status = 'accepted'
|
64
|
+
ac.save!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
saved
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class AttributeChanger::Performer
|
2
|
+
attr_reader :obj, :error, :result, :attrib, :attrib_change
|
3
|
+
|
4
|
+
def initialize(attribs)
|
5
|
+
@obj = attribs[:obj]
|
6
|
+
@attrib = attribs[:attrib]
|
7
|
+
@value = attribs[:value]
|
8
|
+
end
|
9
|
+
|
10
|
+
def save
|
11
|
+
valid
|
12
|
+
return false if result && !result.success
|
13
|
+
create_attribute_change
|
14
|
+
@result = AttributeChanger::Utils::Result.new true, nil
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def attrib_exists?
|
22
|
+
(@obj.attributes.keys - %w(id created_at updated_at)).include? @attrib.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def attribute_allowed?
|
26
|
+
@obj.class.attributes_allowed_for_step_change.include? @attrib
|
27
|
+
end
|
28
|
+
|
29
|
+
#BEGIN validation
|
30
|
+
def valid
|
31
|
+
unless attribute_allowed?
|
32
|
+
@result = AttributeChanger::Utils::Result.new false, :not_allowed
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
unless attrib_exists?
|
37
|
+
@result = AttributeChanger::Utils::Result.new false, :missing_attribute
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
if same_value?
|
42
|
+
@result = AttributeChanger::Utils::Result.new false, :same
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
unless valid_attrib?
|
47
|
+
assign_error
|
48
|
+
reset_obj
|
49
|
+
@result = AttributeChanger::Utils::Result.new false, :invalid
|
50
|
+
return
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid_attrib?
|
55
|
+
@obj.send "#{@attrib}=", @value
|
56
|
+
@obj.valid?
|
57
|
+
@obj.errors[@attrib].empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
def same_value?
|
61
|
+
@obj.send(@attrib) == @value
|
62
|
+
end
|
63
|
+
|
64
|
+
def assign_error
|
65
|
+
@error = @obj.errors[@attrib].first
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_obj
|
69
|
+
@obj.errors.clear
|
70
|
+
@obj.reload
|
71
|
+
end
|
72
|
+
# END validation
|
73
|
+
|
74
|
+
def create_attribute_change
|
75
|
+
@attrib_change = AttributeChanger::AttributeChange.new do |o|
|
76
|
+
o.obj = @obj
|
77
|
+
o.attrib = @attrib
|
78
|
+
o.value = @value
|
79
|
+
end
|
80
|
+
@attrib_change.save!
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class AttributeChangerGenerator < Rails::Generators::Base
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
desc "Create migration"
|
5
|
+
|
6
|
+
def copy_migration
|
7
|
+
copy_file "create_attribute_changer_attribute_changes.rb", "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_create_attribute_changer_attribute_changes.rb"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateAttributeChangerAttributeChanges < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :attribute_changer_attribute_changes do |t|
|
4
|
+
t.string :obj_type
|
5
|
+
t.integer :obj_id
|
6
|
+
t.string :attrib
|
7
|
+
t.string :value
|
8
|
+
t.string :status
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Dummy
|
2
|
+
include ActiveModel::Validations
|
3
|
+
include ActiveModel::Conversion
|
4
|
+
include AttributeChanger::Allower
|
5
|
+
|
6
|
+
@@objects = []
|
7
|
+
|
8
|
+
attr_reader :id
|
9
|
+
attr_accessor :attr2
|
10
|
+
attributes_allowed_for_step_change [:attr1, :attr3, :attr4]
|
11
|
+
|
12
|
+
validates :attr1, presence: true, inclusion:{in: %w(foo bar baz boo)}
|
13
|
+
validates :attr2, presence: true
|
14
|
+
validates :attr3, presence: true, if: ->(ac){ ac.attr1 == 'boo' }
|
15
|
+
validates :attr4, presence: true, if: ->(ac){ ac.attr3.present? }
|
16
|
+
|
17
|
+
def initialize attributes={}
|
18
|
+
attributes.each do |name, value|
|
19
|
+
send "#{name}=", value
|
20
|
+
end
|
21
|
+
@id = @@objects.size + 1
|
22
|
+
@@objects << self
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def find_by_id id
|
27
|
+
@@objects.find{ |o| o.id == id }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def attr1; @attr1 end
|
32
|
+
def attr3; @attr3 end
|
33
|
+
def attr4; @attr4 end
|
34
|
+
|
35
|
+
def attr1= val
|
36
|
+
@attr1_was = @attr1 if val != @attr1
|
37
|
+
@attr1 = val
|
38
|
+
end
|
39
|
+
|
40
|
+
def attr3= val
|
41
|
+
@attr3_was = @attr3 if val != @attr3
|
42
|
+
@attr3 = val
|
43
|
+
end
|
44
|
+
|
45
|
+
def attr4= val
|
46
|
+
@attr4_was = @attr4 if val != @attr4
|
47
|
+
@attr4 = val
|
48
|
+
end
|
49
|
+
|
50
|
+
def persisted?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def attributes
|
55
|
+
attrs = {}
|
56
|
+
%w(attr1 attr2 attr3 attr4).each{ |name| attrs[name] = send(name) }
|
57
|
+
attrs
|
58
|
+
end
|
59
|
+
|
60
|
+
def save
|
61
|
+
if valid?
|
62
|
+
clear_changes
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def reload
|
70
|
+
@attr1 = @attr1_was if @attr1_was
|
71
|
+
@attr3 = @attr3_was if @attr3_was
|
72
|
+
@attr4 = @attr4_was if @attr4_was
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def clear_changes
|
80
|
+
@attr1_was = nil
|
81
|
+
@attr3_was = nil
|
82
|
+
@attr4_was = nil
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttributeChanger::Allower do
|
4
|
+
it "includes attributes_allowed_for_step_change class method" do
|
5
|
+
Dummy.stub(:attributes_allowed_for_step_change){ |arg1| true }
|
6
|
+
Dummy.attributes_allowed_for_step_change(:attrib1).should be_true
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttributeChanger::AttributeChange do
|
4
|
+
describe "attributes" do
|
5
|
+
it "has obj attribute" do
|
6
|
+
subject.obj.should be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "obj" do
|
10
|
+
it "sets obj_type and obj_id from obj" do
|
11
|
+
obj = Dummy.new attr1: 'foo', attr2: true
|
12
|
+
subject.obj = obj
|
13
|
+
subject.obj_id.should eq obj.id
|
14
|
+
subject.obj_type.should eq Dummy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "status" do
|
19
|
+
it "has default value equals pending" do
|
20
|
+
subject.status.should eq 'pending'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "value" do
|
25
|
+
it "is serialized" do
|
26
|
+
ac = FactoryGirl.create :attribute_change, value: 'baz'
|
27
|
+
AttributeChanger::AttributeChange.find(ac.id).value.should eql 'baz'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "could be nil" do
|
31
|
+
ac = FactoryGirl.create :attribute_change, value: nil
|
32
|
+
AttributeChanger::AttributeChange.find(ac.id).value.should be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "instance methods" do
|
38
|
+
describe "#dependent_attribs" do
|
39
|
+
it "returns dependent attributes"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe AttributeChanger::Committer do
|
4
|
+
describe "attributes" do
|
5
|
+
describe "result" do
|
6
|
+
it "is instance of AttributeChanger::Utils::Result" do
|
7
|
+
AttributeChanger::Committer.new(nil).result.should be_instance_of AttributeChanger::Utils::Result
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "instance methods" do
|
13
|
+
let(:dummy_obj){ Dummy.new attr1: 'foo', attr2: true }
|
14
|
+
let(:performer){ AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'bar') }
|
15
|
+
let(:committer){ AttributeChanger::Committer.new performer.attrib_change }
|
16
|
+
|
17
|
+
before do
|
18
|
+
performer.save
|
19
|
+
dummy_obj.reload
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#accept" do
|
23
|
+
before { committer.accept }
|
24
|
+
|
25
|
+
context "change is valid" do
|
26
|
+
it "changes status of attribute's change to accepted" do
|
27
|
+
committer.attrib_change.reload.status.should eq "accepted"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "changes attribute's value" do
|
31
|
+
dummy_obj.reload.attr1.should == 'bar'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns true" do
|
35
|
+
performer = AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'baz')
|
36
|
+
performer.save
|
37
|
+
AttributeChanger::Committer.new(performer.attrib_change).accept.should be_true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "change is invalid" do
|
42
|
+
let(:committer) do
|
43
|
+
attrib_change = performer.attrib_change
|
44
|
+
attrib_change.value = 'buz'
|
45
|
+
AttributeChanger::Committer.new attrib_change
|
46
|
+
end
|
47
|
+
|
48
|
+
it "leaves pending status" do
|
49
|
+
committer.attrib_change.reload.status.should eq "pending"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not change attribute's value" do
|
53
|
+
dummy_obj.reload.attr1.should == 'foo'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns false" do
|
57
|
+
committer.accept.should be_false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "has result attribute with success equals false" do
|
61
|
+
committer.result.success.should be_false
|
62
|
+
end
|
63
|
+
|
64
|
+
it "has result attribute with proper message" do
|
65
|
+
committer.result.message.should eq :invalid_attrib
|
66
|
+
end
|
67
|
+
|
68
|
+
it "assigns error attribute" do
|
69
|
+
committer.error.should eq 'Attr1 is not included in the list'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "change is valid but whole object is invalid" do
|
74
|
+
let(:performer){ AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'boo') }
|
75
|
+
|
76
|
+
it "sets status to waiting" do
|
77
|
+
committer.attrib_change.reload.status.should eq "waiting"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "has result attribute with proper message" do
|
81
|
+
committer.result.message.should eq :invalid_obj
|
82
|
+
end
|
83
|
+
|
84
|
+
it "allows to get invalid attributes through getter" do
|
85
|
+
committer.invalid_attribs.should eq [:attr3]
|
86
|
+
end
|
87
|
+
|
88
|
+
context "changes for invalid attributes are waiting" do
|
89
|
+
before do
|
90
|
+
p1 = AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr3, value: 'foo')
|
91
|
+
p1.save
|
92
|
+
AttributeChanger::Committer.new(p1.attrib_change).accept # false, :invalid_obj
|
93
|
+
dummy_obj.attr3 = nil
|
94
|
+
p2 = AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr4, value: 'foo')
|
95
|
+
p2.save
|
96
|
+
AttributeChanger::Committer.new(p2.attrib_change).accept
|
97
|
+
committer.accept
|
98
|
+
end
|
99
|
+
|
100
|
+
it "takes into consideration all valid waiting changes and change status to accepted" do
|
101
|
+
committer.attrib_change.reload.status.should eq "accepted"
|
102
|
+
end
|
103
|
+
|
104
|
+
it "changes status of waiting changes to accepted" do
|
105
|
+
AttributeChanger::AttributeChange.waiting.count.should eq 0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#reject" do
|
112
|
+
before { committer.reject }
|
113
|
+
|
114
|
+
it "changes status of attribute's change to rejected" do
|
115
|
+
committer.attrib_change.reload.status.should eq "rejected"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "does not change attribute's value" do
|
119
|
+
dummy_obj.attr1.should == 'foo'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttributeChanger::Performer do
|
4
|
+
describe "instance methods" do
|
5
|
+
describe "#save" do
|
6
|
+
let(:dummy_obj){ Dummy.new attr1: 'foo' }
|
7
|
+
|
8
|
+
it "sets Utils::Result object to result attribute" do
|
9
|
+
ac = AttributeChanger::Performer.new({obj: dummy_obj, attrib: :attr1, value: 'bar'})
|
10
|
+
ac.save
|
11
|
+
ac.result.should be_instance_of AttributeChanger::Utils::Result
|
12
|
+
end
|
13
|
+
|
14
|
+
context "value is allowed" do
|
15
|
+
context "new value is valid" do
|
16
|
+
context "new value if different" do
|
17
|
+
it "creates attribute's change object" do
|
18
|
+
AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'bar').save
|
19
|
+
AttributeChanger::AttributeChange.count.should eq 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context "new value is the same" do
|
23
|
+
it "does not create attribute's change object" do
|
24
|
+
AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'foo').save
|
25
|
+
AttributeChanger::AttributeChange.count.should eq 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "new value is invalid" do
|
31
|
+
let(:attribute_changer){ AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr1, value: 'buz') }
|
32
|
+
|
33
|
+
before{ attribute_changer.save }
|
34
|
+
|
35
|
+
it "assigns error attribute" do
|
36
|
+
attribute_changer.error.should eq "is not included in the list"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "do not change the object" do
|
40
|
+
attribute_changer.obj.attr1.should eq 'foo'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "do not assign error to object" do
|
44
|
+
attribute_changer.obj.errors.should be_empty
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "value is not allowed" do
|
50
|
+
subject{ AttributeChanger::Performer.new(obj: dummy_obj, attrib: :attr2, value: 'foo') }
|
51
|
+
|
52
|
+
before{ subject.save }
|
53
|
+
|
54
|
+
it("returns false") { subject.save.should be_false }
|
55
|
+
|
56
|
+
it("has result with success attribute equals false") { subject.result.success.should be_false }
|
57
|
+
|
58
|
+
it("has result with proper message") { subject.result.message.should == :not_allowed }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'active_model'
|
6
|
+
require 'factory_girl'
|
7
|
+
require 'database_cleaner'
|
8
|
+
|
9
|
+
Bundler.require
|
10
|
+
|
11
|
+
Dir["#{File.dirname(__FILE__)}/fixtures/**/*.rb"].each{|f| require f}
|
12
|
+
Dir["#{File.dirname(__FILE__)}/factories/**/*.rb"].each{|f| require f}
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.order = "random"
|
16
|
+
|
17
|
+
config.before :suite do
|
18
|
+
DatabaseCleaner.strategy = :truncation
|
19
|
+
end
|
20
|
+
|
21
|
+
config.before :each do
|
22
|
+
DatabaseCleaner.start
|
23
|
+
end
|
24
|
+
|
25
|
+
config.after :each do
|
26
|
+
DatabaseCleaner.clean
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
ActiveRecord::Base.establish_connection({
|
31
|
+
adapter: 'sqlite3',
|
32
|
+
database: ':memory:'
|
33
|
+
})
|
34
|
+
|
35
|
+
ActiveRecord::Schema.define do
|
36
|
+
self.verbose = false
|
37
|
+
|
38
|
+
create_table "attribute_changer_attribute_changes", force: true do |t|
|
39
|
+
t.string "obj_type"
|
40
|
+
t.integer "obj_id"
|
41
|
+
t.string "attrib"
|
42
|
+
t.string "value"
|
43
|
+
t.string "status"
|
44
|
+
t.datetime "created_at", null: false
|
45
|
+
t.datetime "updated_at", null: false
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attribute_changer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zbigniew Humeniuk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.0.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.13.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.13.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: activemodel
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 3.2.13
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 3.2.13
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: sqlite3
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.3.7
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.3.7
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: database_cleaner
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.9.1
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.9.1
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: factory_girl
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 4.2.0
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 4.2.0
|
158
|
+
description: Allows step change for attributes.
|
159
|
+
email:
|
160
|
+
- zbigniew.humeniuk@gmail.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files: []
|
164
|
+
files:
|
165
|
+
- .gitignore
|
166
|
+
- .rspec
|
167
|
+
- Gemfile
|
168
|
+
- LICENSE.txt
|
169
|
+
- README.md
|
170
|
+
- Rakefile
|
171
|
+
- attribute_changer.gemspec
|
172
|
+
- lib/attribute_changer.rb
|
173
|
+
- lib/attribute_changer/allower.rb
|
174
|
+
- lib/attribute_changer/attribute_change.rb
|
175
|
+
- lib/attribute_changer/committer.rb
|
176
|
+
- lib/attribute_changer/performer.rb
|
177
|
+
- lib/attribute_changer/utils/result.rb
|
178
|
+
- lib/attribute_changer/version.rb
|
179
|
+
- lib/generators/attribute_changer_generator.rb
|
180
|
+
- lib/generators/templates/create_attribute_changer_attribute_changes.rb
|
181
|
+
- spec/factories/attribute_change.rb
|
182
|
+
- spec/fixtures/dummy.rb
|
183
|
+
- spec/lib/attribute_changer/allower_spec.rb
|
184
|
+
- spec/lib/attribute_changer/attribute_change_spec.rb
|
185
|
+
- spec/lib/attribute_changer/committer_spec.rb
|
186
|
+
- spec/lib/attribute_changer/performer_spec.rb
|
187
|
+
- spec/lib/attribute_changer/version_spec.rb
|
188
|
+
- spec/spec_helper.rb
|
189
|
+
homepage: ''
|
190
|
+
licenses:
|
191
|
+
- MIT
|
192
|
+
post_install_message:
|
193
|
+
rdoc_options: []
|
194
|
+
require_paths:
|
195
|
+
- lib
|
196
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
197
|
+
none: false
|
198
|
+
requirements:
|
199
|
+
- - ! '>='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
|
+
none: false
|
204
|
+
requirements:
|
205
|
+
- - ! '>='
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
requirements: []
|
209
|
+
rubyforge_project:
|
210
|
+
rubygems_version: 1.8.24
|
211
|
+
signing_key:
|
212
|
+
specification_version: 3
|
213
|
+
summary: Allows step change for attributes.
|
214
|
+
test_files:
|
215
|
+
- spec/factories/attribute_change.rb
|
216
|
+
- spec/fixtures/dummy.rb
|
217
|
+
- spec/lib/attribute_changer/allower_spec.rb
|
218
|
+
- spec/lib/attribute_changer/attribute_change_spec.rb
|
219
|
+
- spec/lib/attribute_changer/committer_spec.rb
|
220
|
+
- spec/lib/attribute_changer/performer_spec.rb
|
221
|
+
- spec/lib/attribute_changer/version_spec.rb
|
222
|
+
- spec/spec_helper.rb
|