passive_model 1.0.2 → 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: 92b215b614d68f0e42bf4f96475a4c84ee331f3cb9e48c3627d1dc6bf0cf25c3
4
- data.tar.gz: cb7d3782cab1e2d86390dfad69ede00f0e7176ef092b75327f01aec1d015c5fb
3
+ metadata.gz: 3c2b1ac63fdec874fc2f59c9d9bedd53f40dfec303cf9c045ce7f922cb312679
4
+ data.tar.gz: e6344a1d4168704cdc9160ebb5fcfbd6bc9d98e661c0c1c6e9feeffa7cb28648
5
5
  SHA512:
6
- metadata.gz: 33f7f75e0bb079a39e49f3ea5cd6396e2e299e3dd31e32c7732ff43efe9df65141178ae1a8f8567bb4cae44c37b8571c207b3e1aa901c8eef818f2f955f3fee4
7
- data.tar.gz: c1f82c6c0e0b372aa9bd1cd9b56091bc024fa01729cce504873756fa43c2d00583689ca6532da2ac53f497c1e5d9cf0ac14e5b62ff509bc7519629172fc6e596
6
+ metadata.gz: c1c90541613b89b7704700d32a48fef9bb0adb9eeee6862f65129b71f1547a77a3c98d4ec337184381a6bbc3a5a24cdc4764d4f22424145a40b32f0cf157ccc1
7
+ data.tar.gz: ddacc10308fee161f2e1d059fb8e96736700d3529466a825df52deffc5a5b67a09bcc80a6a3848ab8d1e3c70db49c5306ba5b92ab903fff8c47bd1f499912c3f
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0 - 2026-06-14
4
+
5
+ - Require `active_model` directly when loading `passive_model`, so the gem boots
6
+ without relying on the host Rails application to preload ActiveModel.
7
+ - Replace the broad `rails` runtime dependency with `activemodel`.
8
+ - Align the gem's Ruby requirement with ActiveModel 7.
9
+ - Change `save!` to raise `PassiveModel::ValidationError`, an
10
+ `ActiveModel::ValidationError` subclass, instead of
11
+ `ActiveRecord::RecordInvalid`.
12
+ - Store `before_save` callbacks per subclass so callbacks do not leak between
13
+ unrelated `PassiveModel::Base` subclasses.
14
+ - Add focused tests for independent boot, validations, `save`, `save!`, and
15
+ callback isolation.
data/README.md CHANGED
@@ -1,95 +1,162 @@
1
1
  # PassiveModel
2
2
 
3
- This is a gem for using Rails ActiveModel functionalities without having an actual database table.
3
+ PassiveModel provides lightweight model objects for Rails applications when you
4
+ want ActiveModel-style behavior without a database table.
4
5
 
5
- Supports validation, callbacks etc. And behaves like a model object, so can be replaced with actual model objects.
6
+ It is useful for form objects, service inputs, or other plain Ruby objects that
7
+ need validations, naming, translation, conversion, and callback hooks similar to
8
+ Active Record models.
6
9
 
7
10
  ## Installation
8
11
 
9
12
  Add this line to your application's Gemfile:
10
13
 
11
14
  ```ruby
12
- gem 'passive_model'
15
+ gem "passive_model"
13
16
  ```
14
17
 
15
18
  And then execute:
16
19
 
17
- $ bundle install
20
+ ```sh
21
+ bundle install
22
+ ```
18
23
 
19
- Or install it yourself as:
24
+ Or install it yourself:
20
25
 
21
- $ gem install passive_model
26
+ ```sh
27
+ gem install passive_model
28
+ ```
22
29
 
23
- ## Usage
30
+ PassiveModel depends on ActiveModel 7.0 or newer. It does not depend on Rails or
31
+ ActiveRecord.
24
32
 
25
- Create a class from `PassiveModel::Base`.
33
+ ## Usage
26
34
 
27
- For example:
35
+ Create a class that inherits from `PassiveModel::Base` and define the attributes
36
+ your object exposes.
28
37
 
29
- ```erbruby
38
+ ```ruby
30
39
  class ContactForm < PassiveModel::Base
31
40
  attr_accessor :first_name, :last_name
32
41
 
33
42
  validates :first_name, presence: true
34
43
  validates :last_name, presence: true
35
44
  validate :first_name_is_not_spam
36
-
45
+
37
46
  private
38
-
47
+
39
48
  def first_name_is_not_spam
