activeitem 0.0.22 → 0.0.23
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 +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +32 -0
- data/lib/active_item/base.rb +81 -7
- data/lib/active_item/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3391ecc77f10aaffc7953d95d84691bdf4791ea11163fe45ba7b3b5be3b8418f
|
|
4
|
+
data.tar.gz: 02ad5cbf59628300f250fa972729ad503a53e197824350f7990e7d8cefd4281f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b60d3ec30ab2f3f5a593c320d6af5764e793574b03e4389661325942d9e31d8c7b40b25532816ac0e1a5b8cfd4c5b49650ea413673d2686dd05a3841aeb0ae56
|
|
7
|
+
data.tar.gz: 37398daa0b3af333ed40fb9dc2ad4571d9044df57ebaefe7f20fd21a5c714676cce31925e5b8d5a9a6a847768b15e0e53eca8138ed7b89b38fbf17d8cc71b665
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`read_attribute` / `write_attribute`** — ActiveItem now exposes the same attribute primitives as ActiveRecord. `write_attribute(:name, value)` records the change for dirty tracking; `read_attribute(:name)` returns the current value. Custom readers/writers can route through these instead of touching the ivar directly.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Custom attribute writers now persist on update, Rails-style** — Overriding a generated writer to coerce or normalize a value no longer silently drops the change. Generated readers/writers are now defined in an included module (like Rails' `GeneratedAttributeMethods`), so a hand-written `def foo=` can call `super` and get dirty tracking for free. Previously, a custom writer that assigned `@foo` directly left the attribute out of the changeset — `save` returned `true` with no error, but the update wrote nothing. Both `super` and `write_attribute` are now supported.
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class Article < ActiveItem::Base
|
|
15
|
+
attr_accessor :tags, :payload
|
|
16
|
+
|
|
17
|
+
# Coerce to an Array, Rails-style: override and call super.
|
|
18
|
+
def tags=(value)
|
|
19
|
+
super(Array(value))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Or be explicit with write_attribute.
|
|
23
|
+
def payload=(value)
|
|
24
|
+
write_attribute(:payload, value.is_a?(String) ? value : JSON.generate(value))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
3
29
|
## 0.0.22
|
|
4
30
|
|
|
5
31
|
### Changed
|
data/README.md
CHANGED
|
@@ -114,6 +114,38 @@ class Customer < ActiveItem::Base
|
|
|
114
114
|
end
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Custom Attribute Writers
|
|
118
|
+
|
|
119
|
+
Like ActiveRecord, you can override a generated writer to coerce or normalize a
|
|
120
|
+
value on assignment. Just route the assignment back through the generated
|
|
121
|
+
writer — call `super`, or use `write_attribute`. Both record the change for
|
|
122
|
+
dirty tracking, so `save` (and `update`) persist it. Assigning `@ivar` directly
|
|
123
|
+
bypasses dirty tracking and the change is silently dropped on update.
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
class Article < ActiveItem::Base
|
|
127
|
+
attr_accessor :tags, :payload
|
|
128
|
+
|
|
129
|
+
# Coerce to an Array, Rails-style: override and call super.
|
|
130
|
+
def tags=(value)
|
|
131
|
+
super(Array(value))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Or be explicit with write_attribute.
|
|
135
|
+
def payload=(value)
|
|
136
|
+
write_attribute(:payload, value.is_a?(String) ? value : JSON.generate(value))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`read_attribute(:name)` is available too, for custom readers:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
def display_name
|
|
145
|
+
read_attribute(:name).to_s.strip
|
|
146
|
+
end
|
|
147
|
+
```
|
|
148
|
+
|
|
117
149
|
## License
|
|
118
150
|
|
|
119
151
|
MIT
|
data/lib/active_item/base.rb
CHANGED
|
@@ -100,15 +100,33 @@ module ActiveItem
|
|
|
100
100
|
|
|
101
101
|
define_attribute_methods attr_name
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
# Define the generated reader/writer in a dedicated module that is
|
|
104
|
+
# included into the model, rather than directly on the class. This
|
|
105
|
+
# mirrors Rails' GeneratedAttributeMethods: a hand-written
|
|
106
|
+
# `def foo=` on the model sits *above* the generated writer in the
|
|
107
|
+
# ancestor chain, so a custom writer can call `super` (or
|
|
108
|
+
# `write_attribute`) to get dirty tracking for free.
|
|
109
|
+
generated_attribute_methods.module_eval do
|
|
110
|
+
define_method(attr_name) do
|
|
111
|
+
read_attribute(attr_name)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
define_method("#{attr_name}=") do |value|
|
|
115
|
+
write_attribute(attr_name, value)
|
|
116
|
+
end
|
|
105
117
|
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
106
120
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
# Per-class module that holds the generated attribute reader/writer
|
|
122
|
+
# methods. Included once, ahead of ActiveModel in the ancestor chain but
|
|
123
|
+
# behind the model class itself, so user-defined setters can `super`.
|
|
124
|
+
def generated_attribute_methods
|
|
125
|
+
@generated_attribute_methods ||= begin
|
|
126
|
+
mod = Module.new
|
|
127
|
+
include mod
|
|
128
|
+
|
|
129
|
+
mod
|
|
112
130
|
end
|
|
113
131
|
end
|
|
114
132
|
|
|
@@ -490,6 +508,50 @@ module ActiveItem
|
|
|
490
508
|
end
|
|
491
509
|
end
|
|
492
510
|
|
|
511
|
+
# Read the current value of an attribute by name.
|
|
512
|
+
#
|
|
513
|
+
# This is the Rails-like primitive that generated readers delegate to.
|
|
514
|
+
# Custom readers can call it (or `super`) instead of poking at the ivar
|
|
515
|
+
# directly.
|
|
516
|
+
#
|
|
517
|
+
# def display_name
|
|
518
|
+
# read_attribute(:name).to_s.strip
|
|
519
|
+
# end
|
|
520
|
+
#
|
|
521
|
+
# @param attr_name [String, Symbol] the attribute name
|
|
522
|
+
# @return the attribute's current value
|
|
523
|
+
def read_attribute(attr_name)
|
|
524
|
+
instance_variable_get("@#{attr_name}")
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# Write an attribute value, recording it for dirty tracking so `save`
|
|
528
|
+
# actually persists it.
|
|
529
|
+
#
|
|
530
|
+
# This is the single place dirty tracking happens for attributes, mirroring
|
|
531
|
+
# Rails' `write_attribute`. Generated writers delegate here, which means a
|
|
532
|
+
# custom writer gets dirty tracking for free by going through either
|
|
533
|
+
# `super` or `write_attribute` directly:
|
|
534
|
+
#
|
|
535
|
+
# # coerce/normalize on assignment, Rails-style
|
|
536
|
+
# def tags=(value)
|
|
537
|
+
# super(Array(value)) # -> generated writer -> write_attribute
|
|
538
|
+
# end
|
|
539
|
+
#
|
|
540
|
+
# # or be explicit
|
|
541
|
+
# def payload=(value)
|
|
542
|
+
# write_attribute(:payload, value.is_a?(String) ? value : JSON.generate(value))
|
|
543
|
+
# end
|
|
544
|
+
#
|
|
545
|
+
# @param attr_name [String, Symbol] the attribute name
|
|
546
|
+
# @param value the value to assign
|
|
547
|
+
# @return the assigned value
|
|
548
|
+
def write_attribute(attr_name, value)
|
|
549
|
+
attr_name = attr_name.to_s
|
|
550
|
+
old_value = instance_variable_get("@#{attr_name}")
|
|
551
|
+
mark_attribute_will_change(attr_name, old_value, value)
|
|
552
|
+
instance_variable_set("@#{attr_name}", value)
|
|
553
|
+
end
|
|
554
|
+
|
|
493
555
|
def attribute_changed?(attr_name)
|
|
494
556
|
super(attr_name.to_s)
|
|
495
557
|
end
|
|
@@ -511,6 +573,18 @@ module ActiveItem
|
|
|
511
573
|
|
|
512
574
|
private
|
|
513
575
|
|
|
576
|
+
# Record a pending change for dirty tracking. Guarded with `respond_to?`
|
|
577
|
+
# so it degrades gracefully for attributes that weren't declared via
|
|
578
|
+
# `attr_accessor` (and under test harnesses that stub the ORM), and skips
|
|
579
|
+
# re-recording an original value that's already been captured.
|
|
580
|
+
def mark_attribute_will_change(attr_name, old_value, new_value)
|
|
581
|
+
return if old_value == new_value
|
|
582
|
+
return if changed_attributes.key?(attr_name)
|
|
583
|
+
return unless respond_to?("#{attr_name}_will_change!", true)
|
|
584
|
+
|
|
585
|
+
send("#{attr_name}_will_change!")
|
|
586
|
+
end
|
|
587
|
+
|
|
514
588
|
# Enroll this record's save operation in the current transaction.
|
|
515
589
|
# Called when save is invoked inside a transaction block.
|
|
516
590
|
def enroll_in_transaction
|
data/lib/active_item/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeitem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-09-
|
|
12
|
+
date: 2026-09-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activemodel
|