config_for-data_model 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 075ddb158bd8daf76d3cc92265090e9a2e712a6fd01e6efda3fceb0c18685c3c
4
+ data.tar.gz: d2338abaf2cbdc7820c67ead8ed3aa99b79afdcde31c1e494f4e827cd805514e
5
+ SHA512:
6
+ metadata.gz: 8365c7b0027e74ecaf29a981dc2385bcc1632ff7b34d5e7ccc11cad51a20cf41a0a5f0060e5f43f69d3bf1a54f9dcd910d7f69d02a9cb2cf5a276e6cdf7144f1
7
+ data.tar.gz: c2bfc008112025f33c35a4f91866994e5c1fef4b5e7f30afac9f0ba1e6971eacc6b2b7f6806581014e6364802e8870dbde735d6c422a0ff1ed18282495bd8136
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Andy Cohen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,481 @@
1
+ # ConfigFor::DataModel
2
+
3
+ A minimal, `ActiveRecord::QueryMethods`-like interface for data models backed by Rails configuration files.
4
+
5
+ ## Motivation
6
+
7
+ Not all of your application's data must reside in the database. For example, States & Countries don't change very often and don't need to be editable by users.
8
+
9
+ Rails provides a mechanism for loading such data from YAML files in your application's config directory:
10
+
11
+ ```ruby
12
+ Rails.application.config_for(:filename)
13
+ ```
14
+
15
+ This is useful on it's own, but I've found myself abstracting ontop of this Rails feature often enough that I decided to extract it for reuse.
16
+
17
+ ## Usage
18
+
19
+ Let's say we have a YAML file with Country & State Data like the following.
20
+
21
+ <details>
22
+ <summary>e.g. /config/countries.yml</summary>
23
+
24
+ ```yaml
25
+ shared:
26
+ -
27
+ id: AU
28
+ name: Australia
29
+ states:
30
+ -
31
+ id: ACT
32
+ name: Australian Capital Territory
33
+ country_id: AU
34
+ -
35
+ id: NSW
36
+ name: New South Wales
37
+ country_id: AU
38
+ -
39
+ id: NT
40
+ name: Northern Territory
41
+ country_id: AU
42
+ -
43
+ id: QLD
44
+ name: Queensland
45
+ country_id: AU
46
+ -
47
+ id: SA
48
+ name: South Australia
49
+ country_id: AU
50
+ -
51
+ id: TAS
52
+ name: Tasmania
53
+ country_id: AU
54
+ -
55
+ id: VIC
56
+ name: Victoria
57
+ country_id: AU
58
+ -
59
+ id: WA
60
+ name: Western Australia
61
+ country_id: AU
62
+ -
63
+ id: CA
64
+ name: Canada
65
+ states:
66
+ -
67
+ id: AB
68
+ name: Alberta
69
+ country_id: CA
70
+ -
71
+ id: BC
72
+ name: British Columbia
73
+ country_id: CA
74
+ -
75
+ id: MB
76
+ name: Manitoba
77
+ country_id: CA
78
+ -
79
+ id: NB
80
+ name: New Brunswick
81
+ country_id: CA
82
+ -
83
+ id: NL
84
+ name: Newfoundland
85
+ country_id: CA
86
+ -
87
+ id: NS
88
+ name: Nova Scotia
89
+ country_id: CA
90
+ -
91
+ id: NT
92
+ name: Northwest Territories
93
+ country_id: CA
94
+ -
95
+ id: NU
96
+ name: Nunavut
97
+ country_id: CA
98
+ -
99
+ id: 'ON' # in YAML, unquoted 'ON' is treated as boolean true
100
+ name: Ontario
101
+ country_id: CA
102
+ -
103
+ id: PE
104
+ name: Prince Edward Island
105
+ country_id: CA
106
+ -
107
+ id: QC
108
+ name: Quebec
109
+ country_id: CA
110
+ -
111
+ id: SK
112
+ name: Saskatchewan
113
+ country_id: CA
114
+ -
115
+ id: YT
116
+ name: Yukon
117
+ country_id: CA
118
+ -
119
+ id: US
120
+ name: United States
121
+ states:
122
+ -
123
+ id: AL
124
+ name: Alabama
125
+ country_id: US
126
+ -
127
+ id: AK
128
+ name: Alaska
129
+ country_id: US
130
+ -
131
+ id: AZ
132
+ name: Arizona
133
+ country_id: US
134
+ -
135
+ id: AR
136
+ name: Arkansas
137
+ country_id: US
138
+ -
139
+ id: CA
140
+ name: California
141
+ country_id: US
142
+ -
143
+ id: CO
144
+ name: Colorado
145
+ country_id: US
146
+ -
147
+ id: CT
148
+ name: Connecticut
149
+ country_id: US
150
+ -
151
+ id: DE
152
+ name: Delaware
153
+ country_id: US
154
+ -
155
+ id: DC
156
+ name: District of Columbia
157
+ country_id: US
158
+ -
159
+ id: FL
160
+ name: Florida
161
+ country_id: US
162
+ -
163
+ id: GA
164
+ name: Georgia
165
+ country_id: US
166
+ -
167
+ id: HI
168
+ name: Hawaii
169
+ country_id: US
170
+ -
171
+ id: ID
172
+ name: Idaho
173
+ country_id: US
174
+ -
175
+ id: IL
176
+ name: Illinois
177
+ country_id: US
178
+ -
179
+ id: IN
180
+ name: Indiana
181
+ country_id: US
182
+ -
183
+ id: IA
184
+ name: Iowa
185
+ country_id: US
186
+ -
187
+ id: KS
188
+ name: Kansas
189
+ country_id: US
190
+ -
191
+ id: KY
192
+ name: Kentucky
193
+ country_id: US
194
+ -
195
+ id: LA
196
+ name: Louisiana
197
+ country_id: US
198
+ -
199
+ id: ME
200
+ name: Maine
201
+ country_id: US
202
+ -
203
+ id: MD
204
+ name: Maryland
205
+ country_id: US
206
+ -
207
+ id: MA
208
+ name: Massachusetts
209
+ country_id: US
210
+ -
211
+ id: MI
212
+ name: Michigan
213
+ country_id: US
214
+ -
215
+ id: MN
216
+ name: Minnesota
217
+ country_id: US
218
+ -
219
+ id: MS
220
+ name: Mississippi
221
+ country_id: US
222
+ -
223
+ id: MO
224
+ name: Missouri
225
+ country_id: US
226
+ -
227
+ id: MT
228
+ name: Montana
229
+ country_id: US
230
+ -
231
+ id: NE
232
+ name: Nebraska
233
+ country_id: US
234
+ -
235
+ id: NV
236
+ name: Nevada
237
+ country_id: US
238
+ -
239
+ id: NH
240
+ name: New Hampshire
241
+ country_id: US
242
+ -
243
+ id: NJ
244
+ name: New Jersey
245
+ country_id: US
246
+ -
247
+ id: NM
248
+ name: New Mexico
249
+ country_id: US
250
+ -
251
+ id: NY
252
+ name: New York
253
+ country_id: US
254
+ -
255
+ id: NC
256
+ name: North Carolina
257
+ country_id: US
258
+ -
259
+ id: ND
260
+ name: North Dakota
261
+ country_id: US
262
+ -
263
+ id: OH
264
+ name: Ohio
265
+ country_id: US
266
+ -
267
+ id: OK
268
+ name: Oklahoma
269
+ country_id: US
270
+ -
271
+ id: OR
272
+ name: Oregon
273
+ country_id: US
274
+ -
275
+ id: PA
276
+ name: Pennsylvania
277
+ country_id: US
278
+ -
279
+ id: PR
280
+ name: Puerto Rico
281
+ country_id: US
282
+ -
283
+ id: RI
284
+ name: Rhode Island
285
+ country_id: US
286
+ -
287
+ id: SC
288
+ name: South Carolina
289
+ country_id: US
290
+ -
291
+ id: SD
292
+ name: South Dakota
293
+ country_id: US
294
+ -
295
+ id: TN
296
+ name: Tennessee
297
+ country_id: US
298
+ -
299
+ id: TX
300
+ name: Texas
301
+ country_id: US
302
+ -
303
+ id: UT
304
+ name: Utah
305
+ country_id: US
306
+ -
307
+ id: VT
308
+ name: Vermont
309
+ country_id: US
310
+ -
311
+ id: VA
312
+ name: Virginia
313
+ country_id: US
314
+ -
315
+ id: WA
316
+ name: Washington
317
+ country_id: US
318
+ -
319
+ id: WV
320
+ name: West Virginia
321
+ country_id: US
322
+ -
323
+ id: WI
324
+ name: Wisconsin
325
+ country_id: US
326
+ -
327
+ id: WY
328
+ name: Wyoming
329
+ country_id: US
330
+ ```
331
+
332
+ </details>
333
+
334
+ We could then make use of this in our app with a simple data model like this:
335
+
336
+ e.g. app/models/data/country.rb
337
+
338
+ ```ruby
339
+ Data::Country = Data.define(:id, :name, :states) do
340
+ include ConfigFor::DataModel
341
+ config :countries
342
+ end
343
+ ```
344
+
345
+ We could then access our data like:
346
+
347
+ ```ruby
348
+ Data::Country.find('AU')
349
+ # =>
350
+ # #<data Data::Country
351
+ # id="AU",
352
+ # name="Australia",
353
+ # states=
354
+ # [{id: "ACT", name: "Australian Capital Territory", country_id: "AU"},
355
+ # {id: "NSW", name: "New South Wales", country_id: "AU"}, ...
356
+ ```
357
+
358
+ Or
359
+
360
+ ```ruby
361
+ Data::Country.find_by(name: 'United States')
362
+ # =>
363
+ # #<data Data::Country
364
+ # id="US",
365
+ # name="United States",
366
+ # states=
367
+ # [{id: "AL", name: "Alabama", country_id: "US"},
368
+ # {id: "AK", name: "Alaska", country_id: "US"}, ...
369
+ ```
370
+
371
+ If we want more from the attribute members of our DataModel, we can define `cast_#{member}` methods.
372
+
373
+ e.g.
374
+
375
+ ```ruby
376
+ Data::State = Data.define(:id, :name, :country_id)
377
+ Data::Country = Data.define(:id, :name, :states) do
378
+ include ConfigFor::DataModel
379
+ config :countries
380
+
381
+ private
382
+
383
+ def cast_states(data = nil)
384
+ Array(data).map { Data::State[**it] }
385
+ end
386
+ end
387
+ ```
388
+
389
+ Now, our data looks like this:
390
+
391
+ ```ruby
392
+ Data::Country.find('AU')
393
+ # =>
394
+ # #<data Data::Country
395
+ # id="AU",
396
+ # name="Australia",
397
+ # states=
398
+ # [#<data Data::State id="ACT", name="Australian Capital Territory", country_id="AU">,
399
+ # #<data Data::State id="NSW", name="New South Wales", country_id="AU">, ...
400
+ ```
401
+
402
+ ### But what if we want to query State data directly?
403
+
404
+ Do we need to duplicate and restructure our data into a new YAML file? Actually, No!
405
+
406
+ In addition to a filename, the `config` class method also accepts a transform proc
407
+
408
+ e.g.
409
+
410
+ ```ruby
411
+ Data::State = Data.define(:id, :name, :country_id) do
412
+ include ConfigFor::DataModel
413
+ config :countries, -> { it.pluck(:states).flatten }
414
+ end
415
+ ```
416
+
417
+ Now we can query States directly.
418
+
419
+ e.g.
420
+
421
+ ```ruby
422
+ Data::State.where(country_id: 'AU')
423
+ # =>
424
+ # [#<data Data::State id="ACT", name="Australian Capital Territory", country_id="AU">,
425
+ # #<data Data::State id="NSW", name="New South Wales", country_id="AU">,
426
+ # #<data Data::State id="NT", name="Northern Territory", country_id="AU">,
427
+ # #<data Data::State id="QLD", name="Queensland", country_id="AU">,
428
+ # #<data Data::State id="SA", name="South Australia", country_id="AU">,
429
+ # #<data Data::State id="TAS", name="Tasmania", country_id="AU">,
430
+ # #<data Data::State id="VIC", name="Victoria", country_id="AU">,
431
+ # #<data Data::State id="WA", name="Western Australia", country_id="AU">]
432
+
433
+ Data::Country.find('US').states == Data::State.where(country_id: 'US')
434
+ # => true
435
+
436
+ Data::State.find_by(name: 'Missouri', country_id: 'US')
437
+ # => #<data Data::State id="MO", name="Missouri", country_id="US">
438
+
439
+ _.in? Data::Country.find('US').states
440
+ # => true
441
+ ```
442
+
443
+ ### Primary Keys
444
+
445
+ > [!IMPORTANT]
446
+ > If your data model does not have an `id` member, you need to provide an alternative `primary_key`
447
+
448
+ e.g.
449
+
450
+ ```ruby
451
+ Data::Book = Data.define(:name, :author, :isbn) do
452
+ include ConfigFor::DataModel
453
+ config :books
454
+
455
+ self.primary_key = :isbn
456
+ end
457
+ ```
458
+
459
+ ## Installation
460
+
461
+ Add this line to your application's Gemfile:
462
+
463
+ ```ruby
464
+ gem "config_for-data_model"
465
+ ```
466
+
467
+ And then execute:
468
+ ```bash
469
+ $ bundle
470
+ ```
471
+
472
+ Or install it yourself as:
473
+ ```bash
474
+ $ gem install config_for-data_model
475
+ ```
476
+
477
+ ## Contributing
478
+ Contribution directions go here.
479
+
480
+ ## License
481
+ 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,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ module ConfigFor
2
+ module DataModel
3
+ class Railtie < ::Rails::Railtie
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module ConfigFor
2
+ module DataModel
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,49 @@
1
+ require "config_for/data_model/version"
2
+ require "config_for/data_model/railtie"
3
+
4
+ module ConfigFor
5
+ module DataModel
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :source_data, instance_writer: false
10
+ class_attribute :primary_key, instance_writer: false, default: :id
11
+
12
+ members.each do |member|
13
+ private(define_method(:"cast_#{member}") { it })
14
+ end
15
+ end
16
+
17
+ def initialize(**kwargs)
18
+ super(**members.index_with { send(:"cast_#{it}", kwargs[it]) })
19
+ end
20
+
21
+ def blank? = send(primary_key).blank?
22
+
23
+ class_methods do
24
+ def config(filename, transform = -> { it })
25
+ self.source_data = transform[Rails.application.config_for(filename)].freeze
26
+ end
27
+
28
+ def all
29
+ @all ||= source_data.map { new(**it) }
30
+ end
31
+
32
+ def find(value)
33
+ find_by(primary_key => value)
34
+ end
35
+
36
+ def find_by(data)
37
+ all.find(&method(:match_all?).curry[data])
38
+ end
39
+
40
+ def where(data)
41
+ all.select(&method(:match_all?).curry[data])
42
+ end
43
+
44
+ def match_all?(constraints, resource)
45
+ constraints.all? { |key, value| resource.public_send(key) == value }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :config_for_data_model do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: config_for-data_model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andy Cohen
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.1'
26
+ description: A minimal, ActiveRecord::QueryMethods-like interface for data models
27
+ backed by Rails configuration files.
28
+ email:
29
+ - outlawandy@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - lib/config_for/data_model.rb
38
+ - lib/config_for/data_model/railtie.rb
39
+ - lib/config_for/data_model/version.rb
40
+ - lib/tasks/config_for/data_model_tasks.rake
41
+ homepage: https://github.com/OutlawAndy/config_for-data_model
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ homepage_uri: https://github.com/OutlawAndy/config_for-data_model
46
+ source_code_uri: https://github.com/OutlawAndy/config_for-data_model
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.6.9
62
+ specification_version: 4
63
+ summary: A small wrapper around Rails.application.config_for
64
+ test_files: []