40
- return if first_name.downcase != 'test'
49
+ return unless first_name.to_s.downcase == "test"
41
50
 
42
- errors.add(:first_name, 'is not valid')
51
+ errors.add(:first_name, "is not valid")
43
52
  end
44
53
  end
45
54
  ```
46
55
 
47
- #### Create new objects
56
+ ### Creating Objects
48
57
 
49
- `form = ContactForm.new(first_name: 'John', last_name: 'Doe')`
58
+ Pass initial attributes to `.new`:
50
59
 
51
- #### Set attributes
60
+ ```ruby
61
+ form = ContactForm.new(first_name: "John", last_name: "Doe")
62
+ ```
52
63
 
53
- `form.attributes = { first_name: 'Lewis' }`
64
+ ### Assigning Attributes
54
65
 
55
- #### Emulate a save
66
+ Assign multiple attributes with `attributes=`:
56
67
 
57
- It runs the validations and also calls `before_save` callbacks if the object is valid
68
+ ```ruby
69
+ form.attributes = { first_name: "Lewis" }
70
+ ```
71
+
72
+ The current implementation copies each key directly to an instance variable. For
73
+ example, `first_name: "Lewis"` sets `@first_name`. Define `attr_accessor`,
74
+ `attr_reader`, or explicit methods for values you need to read later.
58
75
 
59
- `form.save # returns true if valid?`
76
+ This assignment style does not call custom setters and does not reject unknown
77
+ attribute names.
60
78
 
61
- #### Emulate a save!
79
+ ### Validations
62
80
 
63
- Raise an error if cannot be saved
81
+ `PassiveModel::Base` includes ActiveModel validations, so standard Rails
82
+ validators work:
64
83
 
65
- `form.save!`
84
+ ```ruby
85
+ form = ContactForm.new(last_name: "Doe")
86
+
87
+ form.valid? # => false
88
+ form.errors[:first_name] # => ["can't be blank"]
89
+ ```
66
90
 
67
- #### persisted?
91
+ ActiveModel validation callbacks such as `before_validation` and
92
+ `after_validation` are also available.
68
93
 
69
- Return if save has been called
94
+ ### Save
70
95
 
71
- `form.persisted?`
96
+ `save` validates the object. If the object is invalid, it returns `false`. If the
97
+ object is valid, it runs registered `before_save` callbacks and returns `true`.
98
+
99
+ ```ruby
100
+ invalid_form = ContactForm.new(last_name: "Doe")
101
+ valid_form = ContactForm.new(first_name: "John", last_name: "Doe")
102
+
103
+ invalid_form.save # => false
104
+ valid_form.save # => true
105
+ ```
72
106
 
73
- #### before_save callback
107
+ ### Save Bang
74
108
 
75
- Add a callback as follows:
109
+ `save!` calls `save` and raises `PassiveModel::ValidationError` when the object
110
+ cannot be saved. `PassiveModel::ValidationError` inherits from
111
+ `ActiveModel::ValidationError`; it does not require ActiveRecord.
76
112
 
77
- ```erbruby
113
+ ```ruby
114
+ form = ContactForm.new
115
+
116
+ form.save! # raises PassiveModel::ValidationError
117
+ ```
118
+
119
+ ### before_save Callback
120
+
121
+ Register a `before_save` callback with the class method:
122
+
123
+ ```ruby
78
124
  class ContactForm < PassiveModel::Base
125
+ attr_accessor :first_name
126
+
127
+ validates :first_name, presence: true
128
+
79
129
  before_save :send_info_to_mailchimp
80
-
81
- validate :first_name, presence: true
82
-
130
+
83
131
  private
84
-
132
+
85
133
  def send_info_to_mailchimp
86
- # API call
134
+ # API call
87
135
  end
88
136
  end
