activerecord-deepstore 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8a8e192500276489b18a179c56d5e0878a1ca8b4600b85cd2c4f587403f2dc01
4
+ data.tar.gz: d40765f8d041804eb22d02036f7cb458e8540d7a75a7bfc82c5176af189ccfe2
5
+ SHA512:
6
+ metadata.gz: 31153e6a0a5e32e49cd5cc8667cb79681b21d8c296d1521a7cdbf8b264b681552953ca5ee4c9061435cc880044fbf86f530f99ef6d795ef3a7ea7c1db1b3188b
7
+ data.tar.gz: a8e5052ee7ff97e8ac527c693019cad85a32430248984adb841e2be1ac49ae2279c060732fd0858620bc4c3ba3748b4c6622079bef7ba860eeccab8168d88f40
data/.rubocop.yml ADDED
@@ -0,0 +1,26 @@
1
+ AllCops:
2
+ SuggestExtensions: false
3
+ TargetRubyVersion: 2.6
4
+
5
+ Metrics/LineLength:
6
+ Enabled: false
7
+
8
+ Metrics/BlockLength:
9
+ Enabled: false
10
+
11
+ Metrics/ClassLength:
12
+ Enabled: false
13
+
14
+ Metrics/ModuleLength:
15
+ Enabled: false
16
+
17
+ Style/StringLiterals:
18
+ Enabled: true
19
+ EnforcedStyle: double_quotes
20
+
21
+ Style/StringLiteralsInInterpolation:
22
+ Enabled: true
23
+ EnforcedStyle: double_quotes
24
+
25
+ Layout/LineLength:
26
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-03-30
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Emmanuel Cousin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # ActiveRecord-Deepstore
2
+
3
+ ActiveRecord-Deepstore is a Ruby gem that extends ActiveRecord models with additional functionality for handling deeply nested data structures within a database column. It simplifies storing, accessing, and managing complex nested data in your Rails applications.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'activerecord-deepstore'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install activerecord-deepstore
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ To use ActiveRecord-Deepstore in your Rails application, include it in your ActiveRecord models:
28
+
29
+ ```ruby
30
+ class MyModel < ActiveRecord::Base
31
+ extend ActiveRecord::Deepstore
32
+ end
33
+ ```
34
+
35
+ Once included, your models gain access to methods for storing and accessing deeply nested data within database columns.
36
+
37
+ ### Example
38
+
39
+ ```ruby
40
+ class User < ActiveRecord::Base
41
+ extend ActiveRecord::Deepstore
42
+
43
+ deep_store :settings, {
44
+ notifications: {
45
+ posts: { push: true, email: true },
46
+ comments: { push: true, email: false }
47
+ }
48
+ }
49
+ end
50
+ ```
51
+
52
+ This implementation provides with an accessor for every level of the provider hash:
53
+
54
+ ```ruby
55
+ user.notifications_settings # => { posts: { push: true, email: true }, comments: { push: true, email: false } }
56
+ user.posts_notifications_settings # => { push: true, email: true }
57
+ user.push_posts_notifications_settings # => true
58
+ user.email_posts_notifications_settings # => true
59
+ user.comments_notifications_settings # => { push: true, email: false }
60
+ user.push_comments_notifications_settings # => true
61
+ user.email_comments_notifications_settings # => false
62
+ ```
63
+
64
+ You can list all the generated accessors by calling `deep_stored_accessors` on the model `User`:
65
+
66
+ ```ruby
67
+ User.deep_stored_accessors
68
+ # => ["notifications_settings", "posts_notifications_settings", "push_posts_notifications_settings", [...], "email_comments_notifications_settings"]
69
+ ```
70
+
71
+ #### Automatic typecasting
72
+
73
+ Writer methods automatically cast the value to the type the default values belong to. For example:
74
+
75
+ ```ruby
76
+ user.push_comments_notifications_settings = "1" # => "1"
77
+ user.push_comments_notifications_settings # => true
78
+ user.push_comments_notifications_settings = "0" # => "0"
79
+ user.push_comments_notifications_settings # => false
80
+ ```
81
+
82
+ #### Tracking value changes
83
+
84
+ Dirty attributes are implemented for every accessor. For example:
85
+
86
+ ```ruby
87
+ user.push_comments_notifications_settings # => false
88
+ user.push_comments_notifications_settings = true # => true
89
+ user.push_comments_notifications_settings_was # => false
90
+ user.push_comments_notifications_settings_changes # => { false => true }
91
+ user.push_comments_notifications_settings_changed? # => true
92
+ ```
93
+
94
+ #### Accessing default values
95
+
96
+ You can access the default value of every accessor at anytime by calling the associated `default_#{accessor_name}` method:
97
+
98
+ ```ruby
99
+ user.push_comments_notifications_settings # => false
100
+ user.update! push_comments_notifications_settings: true
101
+ user.push_comments_notifications_settings # => true
102
+ user.default_push_comments_notifications_settings #=> false
103
+ ```
104
+
105
+ #### Resetting to default values
106
+
107
+ You can reset every accessor to its default value at anytime by calling the associated `reset_#{accessor_name}` method:
108
+
109
+ ```ruby
110
+ # When the changes are not persisted
111
+ user.push_comments_notifications_settings # => false
112
+ user.push_comments_notifications_settings = true
113
+ user.reset_push_comments_notifications_settings
114
+ user.push_comments_notifications_settings # => false
115
+
116
+ # When the changes are persisted
117
+ user.update! push_comments_notifications_settings: true
118
+ user.push_comments_notifications_settings # => true
119
+ user.reset_push_comments_notifications_settings!
120
+ user.reload.push_comments_notifications_settings # => false
121
+ ```
122
+
123
+ ## Contributing
124
+
125
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/EmCousin/activerecord-deepstore](https://github.com/EmCousin/activerecord-deepstore).
126
+
127
+ ## License
128
+
129
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/active_record/deepstore/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "activerecord-deepstore"
7
+ spec.version = ActiveRecord::Deepstore::VERSION
8
+ spec.authors = ["Emmanuel Cousin"]
9
+ spec.email = ["emmanuel@hey.com"]
10
+
11
+ spec.summary = "ActiveRecord-Deepstore adds powerful functionality to ActiveRecord models for handling deeply nested data structures within database columns. Simplify storing, accessing, and managing complex nested data in your Rails applications with ease."
12
+ spec.description = "ActiveRecord-Deepstore enhances ActiveRecord models with powerful functionality for handling deeply nested data structures within a database column. It provides methods for storing, accessing, and managing deeply nested data, making it easier to work with complex data structures in your Rails applications. With ActiveRecord-Deepstore, you can seamlessly store nested hashes in database columns, access nested data with simple method calls, track changes to nested attributes, and much more. This gem simplifies the handling of complex data structures, improving the maintainability and readability of your Rails codebase."
13
+ spec.homepage = "https://github.com/EmCousin/activerecord-deepstore"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = spec.homepage
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) ||
27
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "activerecord", "~> 7.0"
35
+ spec.add_dependency "activesupport", "~> 7.0"
36
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+
5
+ mysql:
6
+ adapter: mysql2
7
+ database: active_record_deepstore_test
8
+ username: root
9
+ password:
10
+
11
+ postgresql:
12
+ adapter: postgresql
13
+ database: active_record_deepstore_test
14
+ username: postgres
15
+ password:
16
+ host: localhost
17
+ port: 5432
Binary file
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Deepstore
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "deepstore/version"
4
+
5
+ module ActiveRecord
6
+ # The ActiveRecord::Deepstore module extends ActiveRecord models with additional
7
+ # functionality for handling deeply nested data structures within a database column.
8
+ module Deepstore
9
+ # Raised when an error occurs in the ActiveRecord::Deepstore module.
10
+ class Error < StandardError; end
11
+
12
+ attr_reader :deep_stored_accessors
13
+
14
+ # Retrieves or initializes the array containing names of attributes declared as deep stores.
15
+ #
16
+ # @return [Array<String>] The array containing names of deep stores.
17
+ def deep_stores
18
+ @deep_stores ||= []
19
+ end
20
+
21
+ # Recursively traverses a nested hash and returns a flattened representation of leaf nodes along with their paths.
22
+ #
23
+ # @param hash [Hash] The nested hash to traverse.
24
+ # @param path [Array] The current path in the hash.
25
+ # @param current_depth [Integer] The current depth in the hash traversal.
26
+ # @param max_depth [Integer, nil] The maximum depth to traverse. If nil, traverses the entire hash.
27
+ # @return [Hash] The flattened representation of leaf nodes along with their paths.
28
+ def leaves(hash, path: [], current_depth: 0, max_depth: nil)
29
+ hash.each_with_object({}) do |(key, value), result|
30
+ current_path = path + [key]
31
+
32
+ if value.is_a?(Hash) && (max_depth.nil? || current_depth < max_depth)
33
+ result.merge!(leaves(value, path: current_path, current_depth: current_depth + 1, max_depth: max_depth))
34
+ else
35
+ result[current_path] = value
36
+ end
37
+ end
38
+ end
39
+
40
+ # rubocop:disable Metrics/AbcSize
41
+ # rubocop:disable Metrics/CyclomaticComplexity
42
+ # rubocop:disable Metrics/MethodLength
43
+ # rubocop:disable Metrics/PerceivedComplexity
44
+
45
+ # Defines behavior for storing deeply nested data in a database column.
46
+ #
47
+ # @param accessor_name [Symbol, String] The name of the accessor for the deep store.
48
+ # @param payload [Hash] The hash representing the deeply nested data.
49
+ # @param suffix [Boolean] Whether to include a suffix in the accessor name.
50
+ # @param column_required [Boolean] Whether the corresponding column is required in the database table.
51
+ # @raise [ActiveRecord::Deepstore::Error] If the deep store is already declared.
52
+ # @raise [NotImplementedError] If the required column is not found in the database table.
53
+ # @return [void]
54
+ def deep_store(accessor_name, payload, suffix: true, column_required: true)
55
+ accessor_name = accessor_name.to_s.parameterize.underscore
56
+
57
+ raise Error, "Deep store '#{accessor_name}' is already declared" if deep_stores.include?(accessor_name)
58
+
59
+ @deep_stores << accessor_name
60
+
61
+ if column_required && (columns.find do |c|
62
+ c.name == accessor_name.to_s
63
+ end).blank?
64
+ raise NotImplementedError,
65
+ "Column #{accessor_name} not found for table #{table_name}"
66
+ end
67
+
68
+ serialize accessor_name, type: Hash, default: payload, yaml: { unsafe_load: true } if payload.is_a?(Hash)
69
+
70
+ define_method(:"default_#{accessor_name}") { payload.try(:with_indifferent_access) || payload }
71
+
72
+ define_method(:"reset_#{accessor_name}") { assign_attributes(accessor_name => send(:"default_#{accessor_name}")) }
73
+
74
+ define_method(:"reset_#{accessor_name}!") { update(accessor_name => send(:"default_#{accessor_name}")) }
75
+
76
+ define_method(:"#{accessor_name}_changes") do
77
+ old_value = send(:"#{accessor_name}_was")
78
+ current_value = send(accessor_name)
79
+ old_value == current_value ? {} : { old_value => current_value }
80
+ end
81
+
82
+ define_method(:"#{accessor_name}=") do |value|
83
+ old_value = send(:"#{accessor_name}_was")
84
+
85
+ if value.is_a?(Hash)
86
+ value = {}.with_indifferent_access if value.blank?
87
+ self.class.leaves(value).each do |leaf_path, leaf_value|
88
+ default_value = leaf_path.inject(payload.with_indifferent_access) do |h, key|
89
+ h.is_a?(Hash) ? h.fetch(key, h) : h
90
+ end
91
+ cast_type = self.class.cast_type_from_name(self.class.cast_type_name_from_value(default_value))
92
+
93
+ # Traverse the hash using the leaf path and update the leaf value.
94
+ leaf_key = leaf_path.pop
95
+ parent_hash = leaf_path.inject(value, :[])
96
+ # old_leaf_value = parent_hash[leaf_key]
97
+ new_leaf_value = cast_type.cast(leaf_value)
98
+ old_parent_hash = parent_hash.dup
99
+ parent_hash[leaf_key] = new_leaf_value
100
+
101
+ instance_variable_set(:"@#{leaf_path.join("_")}_#{accessor_name}_was",
102
+ old_parent_hash.with_indifferent_access)
103
+ end
104
+
105
+ formatted_value = payload.with_indifferent_access.deep_merge(value)
106
+ else
107
+ default_value = send(:"default_#{accessor_name}")
108
+ cast_type = self.class.cast_type_from_name(self.class.cast_type_name_from_value(default_value))
109
+ formatted_value = cast_type.cast(value)
110
+ end
111
+
112
+ instance_variable_set(:"@#{accessor_name}_was", old_value)
113
+
114
+ super(formatted_value)
115
+ end
116
+ # rubocop:enable Metrics/AbcSize
117
+ # rubocop:enable Metrics/CyclomaticComplexity
118
+ # rubocop:enable Metrics/MethodLength
119
+ # rubocop:enable Metrics/PerceivedComplexity
120
+
121
+ return unless payload.is_a?(Hash)
122
+
123
+ payload.each do |key, value|
124
+ deep_store_accessor(accessor_name, payload, key, value, suffix)
125
+ end
126
+ end
127
+
128
+ # Reloads the model instance and clears deep store changes information.
129
+ #
130
+ # @param args [Array] Arguments to pass to the reload method.
131
+ # @return [void]
132
+ define_method(:reload) do |*args|
133
+ clear_deep_store_changes_information
134
+ super(*args)
135
+ end
136
+
137
+ # Clears deep store changes information.
138
+ #
139
+ # @return [void]
140
+ define_method(:clear_deep_store_changes_information) do
141
+ self.class.deep_stored_accessors.each do |accessor|
142
+ formatted_accessor = accessor.to_s.parameterize.underscore
143
+ instance_variable_set(:"@#{formatted_accessor}_was", send(formatted_accessor))
144
+ end
145
+ end
146
+
147
+ # Defines accessor methods for nested keys within the deep store hash.
148
+ #
149
+ # @param accessor_name [Symbol, String] The name of the deep store accessor.
150
+ # @param payload [Hash] The hash representing the deeply nested data.
151
+ # @param key [Symbol, String] The key within the hash.
152
+ # @param value [Object] The value associated with the key.
153
+ # @param suffix [Boolean] Whether to include a suffix in the accessor name.
154
+ # @return [void]
155
+ def deep_store_accessor(accessor_name, payload, key, value, suffix)
156
+ store_json_accessor(accessor_name, payload, key, suffix)
157
+
158
+ deep_store(deep_accessor_name(accessor_name, key), value, column_required: false)
159
+
160
+ return if value.is_a?(Hash)
161
+
162
+ define_method(deep_accessor_name(accessor_name, key)) do
163
+ return value unless (hash = public_send(accessor_name)).is_a?(Hash) && hash.key?(key)
164
+
165
+ hash[key]
166
+ end
167
+ end
168
+
169
+ # rubocop:disable Metrics/MethodLength
170
+
171
+ # Defines accessor methods for individual keys within the nested hash.
172
+ #
173
+ # @param accessor_name [Symbol, String] The name of the deep store accessor.
174
+ # @param hash [Hash] The hash representing the deeply nested data.
175
+ # @param key [Symbol, String] The key within the hash.
176
+ # @param suffix [Boolean] Whether to include a suffix in the accessor name.
177
+ # @return [void]
178
+ def store_json_accessor(accessor_name, hash, key, suffix)
179
+ store_accessor(accessor_name.to_sym, key, suffix: suffix)
180
+ base_method_name = deep_accessor_name(accessor_name, key)
181
+ @deep_stored_accessors ||= []
182
+ @deep_stored_accessors << base_method_name
183
+ attribute base_method_name, cast_type_name_from_value(hash[key])
184
+
185
+ define_method(:"#{base_method_name}_was") do
186
+ method_name = :"#{base_method_name}_was"
187
+ return instance_variable_get("@#{method_name}") if instance_variable_defined?("@#{method_name}")
188
+
189
+ instance_variable_set("@#{method_name}", send(base_method_name))
190
+ end
191
+
192
+ define_method(:"#{base_method_name}_changed?") do
193
+ send(:"#{base_method_name}_changes").any?
194
+ end
195
+ end
196
+ # rubocop:enable Metrics/MethodLength
197
+
198
+ # Generates a unique name for accessor methods based on the accessor name and key.
199
+ #
200
+ # @param accessor_name [Symbol, String] The name of the deep store accessor.
201
+ # @param key [Symbol, String] The key within the hash.
202
+ # @return [String] The generated accessor name.
203
+ def deep_accessor_name(accessor_name, key)
204
+ "#{key.to_s.parameterize.underscore}_#{accessor_name.to_s.parameterize.underscore}"
205
+ end
206
+
207
+ # Determines the data type for serialization based on the value type.
208
+ #
209
+ # @param name [Symbol, String] The name of the data type.
210
+ # @return [ActiveRecord::Type::Value] The corresponding data type.
211
+ def cast_type_from_name(name)
212
+ ActiveRecord::Type.lookup name.to_sym, adapter: ActiveRecord::Type.adapter_name_from(self)
213
+ end
214
+
215
+ # Determines the data type name based on the value.
216
+ #
217
+ # @param value [Object] The value for which to determine the data type name.
218
+ # @return [Symbol] The name of the data type.
219
+ def cast_type_name_from_value(value)
220
+ type_mappings = {
221
+ TrueClass => :boolean,
222
+ FalseClass => :boolean,
223
+ NilClass => :string,
224
+ Hash => :text
225
+ }
226
+
227
+ type_mappings.fetch(value.class, value.class.name.underscore.to_sym)
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,6 @@
1
+ module Activerecord
2
+ module Deepstore
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-deepstore
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Emmanuel Cousin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '7.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '7.0'
41
+ description: ActiveRecord-Deepstore enhances ActiveRecord models with powerful functionality
42
+ for handling deeply nested data structures within a database column. It provides
43
+ methods for storing, accessing, and managing deeply nested data, making it easier
44
+ to work with complex data structures in your Rails applications. With ActiveRecord-Deepstore,
45
+ you can seamlessly store nested hashes in database columns, access nested data with
46
+ simple method calls, track changes to nested attributes, and much more. This gem
47
+ simplifies the handling of complex data structures, improving the maintainability
48
+ and readability of your Rails codebase.
49
+ email:
50
+ - emmanuel@hey.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - ".rubocop.yml"
56
+ - CHANGELOG.md
57
+ - LICENSE.txt
58
+ - README.md
59
+ - Rakefile
60
+ - activerecord-deepstore.gemspec
61
+ - activerecord-deepstore.sqlite3
62
+ - config/database.yml
63
+ - db/activerecord-deepstore.sqlite3
64
+ - lib/active_record/deepstore.rb
65
+ - lib/active_record/deepstore/version.rb
66
+ - sig/activerecord/deepstore.rbs
67
+ homepage: https://github.com/EmCousin/activerecord-deepstore
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ allowed_push_host: https://rubygems.org
72
+ homepage_uri: https://github.com/EmCousin/activerecord-deepstore
73
+ source_code_uri: https://github.com/EmCousin/activerecord-deepstore
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.6.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.4.21
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: ActiveRecord-Deepstore adds powerful functionality to ActiveRecord models
93
+ for handling deeply nested data structures within database columns. Simplify storing,
94
+ accessing, and managing complex nested data in your Rails applications with ease.
95
+ test_files: []