support_table_data 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 524d00657682e8716a8be2ee07d00579d6e9171718e3c48fdd63dff74cca956b
4
- data.tar.gz: 0db9f298a33524f6d31872047302d7e08a2c0d321bec3bfd8543be319dc4e5b8
3
+ metadata.gz: edd9a57acfcdadac9ce60c8520b4fa2d3cccda8bebc2bfe1eeea54981bcf5488
4
+ data.tar.gz: 31f38443c7edd8af91d3f4de125dbb4a32b8b38612c84ce321633971063fd29b
5
5
  SHA512:
6
- metadata.gz: 11a0d537e71ecfbfdec261ef4bb4d2d8b1625897ee33263c728c75456e6882de50da9b964e5a5d69213b37041b0c02c315a9ddb0a9bb92e2f9ac8bcd05efafa4
7
- data.tar.gz: 1bb84bbd5750d16f9c58736ed2fd70164dfbbc2d3f4cbb9c02b935e774049829081c5982d9322a719a1115fcaf10cbabb7e547765f9dc456ed05b72149da2ab9
6
+ metadata.gz: d425621345850d9bd2ec8f878eefcbeb667fb6981d2d74b3b2174e3943463bf95470727f3925f83ba966766fe6817ab8e11b5ba9ed30ff9a6c1bb0826229c261
7
+ data.tar.gz: 152ccc8196e8eb7fd68e5e94cc6dcea5307e98d3e1ef23a1ed9e5e320d0d511beac19494c0242eb1a858ceebc9eba27b29e68023c69476ea5046273e6f9b1445
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ 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.2.0
8
+
9
+ ### Added
10
+
11
+ - Added `named_instance` method to load a named instance from the database.
12
+ - Added class method `named_instance_data` to return attributes from the data files for a named instance.
13
+ - Added handling for `has_many through` associations to load the dependent through associations first.
14
+
15
+ ## 1.1.2
16
+
17
+ ### Fixed
18
+
19
+ - Ignore anonymous ActiveRecord classes when calling `sync_all!`.
20
+
7
21
  ## 1.1.1
8
22
 
