attribute_guard 1.0.1 → 1.1.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.
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: []