attribute_guard 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00ca78d9fb2f442ce8c5d58b0950e6ae2406e7317cc0060b572083d85705b247
4
- data.tar.gz: 447523ace443fcdeeb1312ea118e2c39dc0e8195cecb5e647aed1f70f4cb0c41
3
+ metadata.gz: fa71cf702b0f01ded796adf8ba3f215d024cf7a7947d633450213266df33cd74
4
+ data.tar.gz: 96bb1d220b952c302e9c749378be645c47fa66f49a0860d8b110ce49d2d1f0cb
5
5
  SHA512:
6
- metadata.gz: d4c1f7e90bb839de650cf4f272c6269106136bd03e91f86674bd73b9d00c3da76ec43741b8389c57d4fc9cef5d505e27ced1e548e823941b34e89048a583fee6
7
- data.tar.gz: 11a93fe37a9db10b1de8e41e511f06d80abbc4cbe2670f193f6d7208cd9247218f6245c9d696aef716da9e55e5a26fd24e1a4a4112cb4f634620df66972d3413
6
+ metadata.gz: 574fcacc35b3a9c4029999e6e9a8c89ae8d35c1f22e6187ebafbea27079e0f812a23bbf93fb42ccd667f570fa3060fba6f3bdc6e2fc1a3411bccb4066345710e
7
+ data.tar.gz: f3b1df97332fdfeed565332fb0be50a53811e95d3661ee0ca1988406ffe690e9cca734a2b6a136d4eacaddf2b995cf9d6c8eaa18d1647aa52ce39e6d96606c49
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.1.0
8
+
9
+ ### Added
10
+
11
+ - Added :raise mode that raises an error if a locked attribute has been changed instead of adding a validation error.
12
+
13
+ ### Changed
14
+
15
+ - Changed gem dependency from `activerecord` to `activemodel`. You can now use locked attributes with ActiveModel classes that include `ActiveModel::Validations` and `ActiveModel::Dirty and implement a `new_record?` method.
16
+
7
17
  ## 1.0.1
8
18
 
9
19
  ### Added
data/README.md CHANGED
@@ -3,8 +3,9 @@
3
3
  [![Continuous Integration](https://github.com/bdurand/attribute_guard/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/attribute_guard/actions/workflows/continuous_integration.yml)
4
4
  [![Regression Test](https://github.com/bdurand/attribute_guard/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/attribute_guard/actions/workflows/regression_test.yml)
5
5
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
6
+ [![Gem Version](https://badge.fury.io/rb/attribute_guard.svg)](https://badge.fury.io/rb/attribute_guard)
6
7
 
7
- This Ruby gem provides an extension for ActiveRecord allowing you to declare certain attributes in a model to be locked. Locked attributes cannot be changed once a record is created unless you explicitly allow changes.
8
+ This Ruby gem provides an extension for ActiveRecord/ActiveModel allowing you to declare certain attributes in a model to be locked. Locked attributes cannot be changed once a record is created unless you explicitly allow changes.
8
9
 
9
10
  This feature can be used for a couple of different purposes.
10
11
 
@@ -118,7 +119,7 @@ record.update!(status: "canceled") # raises ActiveRecord::RecordInvalid error
118
119
 
119
120
  ### Modes
120
121
 
121
- The default behavior when a locked attribute is changed is to add a validation error to the record. You can change this behavior with the `mode` option when locking attributes.
122
+ The default behavior when a locked attribute is changed is to add a validation error to the record. You can change this behavior with the `mode` option when locking attributes. You still need to validate the record to trigger the locked attribute check, regardless of the mode.
122
123
 
123
124
  ```ruby
124
125
  class MyModel
@@ -126,16 +127,23 @@ class MyModel
126
127
 
127
128
  lock_attributes :email, mode: :error
128
129
  lock_attributes :name: mode: :warn
130
+ lock_attributes :updated_at, mode: :raise
129
131
  lock_attributes :created_at, mode: ->(record, attribute) { raise "Created timestamp cannot be changed" }
130
132
  end