89
137
  ```
90
138
 
91
- The callback will be executed only if validations are clear.
139
+ The callback is executed only after validations pass.
140
+
141
+ `before_save` callbacks are stored per class, so callbacks registered on one
142
+ `PassiveModel::Base` subclass do not run for unrelated subclasses.
143
+
144
+ ### persisted?
145
+
146
+ `persisted?` is intended to report whether `save` has been called successfully.
147
+ In the current implementation it returns `false` unless an instance sets
148
+ `@persisted` itself.
149
+
150
+ ## Behavior Notes
151
+
152
+ These notes describe the current gem behavior and should be reviewed before
153
+ changing runtime code:
154
+
155
+ - `persisted?` does not currently switch to `true` after a successful `save`.
156
+ - Attribute assignment writes instance variables directly instead of using
157
+ ActiveModel attributes or custom setters.
92
158
 
93
159
  ## Contributing
94
160
 
95
- If you need more functionality, for bug reports raise a request at https://github.com/RocketApex/passive_model
161
+ For bug reports or feature requests, open an issue at
162
+ https://github.com/RocketApex/passive_model.
@@ -12,6 +12,27 @@ module PassiveModel
12
12
  include ActiveModel::Validations::Callbacks
13
13
  include ActiveModel::Conversion
14
14
  extend ActiveModel::Naming
15
+ extend ActiveModel::Translation
16
+
17
+ class << self
18
+ def before_save(name)
19
+ callback = name.to_sym
20
+
21
+ before_save_callbacks << callback unless before_save_callbacks.include?(callback)
22
+ end
23
+
24
+ def inherited(subclass)
25
+ super
26
+
27
+ subclass.instance_variable_set(:@before_save_callbacks, before_save_callbacks.dup)
28
+ end
29
+
30
+ private
31
+
32
+ def before_save_callbacks
33
+ @before_save_callbacks ||= []
34
+ end
35
+ end
15
36
 
16
37
  def initialize(hash = {})
17
38
  set_attributes(hash)
@@ -24,12 +45,12 @@ module PassiveModel
24
45
  def save
25
46
  return false unless self.valid?
26
47
 
27
- execute_callbacks(@@before_save_callbacks)
48
+ execute_callbacks(before_save_callbacks)
28
49
  true
29
50
  end
30
51
 
31
52
  def save!
32
- raise(ActiveRecord::RecordInvalid) unless self.save
53
+ raise(PassiveModel::ValidationError.new(self)) unless self.save
33
54
  end
34
55
 
35
56
  def persisted?
@@ -44,9 +65,8 @@ module PassiveModel
44
65
  end
45
66
  end
46
67
 
47
- def self.before_save(name)
48
- @@before_save_callbacks ||= []
49
- @@before_save_callbacks << name unless @@before_save_callbacks.include?(name)
68
+ def before_save_callbacks
69
+ self.class.send(:before_save_callbacks)
50
70
  end
51
71
 
52
72
  def set_attributes(hash)
@@ -1,3 +1,3 @@
1
1
  module PassiveModel
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/passive_model.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require "passive_model/version"
2
- require "passive_model/base"
2
+ require "active_model"
3
3
 
4
4
  module PassiveModel
5
5
  class Error < StandardError; end
6
+ class ValidationError < ActiveModel::ValidationError; end
6
7
  end
8
+
9
+ require "passive_model/base"
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passive_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jey Geethan
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-06-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: rails
13
+ name: activemodel
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ">="
@@ -32,6 +31,7 @@ executables: []
32
31
  extensions: []
33
32
  extra_rdoc_files: []
34
33
  files:
34
+ - CHANGELOG.md
35
35
  - MIT-LICENSE
36
36
  - README.md
37
37
  - Rakefile
@@ -44,9 +44,8 @@ licenses:
44
44
  metadata:
45
45
  allowed_push_host: https://rubygems.org
46
46
  homepage_uri: https://github.com/RocketApex/passive_model
47
- source_code_uri: https://github.com/RocketApex/passive_model
48
- changelog_uri: https://github.com/RocketApex/passive_model
49
- post_install_message:
47
+ source_code_uri: https://github.com/RocketApex/passive_model/tree/main
48
+ changelog_uri: https://github.com/RocketApex/passive_model/blob/main/CHANGELOG.md
50
49
  rdoc_options: []
51
50
  require_paths:
52
51
  - lib
@@ -54,15 +53,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
53
  requirements:
55
54
  - - ">="
56
55
  - !ruby/object:Gem::Version
57
- version: 2.3.0
56
+ version: 2.7.0
58
57
  required_rubygems_version: !ruby/object:Gem::Requirement
59
58
  requirements:
60
59
  - - ">="
61
60
  - !ruby/object:Gem::Version
62
61
  version: '0'
63
62
  requirements: []
64
- rubygems_version: 3.3.7
65
- signing_key:
63
+ rubygems_version: 4.0.11
66
64
  specification_version: 4
67
65
  summary: For using models without saving to the database
68
66
  test_files: []