acts_as_immutable 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22f871d40f67984cf982e43abc6c47fa14a9deb57c68da8ecb811b0d78a8b9c9
4
+ data.tar.gz: 035126052b57c775b7826df690acbf195cde7c2e17bec7eb9aede1d5865e8f2a
5
+ SHA512:
6
+ metadata.gz: b531bae10612dbb5e84be6ca6a4bb4d20dbf0345c5e543174bd0cbe3a181c24166a7b943f802137a68448b8789a26361279d394ff20ecec1f443a45a674375ba
7
+ data.tar.gz: 2a5aa95e5ad2844947ea45d6837b43b4bd4fcec3a5224cc44561b8383075bcd73d4535741c79f1143ad79238038da3dbe5b3b05301b266cf20277b4221d918e5
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tags
6
+ *.swp
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1
5
+ - jruby-19mode
6
+ - rbx
7
+ gemfile:
8
+ - gemfiles/active_record_32.gemfile
9
+ - gemfiles/active_record_42.gemfile
10
+ # use Travis containers for faster builds
11
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'sqlite3', :platforms => [:ruby]
4
+ gem 'activerecord-jdbcsqlite3-adapter', :platforms => [:jruby]
5
+
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :minitest do
2
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
3
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
4
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
5
+ end
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ Acts As Immutable
2
+ =================
3
+
4
+ A Rails plugin that will ensure an ActiveRecord object is immutable once
5
+ saved. Optionally, you can specify attributes to be mutable if the object
6
+ is in a particular state (block evaluates to true).
7
+
8
+ Authored by: NuLayer Inc. / www.nulayer.com
9
+
10
+
11
+ Install
12
+ -------
13
+ ```ruby
14
+ gem 'acts_as_immutable', github:'Dealermade/acts_as_immutable'
15
+ ```
16
+
17
+ Usage examples
18
+ --------------
19
+
20
+ Ex. 1 All attributes will be immutable
21
+ ```ruby
22
+ class PaymentTransaction < ActiveRecord::Base
23
+ acts_as_immutable
24
+ end
25
+ ```
26
+
27
+ Ex. 2
28
+ All attributes will be immutable except 'paid',
29
+ ie. if the invoice is still outstanding
30
+ ```ruby
31
+ class Invoice < ActiveRecord::Base
32
+ acts_as_immutable :paid do
33
+ paid == false
34
+ end
35
+ end
36
+ ```
37
+
38
+ Ex. 3
39
+ Objects are immutable unless the subscription is
40
+ active or free in which case changes can be made
41
+ to: status, free and updated_at
42
+ ```ruby
43
+ class Subscription < ActiveRecord::Base
44
+ acts_as_immutable :status, :free, :updated_at do
45
+ free? || active?
46
+ end
47
+
48
+ def active?
49
+ status == "active"
50
+ end
51
+ end
52
+ ```
53
+
54
+ ```bash
55
+ > sub = Subscription.new
56
+ > sub.status = "active"
57
+ > sub.free = false
58
+ > sub.start_date = Time.now
59
+ > sub.end_date = Time.now + 1.year
60
+ > sub.save
61
+ => true
62
+ > sub.start_date = Time.now + 1.week
63
+ => ActiveRecord::ActsAsImmutableError: start_date is an immutable attribute
64
+
65
+ > sub.update_attributes({ :start_date => Time.now + 1.week })
66
+ => ActiveRecord::ActsAsImmutableError: start_date is an immutable attribute
67
+
68
+ > sub.status = "cancelled"
69
+ > sub.save
70
+ => true
71
+ ```
72
+
73
+ Testing
74
+ -------
75
+
76
+ Tested on Rails 4.2.0. To run the tests, invoke `rake` or `guard`.
77
+
78
+
79
+ License (MIT)
80
+ -------------
81
+ Copyright (c) 2009 NuLayer Inc.
82
+
83
+ Permission is hereby granted, free of charge, to any person obtaining a copy
84
+ of this software and associated documentation files (the "Software"), to deal
85
+ in the Software without restriction, including without limitation the rights
86
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
87
+ copies of the Software, and to permit persons to whom the Software is
88
+ furnished to do so, subject to the following conditions:
89
+
90
+ The above copyright notice and this permission notice shall be included in
91
+ all copies or substantial portions of the Software.
92
+
93
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
94
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
95
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
96
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
97
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
98
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
99
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/test_*.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: [:test]
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_immutable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "acts_as_immutable"
7
+ s.version = ActsAsImmutable::VERSION
8
+ s.authors = ["Simon Mathieu", "NuLayer Inc."]
9
+ s.email = ["simon@pagerduty.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Immutable ActiveRecord models}
12
+ s.description = %q{A Rails plugin that will ensure an ActiveRecord object is immutable once
13
+ saved. Optionally, you can specify attributes to be mutable if the object
14
+ is in a particular state (block evaluates to true).}
15
+
16
+ s.rubyforge_project = "acts_as_immutable"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "activerecord", '4.2.0'
25
+ s.add_development_dependency "minitest"
26
+ s.add_development_dependency "guard"
27
+ s.add_development_dependency "guard-minitest"
28
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.2.22', :require => 'active_record'
4
+
5
+ gem 'sqlite3', :platforms => [:ruby]
6
+ gem 'activerecord-jdbcsqlite3-adapter', :platforms => [:jruby]
7
+
8
+ gemspec :path => '../'
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 4.2.5', :require => 'active_record'
4
+
5
+ gem 'sqlite3', :platforms => [:ruby]
6
+ gem 'activerecord-jdbcsqlite3-adapter', :platforms => [:jruby]
7
+
8
+ gemspec :path => '../'
@@ -0,0 +1,73 @@
1
+ # +---------------------------------------------------------------------------+
2
+ # | Acts As Immutable |
3
+ # +---------------------------------------------------------------------------+
4
+ # | A Rails plugin that will ensure an ActiveRecord object is immutable once |
5
+ # | saved. Optionally, you can specify attributes to be mutable if the object |
6
+ # | is in a particular state (block evaluates to true). |
7
+ # +---------------------------------------------------------------------------+
8
+ # | Author: NuLayer Inc. / www.nulayer.com |
9
+ # +---------------------------------------------------------------------------+
10
+
11
+ module ActsAsImmutable
12
+ def self.included(base)
13
+ base.extend ClassMethods
14
+ base.send :include, InstanceMethods
15
+ end
16
+
17
+ module InstanceMethods
18
+ def mutable?
19
+ cond = self.class.mutable_condition
20
+ options = self.class.mutable_options
21
+ (options[:new_records_mutable] && new_record?) || (cond && instance_eval(&cond))
22
+ end
23
+
24
+ def immutable?
25
+ !mutable?
26
+ end
27
+
28
+ protected
29
+
30
+ def validate_immutability
31
+ changed_attrs = self.changes.keys.map(&:to_sym)
32
+ (changed_attrs - self.class.mutable_attributes).each do |attr|
33
+ if immutable?
34
+ self.errors.add(attr, "is immutable")
35
+ end
36
+ end
37
+ end
38
+
39
+ def validate_immutability_destroy
40
+ if immutable?
41
+ errors.add(:base, "Record is immutable")
42
+ false
43
+ else
44
+ true
45
+ end
46
+ end
47
+ end
48
+
49
+ module ClassMethods
50
+ def self.extended(base)
51
+ base.class_attribute :mutable_attributes
52
+ base.class_attribute :mutable_condition
53
+ base.class_attribute :mutable_options
54
+ end
55
+
56
+ def acts_as_immutable(*mutable_attributes, &condition)
57
+ options = {:new_records_mutable => true}
58
+ options.merge!(mutable_attributes.pop) if mutable_attributes.last.is_a?(Hash)
59
+ self.mutable_attributes = mutable_attributes
60
+ self.mutable_condition = condition
61
+ self.mutable_options = options
62
+
63
+ self.validate :validate_immutability
64
+ self.before_destroy :validate_immutability_destroy
65
+ end
66
+
67
+ def mutable_attributes
68
+ self.mutable_attributes || []
69
+ end
70
+ end
71
+ end
72
+
73
+ ActiveRecord::Base.send :include, ActsAsImmutable
@@ -0,0 +1,3 @@
1
+ module ActsAsImmutable
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,130 @@
1
+ require 'test_helper'
2
+
3
+ class ActsAsImmutableUsingVirtualField < MiniTest::Test
4
+ class Payment < ActiveRecord::Base
5
+ attr_accessor :record_locked
6
+
7
+ after_initialize :lock
8
+
9
+ acts_as_immutable(:status, :amount) do
10
+ !record_locked
11
+ end
12
+
13
+ def lock
14
+ self.record_locked = true
15
+ end
16
+ end
17
+
18
+ class Payment2 < ActiveRecord::Base
19
+ self.table_name = :payments
20
+
21
+ attr_accessor :record_locked
22
+
23
+ after_initialize :lock
24
+
25
+ acts_as_immutable do
26
+ !record_locked
27
+ end
28
+
29
+ def lock
30
+ self.record_locked = true
31
+ end
32
+ end
33
+
34
+ class PaymentLockOnNew < ActiveRecord::Base
35
+ self.table_name = :payments
36
+
37
+ attr_accessor :record_locked
38
+
39
+ after_initialize :lock
40
+
41
+ acts_as_immutable :new_records_mutable => false do
42
+ !record_locked
43
+ end
44
+
45
+ def lock
46
+ self.record_locked = true
47
+ end
48
+ end
49
+
50
+ def test_is_mutable
51
+ p = Payment.create!(:customer => "Valentin", :status => "success", :amount => 5.00)
52
+ refute p.mutable?
53
+ p.record_locked = false
54
+ assert p.mutable?
55
+ end
56
+
57
+ def test_is_immutable
58
+ p = Payment.create!(:customer => "Valentin", :status => "success", :amount => 5.00)
59
+ assert p.immutable?
60
+ p.record_locked = false
61
+ refute p.immutable?
62
+ end
63
+
64
+ def test_writing_attributes_without_white_list
65
+ p = Payment2.create!(:customer => "Valentin", :status => "success", :amount => 5.00)
66
+ p.customer = 'test'
67
+ assert_error_on p, :customer
68
+
69
+ p.record_locked = false
70
+ assert_no_error_on p, :customer
71
+ end
72
+
73
+ def test_writing_mutable_attributes
74
+ p = Payment.create!(:customer => "Valentin", :status => "success", :amount => 5.00)
75
+ p.status = 'fail'
76
+ assert_valid p
77
+ end
78
+
79
+ def test_destroy
80
+ p = Payment2.create!(:customer => "Valentin", :status => "success", :amount => 5.00)
81
+ assert_valid p
82
+ p.destroy
83
+ assert_not_nil p.errors.get(:base)
84
+ assert_not_nil Payment2.find_by_id(p.id)
85
+
86
+ p.record_locked = false
87
+ p.destroy
88
+ assert_nil Payment2.find_by_id(p.id)
89
+
90
+ p = Payment2.new(:customer => "Valentin", :status => "success", :amount => 5.00)
91
+ p.destroy
92
+ end
93
+
94
+ def test_new_records_should_be_mutable
95
+ assert Payment.new.mutable?
96
+ end
97
+
98
+ def test_new_records_with_lock_on_new_should_not_be_mutable
99
+ refute PaymentLockOnNew.new.mutable?
100
+ p = PaymentLockOnNew.new(:customer => "Valentin", :status => "success", :amount => 5.00)
101
+ refute p.valid?
102
+ end
103
+
104
+ def test_new_records_with_lock_on_new_should_be_mutable_if_condition_is_met
105
+ p = PaymentLockOnNew.new
106
+ p.record_locked = false
107
+ assert p.mutable?
108
+ end
109
+
110
+ private
111
+
112
+ def assert_error_on(object, association)
113
+ object.valid?
114
+ assert_not_nil object.errors.get(association)
115
+ end
116
+
117
+ def assert_no_error_on(object, association)
118
+ object.valid?
119
+ assert_nil object.errors.get(association)
120
+ end
121
+
122
+ def assert_valid(object)
123
+ assert object.valid?, "#{object.errors.full_messages.to_sentence}"
124
+ end
125
+
126
+ def assert_not_nil(exp, msg = nil)
127
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
128
+ refute exp.nil?, msg
129
+ end
130
+ end
@@ -0,0 +1,22 @@
1
+ # require 'rails'
2
+ # require 'rails/test_help'
3
+ require 'active_record'
4
+ require 'minitest/autorun'
5
+
6
+ $: << 'lib'
7
+
8
+ require 'acts_as_immutable'
9
+
10
+ ActiveRecord::Base.establish_connection(
11
+ adapter: "sqlite3",
12
+ database: ":memory:")
13
+
14
+ silence_stream(STDOUT) do
15
+ ActiveRecord::Schema.define do
16
+ create_table :payments, :force => true do |t|
17
+ t.string :customer
18
+ t.decimal :amount
19
+ t.string :status
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_immutable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Simon Mathieu
8
+ - NuLayer Inc.
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-07-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activerecord
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '='
33
+ - !ruby/object:Gem::Version
34
+ version: 4.2.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '='
40
+ - !ruby/object:Gem::Version
41
+ version: 4.2.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: minitest
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: guard
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: guard-minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: |-
85
+ A Rails plugin that will ensure an ActiveRecord object is immutable once
86
+ saved. Optionally, you can specify attributes to be mutable if the object
87
+ is in a particular state (block evaluates to true).
88
+ email:
89
+ - simon@pagerduty.com
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".gitignore"
95
+ - ".travis.yml"
96
+ - Gemfile
97
+ - Guardfile
98
+ - README.md
99
+ - Rakefile
100
+ - acts_as_immutable.gemspec
101
+ - gemfiles/active_record_32.gemfile
102
+ - gemfiles/active_record_42.gemfile
103
+ - lib/acts_as_immutable.rb
104
+ - lib/acts_as_immutable/version.rb
105
+ - test/lib/test_acts_as_immutable.rb
106
+ - test/test_helper.rb
107
+ homepage: ''
108
+ licenses: []
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project: acts_as_immutable
126
+ rubygems_version: 2.7.3
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Immutable ActiveRecord models
130
+ test_files:
131
+ - test/lib/test_acts_as_immutable.rb
132
+ - test/test_helper.rb