standard_procedure_has_attributes 0.1.0 → 0.2.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: 1fd9d76cb1b8d6eca0e2be02212fb237a1f49c1c014010dc71ddca9678ea247a
4
- data.tar.gz: f66ffafac490c8ad1d0915b20bcd56caa2be7eb0a622134785f00c0c4d6f779b
3
+ metadata.gz: 95b72e89130c7a9ae47a13d0494fc1db6e226270afd199d485784b00a0d8c1d9
4
+ data.tar.gz: 74bb291b713e6411ec4beafc93037b3c820f5a43679a2ed14dbf002cbf241e1a
5
5
  SHA512:
6
- metadata.gz: bf4fe8a2fd19fadd3a8f6838e1ff2df9798a4880049cf36f46be7a39402abe5bc9fd0d7f7c940deb13d22aa6661c902dea5c38dd97bb4826de8812883787c40a
7
- data.tar.gz: 7527db1e32925575be31abd9f887231245782d31f86df335d543cc62924d304724d1c84552ae22131c193c751257da228f3080cfc96ccafceed6e36165534bef
6
+ metadata.gz: 454619f884749f7b3574ab27849b7a05af02fb2771fd7da1b23659929ac74387e3e1e4ca3a86d758036e2e46c0fa7d8b25b550d6cc1140a9ff558a923d8f8c04
7
+ data.tar.gz: 7c637ddb9731fd644075fcee07f62130037cf51bd70b64fce1cf53ceb45402a776ee10687b2579d9595ef07803d3eccf9a77c739f35ec50aefcf00ae78d0be81
data/README.md CHANGED
@@ -71,10 +71,16 @@ Apart from storage, the attribute behaves just like any other attribute on your
71
71
 
72
72
  ### Defining models
73
73
 
74
- You can also store references to other models using the `has_model` declaration. This stores a [GlobalID](https://github.com/rails/globalid) inside the data field, converting the reference back to a real model when needed. The actual GlobalID is mangled slightly before it is stored, to provide compatibility with the [GlobalIdSerialiser](https://github.com/standard-procedure/global_id_serialiser), which also uses GlobalIDs to store references to models. The difference with `has_model` is `has_model` reloads the model on-demand, whereas `GlobalIdSerialiser` loads all models from the data field when the record is loaded. GlobalIdSerialiser also allows you to store your models within arrays or nested inside hashes - but you need to be careful about the performance implications if lots of models are stored.
74
+ You can also store references to other models using the `has_model` declaration. This stores a [GlobalID](https://github.com/rails/globalid) inside the data field, converting the reference back to a real model when needed. `has_model` reloads the model on-demand so there is no database request until you actually try to access the stored model. However, because of the way this works, you cannot eager-load stored models, so be careful when trying to display large numbers of records, as you will end up with N+1 queries.
75
75
 
76
76
  When using `has_model` you can optionally specify a class name (as a string). If given, the model will be tested to make sure it is that class (or a subclass of it) and, if not the record is marked as invalid.
77
77
 
78
+ ### Defining arrays of models
79
+
80
+ Similarly, you can store arrays of models using the `has_models` declaration. Internally this stores a comma separated list of GlobalIDs inside the data field, using `GlobalID::Locator.locate_many` to convert the array back to models as efficiently as possible. Again, eager loading is not possible so watch out for N+1 queries.
81
+
82
+ Like `has_model` you can optionally specify a class name (as a string), which then validates the provided models to make sure they are valid classes or subclasses.
83
+
78
84
  ## Development
79
85
 
80
86
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1 @@
1
+ a81ee1c0a59142295d6bbfddefd39697884c3de3b911836c49d8f51ffa75b73c21ffe2efa1a7ac90235b640ce5aa07d378d09f4f4b76371a416885704d1a184a
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HasAttributes
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -3,7 +3,7 @@
3
3
  require_relative "has_attributes/version"
4
4
  require "active_support/concern"
5
5
  require "active_support/core_ext/string/inflections"
6
- require "global_id_serialiser"
6
+ require "global_id"
7
7
 
8
8
  module HasAttributes
9
9
  extend ActiveSupport::Concern
@@ -12,7 +12,7 @@ module HasAttributes
12
12
  class_methods do
13
13
  def has_attribute name, cast_type = :string, field_name: :data, **options
14
14
  field_name = field_name.to_sym
15
- name = name.to_sym
15
+ name = name.to_s
16
16
  typecaster = cast_type.nil? ? nil : ActiveRecord::Type.lookup(cast_type)
17
17
  typecast_value = ->(value) { typecaster.nil? ? value : typecaster.cast(value) }
18
18
  define_attribute_method name
@@ -38,7 +38,7 @@ module HasAttributes
38
38
  validate :"#{name}_class_name", if: -> { send(name).present? } if class_name.present?
39
39
 
40
40
  define_method(name.to_sym) do
41
- model_id = send id_attribute
41
+ model_id = send id_attribute.to_sym
42
42
  model_id.nil? ? nil : GlobalID::Locator.locate(model_id.sub("modelid-", ""))
43
43
  rescue ActiveRecord::RecordNotFound
44
44
  nil
@@ -53,5 +53,32 @@ module HasAttributes
53
53
  errors.add name, :invalid unless send(name).is_a?(class_name.constantize)
54
54
  end
55
55
  end
56
+
57
+ def has_models name, class_name = nil, field_name: :data, **options
58
+ name = name.to_sym
59
+ array_attribute = "#{name}_array"
60
+ has_attribute array_attribute, field_name:, **options
61
+ validate :"#{name}_class_names", if: -> { send(name).any? } if class_name.present?
62
+
63
+ define_method(name.to_sym) do
64
+ model_ids = send(array_attribute).to_s.split(",").map do |model_id|
65
+ model_id.to_s.sub("modelid-", "")
66
+ end
67
+ GlobalID::Locator.locate_many(model_ids)
68
+ rescue ActiveRecord::RecordNotFound
69
+ []
70
+ end
71
+
72
+ define_method(:"#{name}=") do |models|
73
+ model_ids = Array.wrap(models).map do |model|
74
+ model.nil? ? nil : "modelid-#{model.to_global_id}"
75
+ end.compact
76
+ send :"#{array_attribute}=", model_ids.join(",")
77
+ end
78
+
79
+ define_method :"#{name}_class_names" do
80
+ errors.add name, :invalid if send(name).any? { |model| !model.is_a?(class_name.constantize) }
81
+ end
82
+ end
56
83
  end
57
84
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_procedure_has_attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rahoul Baruah
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-13 00:00:00.000000000 Z
10
+ date: 2025-07-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: globalid
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.2'
26
40
  description: Store Rails attributes in a serialized JSON field, with support for GlobalIDs
27
41
  and indexes
28
42
  email:
@@ -38,6 +52,7 @@ files:
38
52
  - README.md
39
53
  - Rakefile
40
54
  - checksums/standard_procedure_has_attributes-0.1.0.gem.sha512
55
+ - checksums/standard_procedure_has_attributes-0.2.0.gem.sha512
41
56
  - lib/has_attributes.rb
42
57
  - lib/has_attributes/version.rb
43
58
  - sig/has_attributes.rbs