9
23
  - Freeze values returned from helper methods.
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Continuous Integration](https://github.com/bdurand/support_table_data/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/support_table_data/actions/workflows/continuous_integration.yml)
4
4
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
5
+ [![Gem Version](https://badge.fury.io/rb/support_table_data.svg)](https://badge.fury.io/rb/support_table_data)
5
6
 
6
7
  This gem provides a mixin for ActiveRecord support table models that allows you to load data from YAML, JSON, or CSV files and reference specific records more easily. It is intended to solve issues with support tables (also known as lookup tables) that contain a small set of canonical data that must exist for your application to work.
7
8
 
@@ -119,6 +120,55 @@ Status.in_progress_id # => 2
119
120
  Status.completed_id # => 3
120
121
  ```
121
122
 
123
+ You can also use named instances to maintain associations between you models. In order to do this you'll need to implement a custom setter method.
124
+
125
+ ```ruby
126
+ class Group < ApplicationRecord
127
+ include SupportTableData
128
+
129
+ has_many :statuses
130
+ end
131
+
132
+ class Status < ApplicationRecord
133
+ include SupportTableData
134
+
135
+ belongs_to :group
136
+
137
+ def group_name=(instance_name)
138
+ self.group = Group.named_instance(instance_name)
139
+ end
140
+ end
141
+ ```
142
+
143
+ This then allows you to reference groups by instance name in the statuses.yml file:
144
+
145
+ ```yaml
146
+ # groups.yml
147
+ not_done:
148
+ id: 1
149
+ name: Not Done
150
+
151
+ done:
152
+ id: 2
153
+ name: Done
154
+
155
+ # statuses.yml
156
+ pending:
157
+ id: 1
158
+ name: Pending
159
+ group_name: not_done
160
+
161
+ in_progress:
162
+ id: 2
163
+ name: In Progress
164
+ group_name: not_done
165
+
166
+ completed:
167
+ id: 3
168
+ name: Completed
169
+ group_name: done
170
+ ```
171
+
122
172
  ### Caching
123
173
 
124
174
  You can use the companion [support_table_cache gem](https://github.com/bdurand/support_table_cache) to add caching support to your models. That way your application won't need to constantly query the database for records that will never change.
@@ -162,6 +212,8 @@ Loading data is done inside a database transaction. No changes will be persisted
162
212
 
163
213
  You can synchronize the data in all models by calling `SupportTableData.sync_all!`. This method will discover all ActiveRecord models that include `SupportTableData` and synchronize each of them. (Note that there can be issues discovering all support table models in a Rails application if eager loading is turned off.) The discovery mechanism will try to detect unloaded classes by looking at the file names in the support table data directory so it's best to stick to standard Rails naming conventions for your data files.
164
214
 
215
+ The load order for models will resolve any dependencies between models. So if one model has a `belongs_to` association with another model, then the belongs to model will be loaded first.
216
+
165
217
  You need to call `SupportTableData.sync_all!` when deploying your application. This gem includes a rake task `support_table_data:sync` that is suitable for hooking into deploy scripts. An easy way to hook it into a Rails application is by enhancing the `db:migrate` task so that the sync task runs immediately after database migrations are run. You can do this by adding code to a Rakefile in your application's `lib/tasks` directory:
166
218
 
167
219
  ```ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.2.0
@@ -8,7 +8,24 @@
8
8
  module SupportTableData
9
9
  extend ActiveSupport::Concern
10
10
 
11
- module ClassMethods
11
+ included do
12
+ # Internal variables used for memoization.
13
+ @support_table_data_files = []
14
+ @support_table_attribute_helpers = {}
15
+ @support_table_instance_names = {}
16
+ @support_table_instance_keys = nil
17
+
18
+ # Define the attribute used as the key of the hash in the data files.
19
+ # This should be a value that never changes. By default the key attribute will be the id.
20
+ class_attribute :support_table_key_attribute, instance_accessor: false
21
+
22
+ # Define the directory where data files should be loaded from. This value will override the global
23
+ # value set by SupportTableData.data_directory. This is only used if relative paths are passed
24
+ # in to add_support_table_data.
25
+ class_attribute :support_table_data_directory, instance_accessor: false
26
+ end
27
+
28
+ class_methods do
12
29
  # Synchronize the rows in the table with the values defined in the data files added with
13
30
  # `add_support_table_data`. Note that rows will not be deleted if they are no longer in
14
31
  # the data files.
@@ -28,7 +45,7 @@ module SupportTableData
28
45
  key = record[key_attribute].to_s
29
46
  attributes = canonical_data.delete(key)
30
47
  attributes&.each do |name, value|
31
- record.send("#{name}=", value) if record.respond_to?("#{name}=", true)
48
+ record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true)
32
49
  end
33
50
  if record.changed?
34
51
  changes << record.changes
@@ -39,7 +56,7 @@ module SupportTableData
39
56
  canonical_data.each_value do |attributes|
40
57
  record = new
41
58
  attributes.each do |name, value|
42
- record.send("#{name}=", value) if record.respond_to?("#{name}=", true)
59
+ record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true)
43
60
  end
44
61
  changes << record.changes
45
62
  record.save!
@@ -58,7 +75,6 @@ module SupportTableData
58
75
  # this model or the global directory set with SupportTableData.data_directory.
59
76
  # @return [void]
60
77
  def add_support_table_data(data_file_path)
61
- @support_table_data_files ||= []
62
78
  root_dir = (support_table_data_directory || SupportTableData.data_directory || Dir.pwd)
63
79
  @support_table_data_files << File.expand_path(data_file_path, root_dir)
64
80
  define_support_table_named_instances
@@ -72,7 +88,6 @@ module SupportTableData
72
88
  # @param attributes [String, Symbol] The names of the attributes to add helper methods for.
73
89
  # @return [void]
74
90
  def named_instance_attribute_helpers(*attributes)
75
- @support_table_attribute_helpers ||= {}
76
91
  attributes.flatten.collect(&:to_s).each do |attribute|
77
92
  @support_table_attribute_helpers[attribute] = []
78
93
  end
@@ -84,7 +99,6 @@ module SupportTableData
84
99
  #
85
100
  # @return [Array<String>] List of attribute names.
86
101
  def support_table_attribute_helpers
87
- @support_table_attribute_helpers ||= {}
88
102
  @support_table_attribute_helpers.keys
89
103
  end
90
104
 
@@ -92,7 +106,6 @@ module SupportTableData
92
106
  #
93
107
  # @return [Array<Hash>] List of attributes for all records in the data files.
94
108
  def support_table_data
95
- @support_table_data_files ||= []
96
109
  data = {}
97
110
  key_attribute = (support_table_key_attribute || primary_key).to_s
98
111
 
@@ -114,25 +127,57 @@ module SupportTableData
114
127
  data.values
115
128
  end
116
129
 
130
+ # Get the data for a named instances from the data files.
131
+ #
132
+ # @return [Hasn] Hash of named instance attributes.
133
+ def named_instance_data(name)
134
+ data = {}
135
+ name = name.to_s
136
+
137
+ @support_table_data_files.each do |data_file_path|
138
+ file_data = support_table_parse_data_file(data_file_path)
139
+ next unless file_data.is_a?(Hash)
140
+
141
+ file_data.each do |instance_name, attributes|
142
+ next unless name == instance_name.to_s
143
+ next unless attributes.is_a?(Hash)
144
+
145
+ data.merge!(attributes)
146
+ end
147
+ end
148
+
149
+ data
150
+ end
151
+
117
152
  # Get the names of all named instances.
118
153
  #
119
154
  # @return [Array<String>] List of all instance names.
120
155
  def instance_names
121
- @support_table_instance_names ||= Set.new
122
- @support_table_instance_names.to_a
156
+ @support_table_instance_names.keys
157
+ end
158
+
159
+ # Load a named instance from the database.
160
+ #
161
+ # @param instance_name [String, Symbol] The name of the instance to load as defined in the data files.
162
+ # @return [ActiveRecord::Base] The instance loaded from the database.
163
+ # @raise [ActiveRecord::RecordNotFound] If the instance does not exist.
164
+ def named_instance(instance_name)
165
+ key_attribute = (support_table_key_attribute || primary_key).to_s
166
+ instance_name = instance_name.to_s
167
+ find_by!(key_attribute => @support_table_instance_names[instance_name])
123
168
  end
124
169
 
125
170
  # Get the key values for all instances loaded from the data files.
126
171
  #
127
172
  # @return [Array] List of all the key attribute values.
128
173
  def instance_keys
129
- unless defined?(@support_table_instance_keys)
174
+ if @support_table_instance_keys.nil?
130
175
  key_attribute = (support_table_key_attribute || primary_key).to_s
131
176
  values = []
132
177
  support_table_data.each do |attributes|
133
178
  key_value = attributes[key_attribute]
134
179
  instance = new
135
- instance.send("#{key_attribute}=", key_value)
180
+ instance.send(:"#{key_attribute}=", key_value)
136
181
  values << instance.send(key_attribute)
137
182
  end
138
183
  @support_table_instance_keys = values.uniq
@@ -157,9 +202,6 @@ module SupportTableData
157
202
  private
158
203
 
159
204
  def define_support_table_named_instances
160
- @support_table_data_files ||= []
161
- @support_table_instance_names ||= Set.new
162
-
163
205
  @support_table_data_files.each do |file_path|
164
206
  data = support_table_parse_data_file(file_path)
165
207
  next unless data.is_a?(Hash)
@@ -188,7 +230,7 @@ module SupportTableData
188
230
  unless @support_table_instance_names.include?(method_name)
189
231
  define_support_table_instance_helper(method_name, key_attribute, key_value)
190
232
  define_support_table_predicates_helper("#{method_name}?", key_attribute, key_value)
191
- @support_table_instance_names << method_name
233
+ @support_table_instance_names[method_name] = key_value
192
234
  end
193
235
 
194
236
  if defined?(@support_table_attribute_helpers)
@@ -203,8 +245,6 @@ module SupportTableData
203
245
  end
204
246
 
205
247
  def define_support_table_instance_helper(method_name, attribute_name, attribute_value)
206
- return if @support_table_instance_names.include?("self.#{method_name}")
207
-
208
248
  if respond_to?(method_name, true)
209
249
  raise ArgumentError.new("Could not define support table helper method #{name}.#{method_name} because it is already a defined method")
210
250
  end
@@ -217,8 +257,6 @@ module SupportTableData
217
257
  end
218
258
 
219
259
  def define_support_table_instance_attribute_helper(method_name, attribute_value)
220
- return if @support_table_instance_names.include?("self.#{method_name}")
221
-
222
260
  if respond_to?(method_name, true)
223
261
  raise ArgumentError.new("Could not define support table helper method #{name}.#{method_name} because it is already a defined method")
224
262
  end
@@ -231,8 +269,6 @@ module SupportTableData
231
269
  end
232
270
 
233
271
  def define_support_table_predicates_helper(method_name, attribute_name, attribute_value)
234
- return if @support_table_instance_names.include?(method_name)
235
-
236
272
  if method_defined?(method_name) || private_method_defined?(method_name)
237
273
  raise ArgumentError.new("Could not define support table helper method #{name}##{method_name} because it is already a defined method")
238
274
  end
@@ -268,17 +304,6 @@ module SupportTableData
268
304
  end
269
305
  end
270
306
 
271
- included do
272
- # Define the attribute used as the key of the hash in the data files.
273
- # This should be a value that never changes. By default the key attribute will be the id.
274
- class_attribute :support_table_key_attribute, instance_accessor: false
275
-
276
- # Define the directory where data files should be loaded from. This value will override the global
277
- # value set by SupportTableData.data_directory. This is only used if relative paths are passed
278
- # in to add_support_table_data.
279
- class_attribute :support_table_data_directory, instance_accessor: false
280
- end
281
-
282
307
  class << self
283
308
  # Specify the default directory for data files.
284
309
  attr_writer :data_directory
@@ -345,7 +370,8 @@ module SupportTableData
345
370
  end
346
371
  end
347
372
 
348
- ActiveRecord::Base.descendants.sort_by(&:name).each do |klass|
373
+ active_record_classes = ActiveRecord::Base.descendants.reject { |klass| klass.name.nil? }
374
+ active_record_classes.sort_by(&:name).each do |klass|
349
375
  next unless klass.include?(SupportTableData)
350
376
  next if klass.abstract_class?
351
377
  next if classes.include?(klass)
@@ -372,11 +398,16 @@ module SupportTableData
372
398
  # @return [Array<Class>]
373
399
  def support_table_dependencies(klass)
374
400
  dependencies = []
375
- klass.reflections.values.select(&:belongs_to?).each do |reflection|
376
- if reflection.klass.include?(SupportTableData) && !(reflection.klass <= klass)
377
- dependencies << reflection.klass
378
- end
401
+
402
+ klass.reflections.values.each do |reflection|
403
+ next if reflection.polymorphic?
404
+ next unless reflection.klass.include?(SupportTableData)
405
+ next if reflection.klass <= klass
406
+ next unless reflection.belongs_to? || reflection.through_reflection?
407
+
408
+ dependencies << reflection.klass
379
409
  end
410
+
380
411
  dependencies
381
412
  end
382
413
  end
@@ -27,6 +27,8 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.require_paths = ["lib"]
29
29
 
30
+ spec.required_ruby_version = ">= 2.5"
31
+
30
32
  spec.add_dependency "activerecord"
31
33
 
32
34
  spec.add_development_dependency "bundler"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: support_table_data
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.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-05-01 00:00:00.000000000 Z
11
+ date: 2024-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -46,7 +46,7 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - CHANGELOG.md
49
- - MIT-LICENSE
49
+ - MIT-LICENSE.txt
50
50
  - README.md
51
51
  - VERSION
52
52
  - lib/support_table_data.rb
@@ -65,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '2.5'
69
69
  required_rubygems_version: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - ">="
File without changes