131
133
  ```
132
134
 
133
135
  * `:error` - Add a validation error to the record. This is the default.
134
136
 
135
- * `:warn` - Log a warning that the record was changed. This mode is useful to allow you soft deploy locked attributes to production on a mature project and give you information about where you may need to update code to unlock attributes.
137
+ * `:warn` - Log a warning that the record was changed. This mode is useful to allow you soft deploy locked attributes to production on a mature project and give you information about where you may need to update code to unlock attributes. If the model does not have a `logger` method that returns a `Logger`-like object, then the output will be sent to `STDERR`.
138
+
139
+ * `:raise` = Raise an `AttributeGuard::LockedAttributeError` error.
136
140
 
137
141
  * `Proc` - If you provide a `Proc` object, it will be called with the record and the attribute name when a locked attribute is changed.
138
142
 
143
+ ### Using with ActiveModel
144
+
145
+ The gem works out of the box with ActiveRecord. You can also use it with ActiveModel classes as long as they include the `ActiveModel::Validations` and `ActiveModel::Dirty` modules. The model also needs to implement a `new_record?` method.
146
+
139
147
  ## Installation
140
148
 
141
149
  Add this line to your application's Gemfile:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.0
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
4
4
  spec.authors = ["Brian Durand"]
5
5
  spec.email = ["bbdurand@gmail.com"]
6
6
 
7
- spec.summary = "ActiveRecord extension that allows locking attributes to prevent unintended updates."
7
+ spec.summary = "ActiveRecord/ActiveModel extension that allows locking attributes to prevent unintended updates."
8
8
 
9
9
  spec.homepage = "https://github.com/bdurand/attribute_guard"
10
10
  spec.license = "MIT"
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.required_ruby_version = ">= 2.5"
33
33
 
34
- spec.add_dependency "activerecord", ">= 5.0"
34
+ spec.add_dependency "activemodel", ">= 5.2"
35
35
 
36
36
  spec.add_development_dependency "bundler"
37
37
  end
@@ -36,6 +36,9 @@ end
36
36
  module AttributeGuard
37
37
  extend ActiveSupport::Concern
38
38
 
39
+ class LockedAttributeError < StandardError
40
+ end
41
+
39
42
  included do
40
43
  class_attribute :locked_attributes, default: {}, instance_accessor: false
41
44
  private_class_method :locked_attributes=
@@ -56,13 +59,19 @@ module AttributeGuard
56
59
  # Validator that checks for changes to locked attributes.
57
60
  class LockedAttributesValidator < ActiveModel::Validator
58
61
  def validate(record)
62
+ unless record.respond_to?(:new_record?)
63
+ raise "AttributeGuard can only be used with models that respond to :new_record?"
64
+ end
65
+
59
66
  return if record.new_record?
60
67
 
61
68
  record.class.send(:locked_attributes).each do |attribute, params|
62
69
  if record.changes.include?(attribute) && record.attribute_locked?(attribute)
63
70
  message, mode = params
64
71
  if mode == :warn
65
- record&.logger&.warn("Changed locked attribute #{attribute} on #{record.class.name} with id #{record.id}")
72
+ log_warning(record, attribute)
73
+ elsif mode == :raise
74
+ raise LockedAttributeError.new(error_message(record, attribute))
66
75
  elsif mode.is_a?(Proc)
67
76
  mode.call(record, attribute)
68
77
  else
@@ -71,6 +80,21 @@ module AttributeGuard
71
80
  end
72
81
  end
73
82
  end
83
+
84
+ private
85
+
86
+ def error_message(record, attribute)
87
+ "Changed locked attribute #{attribute} on #{record.class.name} with id #{record.id}"
88
+ end
89
+
90
+ def log_warning(record, attribute)
91
+ message = error_message(record, attribute)
92
+ if record.respond_to?(:logger) && record.logger.respond_to?(:warn)
93
+ record.logger.warn(message)
94
+ else
95
+ warn(message)
96
+ end
97
+ end
74
98
  end
75
99
 
76
100
  module ClassMethods
@@ -113,7 +137,7 @@ module AttributeGuard
113
137
  # user.unlock_attributes(:email).update!(email: "user@example.com")
114
138
  #
115
139
  # @param attributes [Array<Symbol, String>] the attributes to unlock
116
- # @return [ActiveRecord::Base] the object itself
140
+ # @return [Object] the object itself
117
141
  def unlock_attributes(*attributes)
118
142
  attributes = attributes.flatten.map(&:to_s)
119
143
  return if attributes.empty?
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribute_guard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-11 00:00:00.000000000 Z
11
+ date: 2024-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
14
+ name: activemodel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -74,6 +74,6 @@ requirements: []
74
74
  rubygems_version: 3.4.12
75
75
  signing_key:
76
76
  specification_version: 4
77
- summary: ActiveRecord extension that allows locking attributes to prevent unintended
78
- updates.
77
+ summary: ActiveRecord/ActiveModel extension that allows locking attributes to prevent
78
+ unintended updates.
79
79
  test_files: []