validates_immutability 1.0.0
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 +6 -0
- data/CHANGELOG +3 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +54 -0
- data/Rakefile +1 -0
- data/lib/validates_immutability.rb +25 -0
- data/lib/validates_immutability/immutable_validator.rb +17 -0
- data/lib/validates_immutability/version.rb +3 -0
- data/spec/lib/validates_immutability/immutable_validator_spec.rb +131 -0
- data/spec/lib/validates_immutability_spec.rb +85 -0
- data/spec/spec_helper.rb +15 -0
- data/validates_immutability.gemspec +26 -0
- metadata +138 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Austin Schneider
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= ValidatesImmutability
|
2
|
+
|
3
|
+
Validations for making ActiveRecord objects immutable.
|
4
|
+
|
5
|
+
== Examples
|
6
|
+
|
7
|
+
class Person < ActiveRecord::Base
|
8
|
+
immutable :unless => :can_change?, :message => "I don't like being changed."
|
9
|
+
|
10
|
+
def can_change?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
> p = Person.first
|
16
|
+
> p.plays = "soccer"
|
17
|
+
> p.valid?
|
18
|
+
=> false
|
19
|
+
> p.errors
|
20
|
+
=> {:base=>["I don't like being changed."]}
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
class Car < ActiveRecord::Base
|
25
|
+
validates :color, :immutable => {
|
26
|
+
:if => lambda { !color_can_change? },
|
27
|
+
:message => "is beautiful already."
|
28
|
+
}
|
29
|
+
|
30
|
+
def color_can_change?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
> c = Car.first
|
36
|
+
> c.color = "red"
|
37
|
+
> c.valid?
|
38
|
+
=> false
|
39
|
+
> c.errors
|
40
|
+
=> {:color=>["is beautiful already."]}
|
41
|
+
|
42
|
+
== Note on Patches/Pull Requests
|
43
|
+
|
44
|
+
* Fork the project.
|
45
|
+
* Make your feature addition or bug fix.
|
46
|
+
* Add tests for it. This is important so I don't break it in a
|
47
|
+
future version unintentionally.
|
48
|
+
* Commit, do not mess with rakefile, version, or changelog.
|
49
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
50
|
+
* Send me a pull request. Bonus points for topic branches.
|
51
|
+
|
52
|
+
== Copyright
|
53
|
+
|
54
|
+
See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_support"
|
2
|
+
|
3
|
+
module ValidatesImmutability
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def immutable(options = {})
|
8
|
+
if options.key?(:on)
|
9
|
+
raise ArgumentError, "Unknown option :on"
|
10
|
+
end
|
11
|
+
options.merge!(:on => :update)
|
12
|
+
error_msg = options.delete(:message) || "Updates are not allowed."
|
13
|
+
validate options do
|
14
|
+
errors[:base] << error_msg if changed?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require "validates_immutability/version"
|
21
|
+
|
22
|
+
ActiveSupport.on_load :active_record do
|
23
|
+
ActiveRecord::Base.send(:include, ValidatesImmutability)
|
24
|
+
require "validates_immutability/immutable_validator"
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ValidatesImmutability
|
2
|
+
class ImmutableValidator < ActiveModel::EachValidator
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
if options.key?(:on)
|
5
|
+
raise ArgumentError, "Unknown option :on"
|
6
|
+
end
|
7
|
+
return if record.new_record?
|
8
|
+
if record.send("#{attribute}_changed?")
|
9
|
+
record.errors.add(attribute, (options[:message] || "can't be modified"))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# ImmutableValidator needs to be in global scope to be recognized up by AR
|
16
|
+
class ImmutableValidator < ValidatesImmutability::ImmutableValidator
|
17
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Widget4 < ActiveRecord::Base
|
4
|
+
set_table_name :widgets
|
5
|
+
validates :attr1, :immutable => true
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Widget4, "validations" do
|
9
|
+
context "when persisted" do
|
10
|
+
subject { Widget4.create! }
|
11
|
+
|
12
|
+
context "when attr1 is changed" do
|
13
|
+
before do
|
14
|
+
subject.attr1 = "changed"
|
15
|
+
subject.valid?
|
16
|
+
p subject.errors
|
17
|
+
end
|
18
|
+
|
19
|
+
it { should be_invalid }
|
20
|
+
it { subject.errors.full_messages.should == ["Attr1 can't be modified"] }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when attr1 is not changed" do
|
24
|
+
it { should be_valid }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when new" do
|
29
|
+
subject { Widget4.new }
|
30
|
+
["change", nil].each do |value|
|
31
|
+
context "when attr1 is #{value ? 'changed' : 'not changed'}" do
|
32
|
+
before { subject.attr1 = value if value }
|
33
|
+
it { should be_valid }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
##################################################
|
40
|
+
|
41
|
+
class Widget5 < ActiveRecord::Base
|
42
|
+
set_table_name :widgets
|
43
|
+
validates :attr1, :immutable => {:if => :validate_immutability}
|
44
|
+
attr_accessor :validate_immutability
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Widget5, "validations" do
|
48
|
+
context "when persisted" do
|
49
|
+
subject { Widget5.create! }
|
50
|
+
|
51
|
+
context "when attr1 is changed" do
|
52
|
+
before { subject.attr1 = "changed" }
|
53
|
+
|
54
|
+
context "validate_immutability is true" do
|
55
|
+
before do
|
56
|
+
subject.validate_immutability = true
|
57
|
+
subject.valid?
|
58
|
+
end
|
59
|
+
it { should be_invalid }
|
60
|
+
it { subject.errors.full_messages.should == ["Attr1 can't be modified"] }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "validate_immutability is not true" do
|
64
|
+
before { subject.validate_immutability = false }
|
65
|
+
it { should be_valid }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when attr1 is not changed" do
|
70
|
+
[true, false, nil].each do |value|
|
71
|
+
context "validate_immutability is #{value}" do
|
72
|
+
before { subject.validate_immutability = value }
|
73
|
+
it { should be_valid }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when new" do
|
80
|
+
subject { Widget5.new }
|
81
|
+
|
82
|
+
["changed", nil].each do |value|
|
83
|
+
context "when attr1 is #{value ? 'changed' : 'not changed'}" do
|
84
|
+
before { subject.attr1 = value }
|
85
|
+
[true, false, nil].each do |value|
|
86
|
+
context "validate_immutability is #{value}" do
|
87
|
+
before { subject.validate_immutability = value }
|
88
|
+
it { should be_valid }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
##################################################
|
97
|
+
|
98
|
+
class Widget6 < ActiveRecord::Base
|
99
|
+
set_table_name :widgets
|
100
|
+
validates :attr1, :immutable => {:message => "is frozen!"}
|
101
|
+
end
|
102
|
+
|
103
|
+
describe Widget6, "validations" do
|
104
|
+
context "when persisted" do
|
105
|
+
subject { Widget6.create! }
|
106
|
+
|
107
|
+
context "when attr1 is changed" do
|
108
|
+
before do
|
109
|
+
subject.attr1 = "changed"
|
110
|
+
subject.valid?
|
111
|
+
end
|
112
|
+
|
113
|
+
it { should be_invalid }
|
114
|
+
it { subject.errors.full_messages.should == ["Attr1 is frozen!"] }
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when attr1 is not changed" do
|
118
|
+
it { should be_valid }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "when new" do
|
123
|
+
subject { Widget6.new }
|
124
|
+
["change", nil].each do |value|
|
125
|
+
context "when attr1 is #{value ? 'changed' : 'not changed'}" do
|
126
|
+
before { subject.attr1 = value if value }
|
127
|
+
it { should be_valid }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Widget1 < ActiveRecord::Base
|
4
|
+
set_table_name :widgets
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Widget1, ".immutable" do
|
8
|
+
it "calls .validate" do
|
9
|
+
Widget1.should_receive(:validate).with(:a => 1, :b => 2, :on => :update)
|
10
|
+
Widget1.immutable(:a => 1, :b => 2, :message => 'some message')
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when :on is given" do
|
14
|
+
it "raises an ArgumentError" do
|
15
|
+
lambda do
|
16
|
+
Widget1.immutable(:on => :create)
|
17
|
+
end.should raise_error(ArgumentError, "Unknown option :on")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
##################################################
|
23
|
+
|
24
|
+
class Widget2 < ActiveRecord::Base
|
25
|
+
set_table_name :widgets
|
26
|
+
immutable :message => 'My message'
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Widget2, "validations" do
|
30
|
+
context "when persisted" do
|
31
|
+
subject { Widget2.create! }
|
32
|
+
|
33
|
+
context "when changed" do
|
34
|
+
before do
|
35
|
+
subject.attr1 = "changed"
|
36
|
+
subject.valid?
|
37
|
+
end
|
38
|
+
|
39
|
+
it { should be_invalid }
|
40
|
+
it { subject.errors.full_messages.should == ['My message'] }
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when not changed" do
|
44
|
+
before { subject.valid? }
|
45
|
+
it { should be_valid }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when new" do
|
50
|
+
subject { Widget2.new }
|
51
|
+
|
52
|
+
context "when changed" do
|
53
|
+
before do
|
54
|
+
subject.attr1 = "changed"
|
55
|
+
subject.valid?
|
56
|
+
end
|
57
|
+
it { should be_valid }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when not changed" do
|
61
|
+
before { subject.valid? }
|
62
|
+
it { should be_valid }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##################################################
|
68
|
+
|
69
|
+
class Widget3 < ActiveRecord::Base
|
70
|
+
set_table_name :widgets
|
71
|
+
immutable
|
72
|
+
end
|
73
|
+
|
74
|
+
describe Widget3, "validations" do
|
75
|
+
context "when persisted and changed" do
|
76
|
+
subject { Widget3.create! }
|
77
|
+
before do
|
78
|
+
subject.attr1 = "changed"
|
79
|
+
subject.valid?
|
80
|
+
end
|
81
|
+
|
82
|
+
it { should_not be_valid }
|
83
|
+
it { subject.errors.full_messages.should == ["Updates are not allowed."] }
|
84
|
+
end
|
85
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'validates_immutability'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
:adapter => 'sqlite3',
|
6
|
+
:database => ':memory:'
|
7
|
+
)
|
8
|
+
|
9
|
+
ActiveRecord::Schema.define(:version => 0) do
|
10
|
+
create_table :widgets, :force => true do |t|
|
11
|
+
t.string :attr1
|
12
|
+
t.string :attr2
|
13
|
+
t.string :attr3
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "validates_immutability/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "validates_immutability"
|
7
|
+
s.version = ValidatesImmutability::VERSION
|
8
|
+
s.authors = ["Austin Schneider"]
|
9
|
+
s.email = ["soccer022483@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = "Validations for making ActiveRecord objects immutable."
|
12
|
+
s.description = "Validations for making ActiveRecord objects immutable."
|
13
|
+
|
14
|
+
s.rubyforge_project = "validates_immutability"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rspec", "~> 2.6.0"
|
22
|
+
s.add_development_dependency "sqlite3", "~> 1.3.3"
|
23
|
+
s.add_development_dependency "activerecord", "~> 3.0.0"
|
24
|
+
|
25
|
+
s.add_dependency "activesupport", "~> 3.0.0"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: validates_immutability
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Austin Schneider
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-01 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 6
|
31
|
+
- 0
|
32
|
+
version: 2.6.0
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sqlite3
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 3
|
46
|
+
- 3
|
47
|
+
version: 1.3.3
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: activerecord
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 3
|
60
|
+
- 0
|
61
|
+
- 0
|
62
|
+
version: 3.0.0
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: activesupport
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 3
|
75
|
+
- 0
|
76
|
+
- 0
|
77
|
+
version: 3.0.0
|
78
|
+
type: :runtime
|
79
|
+
version_requirements: *id004
|
80
|
+
description: Validations for making ActiveRecord objects immutable.
|
81
|
+
email:
|
82
|
+
- soccer022483@gmail.com
|
83
|
+
executables: []
|
84
|
+
|
85
|
+
extensions: []
|
86
|
+
|
87
|
+
extra_rdoc_files: []
|
88
|
+
|
89
|
+
files:
|
90
|
+
- .gitignore
|
91
|
+
- CHANGELOG
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE
|
94
|
+
- README.rdoc
|
95
|
+
- Rakefile
|
96
|
+
- lib/validates_immutability.rb
|
97
|
+
- lib/validates_immutability/immutable_validator.rb
|
98
|
+
- lib/validates_immutability/version.rb
|
99
|
+
- spec/lib/validates_immutability/immutable_validator_spec.rb
|
100
|
+
- spec/lib/validates_immutability_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- validates_immutability.gemspec
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: ""
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
version: "0"
|
128
|
+
requirements: []
|
129
|
+
|
130
|
+
rubyforge_project: validates_immutability
|
131
|
+
rubygems_version: 1.3.7
|
132
|
+
signing_key:
|
133
|
+
specification_version: 3
|
134
|
+
summary: Validations for making ActiveRecord objects immutable.
|
135
|
+
test_files:
|
136
|
+
- spec/lib/validates_immutability/immutable_validator_spec.rb
|
137
|
+
- spec/lib/validates_immutability_spec.rb
|
138
|
+
- spec/spec_helper.rb
|