dbee 1.2.1 → 2.0.0.pre.alpha

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: 4e099b2ff5d651a4bc97562314e0216e07afa778cc7b99cc775a2461f744ab3a
4
- data.tar.gz: 3d9ea4d091416484bdd5186caf02f9fec23e6d15d91fb9528965c9eff07f9669
3
+ metadata.gz: ccd40ea06b62b782bc525c069d38b01fc4c486b42507dd8f70e915ba197f01b6
4
+ data.tar.gz: a2f2fac76927357e5187d922c3da1ffd8e102430a878f8dedbec3aeb654ad0be
5
5
  SHA512:
6
- metadata.gz: 7a31b7129beab7271692bc7ff3534ce53977d80a017d5fee250e13fa1f5c586d51cd867c39e72aa646265287b2ae152a5b8d824bdd849459fedfb67f6b240ae6
7
- data.tar.gz: 5d0149dedb9a61efc00df03e37e454bfb53755a7ecdb8f8440751779e352b30e2f1f910a97f60df7bbbd3cd7f8111dbf2af1d2b93635a2dfc255e735cd3e2ebc
6
+ metadata.gz: ea4bfb0f14b33d70b7704ba8006189d5d725f90cf7de89d321d7147ab75c7b068644dd4d483b2aa7c1ee187080ae438096d05856b4c9d2b46265b5548a481d4a
7
+ data.tar.gz: ee4ecf4505be3b1911662de5553a3b38606af539a0bfadbc243fb2d38bf6d11310945bf2664d10795c97d0e6a82bc49262ae9550c0c7a867b25a4c1d9f3a4503
data/CHANGELOG.md CHANGED
@@ -1,55 +1,71 @@
1
+ # 2.0.0 (September 3rd, 2019)
2
+
3
+ ### Additions:
4
+
5
+ * New DSL method for declaring child associations: `child`.
6
+ * New DSL method for declaring parent associations: `parent`.
7
+ * Added global inflection using Dry::Inflector, can be overridden using Dbee.inflector = Dry::Inflector.new # or some other instance.
8
+
9
+ ### Breaking changes:
10
+
11
+ Inflection was introduced with this release and hence changes some of the default naming inference behavior:
12
+
13
+ * Table name is now inferred to be the pluralized and de-modulized class name (unless explicitly declared.)
14
+ * Top level root model name is inferred to be the underscored and de-modulized class name.
15
+ * Model is now inferred to be the relative, singular, and camelized association name (unless explicitly declared.)
16
+
1
17
  # 1.2.1 (September 1st, 2019)
2
18
 
3
- Fixes:
19
+ ### Fixes:
4
20
 
5
21
  * Dbee::Base table name should default to the most-parent table name (if none are explicitly defined.) Overriding still takes most child sub-class precedence.
6
22
 
7
23
  # 1.2.0 (August 29th, 2019)
8
24
 
9
- Additions:
25
+ ### Additions:
10
26
 
11
27
  * Added partitioners to a model specification. A Partitioner is essentially a way of explicitly specifying that a data model must meet a specific equality for a column.
12
28
 
13
- Changes:
29
+ ### Changes:
14
30
 
15
31
  * Model#ancestors renamed to Model#ancestors!
16
32
  * Model#ancestor now returns a hash where the key is an array of strings and not just a pre-concatenated string.
17
33
 
18
34
  # 1.1.0 (August 28th, 2019)
19
35
 
20
- Additions:
36
+ ### Additions:
21
37
 
22
38
  * Added better equality and sort methods for Ruby objects (added class type checks where needed.)
23
39
 
24
- Changes:
40
+ ### Changes:
25
41
 
26
42
  * Removed Sorter in favor of Sorter subclasses.
27
43
  * Duplicate filters and sorters will be ignored in a Query.
28
44
 
29
45
  # 1.0.3 (August 27th, 2019)
30
46
 
31
- Additions:
47
+ ### Additions:
32
48
 
33
49
  * Added 'parent' keyword for static constraints. This makes it possible to have a static constraint apply to a parent and not just a child relationship.
34
50
 
35
51
  # 1.0.2 (August 26th, 2019)
36
52
 
37
- Fixes:
53
+ ### Fixes:
38
54
 
39
55
  * Dbee::Base subclasses can now support cycles to N-depth, where N is limited by the Query. The recursion will go as far as the Query specifies it has to go.
40
56
 
41
- Additions:
57
+ ### Additions:
42
58
 
43
59
  * Equals filter is now the default type when type is omitted.
44
60
  * model can now be a string so it can be lazy evaluated at run-time instead of at script evaluation time.
45
61
 
46
62
  # 1.0.1 (August 26th, 2019)
47
63
 
48
- Fixes:
64
+ ### Fixes:
49
65
 
50
66
  * Dbee::Base subclasses can now declare self-referential associations. Note that it will stop the hierarchy once the cycle is detected.
51
67
 
52
- Additions:
68
+ ### Additions:
53
69
 
54
70
  * Static constraint is now the default type when type is omitted.
55
71
 
data/README.md CHANGED
@@ -80,58 +80,116 @@ There are two ways to model this schema using Dbee:
80
80
  1. code-first
81
81
  2. configuration-first
82
82
 
83
- #### Code-First Data Modeling
83
+ #### Code-First Data Modeling (With Inflection)
84
84
 
85
85
  Code-first data modeling involves creating sub-classes of Dbee::Base that describes the tables and associations. We could model the above example as:
86
86
 
87
87
  ````ruby
88
88
  module ReadmeDataModels
89
- class PhoneNumbers < Dbee::Base
89
+ class PhoneNumber < Dbee::Base
90
90
  table :phones
91
+
92
+ parent :patient
93
+ end
94
+
95
+ class Note < Dbee::Base
96
+ parent :patient
97
+ end
98
+
99
+ class Patient < Dbee::Base
100
+ child :notes
101
+
102
+ child :work_phone_number, model: 'ReadmeDataModels::PhoneNumber',
103
+ static: { name: :phone_number_type, value: 'work' }
104
+
105
+ child :cell_phone_number, model: 'ReadmeDataModels::PhoneNumber',
106
+ static: { name: :phone_number_type, value: 'cell' }
107
+
108
+ child :fax_phone_number, model: 'ReadmeDataModels::PhoneNumber',
109
+ static: { name: :phone_number_type, value: 'fax' }
110
+ end
111
+
112
+ class Practice < Dbee::Base
113
+ child :patients
114
+ end
115
+ end
116
+ ````
117
+
118
+ The two DSL methods: parent/child are very similar to ActiveRecord's belongs_to/has_many, respectively. Options for these methods are:
119
+
120
+ * **model**: class constant, string, or symbol to use as associated data model. If omitted, the model name will be the relative, singular, and camelized version of the association name.
121
+ * **foreign_key**: name of the key on the child table. If omitted for child then it will resolve as singular, underscored, de-modulized class name suffixed with '\_id'. If omitted for parent it will resolve to 'id'.
122
+ * **primary_key**: name of the key on the parent table. If omitted for child then it will resolve as 'id'. If omitted for parent then it will resolve as the singular, underscored, de-modulized name of the relationship suffixed with '\_id'
123
+
124
+ ##### Customizing Inflection Rules
125
+
126
+ Inflection is provided via the (Dry::Inflector gem)[https://github.com/dry-rb/dry-inflector]. There are options to add custom grammar rules which you can then pass into Dbee. For example:
127
+
128
+ ````ruby
129
+ Dbee.inflector = Dry::Inflector.new do |inflections|
130
+ inflections.plural 'virus', 'viruses' # specify a rule for #pluralize
131
+ inflections.singular 'thieves', 'thief' # specify a rule for #singularize
132
+ inflections.uncountable 'dry-inflector' # add an exception for an uncountable word
133
+ end
134
+ ````
135
+
136
+ #### Code-First Data Modeling (Without Inflection)
137
+
138
+ You can use the raw `association` method you wish to fully control the entire referencing configuration.
139
+
140
+ ````ruby
141
+ module ReadmeDataModels
142
+ class PhoneNumber < Dbee::Base
143
+ table :phones
144
+
145
+ association :patient, model: 'ReadmeDataModels::Patient', constraints: {
146
+ type: :reference, name: :patient_id, parent: :id
147
+ }
91
148
  end
92
149
 
93
- class Notes < Dbee::Base
150
+ class Note < Dbee::Base
151
+ association :patient, model: 'ReadmeDataModels::Patient', constraints: {
152
+ type: :reference, name: :patient_id, parent: :id
153
+ }
94
154
  end
95
155
 
96
- class Patients < Dbee::Base
97
- association :notes, model: Notes, constraints: {
156
+ class Patient < Dbee::Base
157
+ association :notes, model: 'ReadmeDataModels::Note', constraints: {
98
158
  type: :reference, name: :patient_id, parent: :id
99
159
  }
100
160
 
101
- association :work_phone_number, model: PhoneNumbers, constraints: [
161
+ association :work_phone_number, model: 'ReadmeDataModels::PhoneNumber', constraints: [
102
162
  { type: :reference, name: :patient_id, parent: :id },
103
163
  { type: :static, name: :phone_number_type, value: 'work' }
104
164
  ]
105
165
 
106
- association :cell_phone_number, model: PhoneNumbers, constraints: [
166
+ association :cell_phone_number, model: 'ReadmeDataModels::PhoneNumber', constraints: [
107
167
  { type: :reference, name: :patient_id, parent: :id },
108
168
  { type: :static, name: :phone_number_type, value: 'cell' }
109
169
  ]
110
170
 
111
- association :fax_phone_number, model: PhoneNumbers, constraints: [
171
+ association :fax_phone_number, model: 'ReadmeDataModels::PhoneNumber', constraints: [
112
172
  { type: :reference, name: :patient_id, parent: :id },
113
173
  { type: :static, name: :phone_number_type, value: 'fax' }
114
174
  ]
115
175
  end
116
176
 
117
- class Practices < Dbee::Base
118
- association :patients, model: Patients, constraints: {
177
+ class Practice < Dbee::Base
178
+ association :patients, model: Patient, constraints: {
119
179
  type: :reference, name: :practice_id, parent: :id
120
180
  }
121
181
  end
122
182
  end
123
-
124
183
  ````
125
184
 
126
- **Note:** the 'table' directive is optional, and if omitted, the classes name will be turned into snake_case and used. In the above example you can see we wanted the class name of PhoneNumbers but the table is actually 'phones'
127
-
185
+ The two code-first examples above should be technically equivalent.
128
186
 
129
187
  #### Configuration-First Data Modeling
130
188
 
131
189
  You can choose to alternatively describe your data model using configuration. The YAML below is equivalent to the Ruby sub-classes above:
132
190
 
133
191
  ````yaml
134
- name: practices
192
+ name: practice
135
193
  models:
136
194
  - name: patients
137
195
  constraints:
@@ -182,15 +240,14 @@ You can leverage the model partitioners for hard-coding partitioning by column=v
182
240
  ##### Code-first:
183
241
 
184
242
  ````ruby
185
- class Dogs < Dbee::Base
186
- table 'animals'
243
+ class Animal < Dbee::Base
244
+ end
187
245
 
246
+ class Dog < Animal
188
247
  partitioner :type, 'Dog'
189
248
  end
190
249
 
191
- class Cats < Dbee::Base
192
- table 'animals'
193
-
250
+ class Cat < Animal
194
251
  partitioner :type, 'Cat'
195
252
  end
196
253
  ````
@@ -199,14 +256,14 @@ end
199
256
 
200
257
  ````yaml
201
258
  Dogs:
202
- name: dogs
259
+ name: dog
203
260
  table: animals
204
261
  partitioners:
205
262
  - name: type
206
263
  value: Dog
207
264
 
208
265
  Cats:
209
- name: cats
266
+ name: cat
210
267
  table: animals
211
268
  partitioners:
212
269
  - name: type
@@ -309,10 +366,9 @@ Here are some sample executions based off the preceding examples:
309
366
  ##### Code-First Execution
310
367
 
311
368
  ````ruby
312
- require 'dbee'
313
369
  require 'dbee/providers/active_record_provider'
314
370
 
315
- class Practices < Dbee::Base; end
371
+ class Practice < Dbee::Base; end
316
372
 
317
373
  provider = Dbee::Providers::ActiveRecordProvider.new
318
374
 
@@ -324,19 +380,18 @@ query = {
324
380
  ]
325
381
  }
326
382
 
327
- sql = Dbee.sql(Practices, query, provider)
383
+ sql = Dbee.sql(Practice, query, provider)
328
384
  ````
329
385
 
330
386
  ##### Configuration-First Execution
331
387
 
332
388
  ````ruby
333
- require 'dbee'
334
389
  require 'dbee/providers/active_record_provider'
335
390
 
336
391
  provider = Dbee::Providers::ActiveRecordProvider.new
337
392
 
338
393
  model = {
339
- name: :practices
394
+ name: :practice
340
395
  }
341
396
 
342
397
  query = {
data/dbee.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.required_ruby_version = '>= 2.4.6'
23
23
 
24
24
  s.add_dependency('acts_as_hashable', '~>1', '>=1.1.0')
25
+ s.add_dependency('dry-inflector', '~>0')
25
26
 
26
27
  s.add_development_dependency('guard-rspec', '~>4.7')
27
28
  s.add_development_dependency('pry', '~>0')
data/lib/dbee/base.rb CHANGED
@@ -7,38 +7,28 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
- require_relative 'dsl/inflectable'
10
+ require_relative 'dsl/association'
11
+ require_relative 'dsl/association_builder'
12
+ require_relative 'dsl/methods'
11
13
  require_relative 'dsl/reflectable'
12
14
 
13
15
  module Dbee
14
16
  # Instead of using the configuration-first approach, you could use this super class for
15
17
  # Model declaration.
16
18
  class Base
17
- extend Dsl::Inflectable
18
19
  extend Dsl::Reflectable
20
+ extend Dsl::Methods
19
21
 
20
22
  BASE_CLASS_CONSTANT = Dbee::Base
21
23
 
22
24
  class << self
23
- def partitioner(name, value)
24
- partitioners << { name: name, value: value }
25
- end
26
-
27
- def table(name)
28
- tap { @table_name = name.to_s }
29
- end
30
-
31
- def association(name, opts = {})
32
- tap { associations_by_name[name.to_s] = opts.merge(name: name) }
33
- end
34
-
35
25
  # This method is cycle-resistant due to the fact that it is a requirement to send in a
36
26
  # key_chain. That means each model produced using to_model is specific to a set of desired
37
27
  # fields. Basically, you cannot derive a Model from a Base subclass without the context
38
28
  # of a Query. This is not true for configuration-first Model definitions because, in that
39
29
  # case, cycles do not exist since the nature of the configuration is flat.
40
30
  def to_model(key_chain, name = nil, constraints = [], path_parts = [])
41
- derived_name = name.to_s.empty? ? tableize(self.name) : name.to_s
31
+ derived_name = name.to_s.empty? ? inflected_class_name(self.name) : name.to_s
42
32
  key = [key_chain, derived_name, constraints, path_parts]
43
33
 
44
34
  to_models[key] ||= Model.make(
@@ -51,25 +41,9 @@ module Dbee
51
41
  )
52
42
  end
53
43
 
54
- def table_name
55
- @table_name || ''
56
- end
57
-
58
- def associations_by_name
59
- @associations_by_name ||= {}
60
- end
61
-
62
- def partitioners
63
- @partitioners ||= []
64
- end
65
-
66
- def table_name?
67
- !table_name.empty?
68
- end
69
-
70
44
  def inherited_table_name
71
45
  subclasses(BASE_CLASS_CONSTANT).find(&:table_name?)&.table_name ||
72
- tableize(reversed_subclasses(BASE_CLASS_CONSTANT).first.name)
46
+ inflected_table_name(reversed_subclasses(BASE_CLASS_CONSTANT).first.name)
73
47
  end
74
48
 
75
49
  def inherited_associations
@@ -97,16 +71,14 @@ module Dbee
97
71
  end
98
72
 
99
73
  def associations(key_chain, path_parts)
100
- inherited_associations.select { |c| key_chain.ancestor_path?(path_parts, c[:name]) }
101
- .each_with_object([]) do |config, memo|
102
- model_constant = constantize(config[:model])
103
- associated_constraints = config[:constraints]
104
- name = config[:name]
74
+ inherited_associations.select { |c| key_chain.ancestor_path?(path_parts, c.name) }
75
+ .map do |association|
76
+ model_constant = association.model_constant
105
77
 
106
- memo << model_constant.to_model(
78
+ model_constant.to_model(
107
79
  key_chain,
108
- name,
109
- associated_constraints,
80
+ association.name,
81
+ association.constraints,
110
82
  path_parts
111
83
  )
112
84
  end
@@ -115,6 +87,14 @@ module Dbee
115
87
  def to_models
116
88
  @to_models ||= {}
117
89
  end
90
+
91
+ def inflected_table_name(name)
92
+ inflector.pluralize(inflector.underscore(inflector.demodulize(name)))
93
+ end
94
+
95
+ def inflected_class_name(name)
96
+ inflector.underscore(inflector.demodulize(name))
97
+ end
118
98
  end
119
99
  end
120
100
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Dbee
11
+ module Dsl
12
+ # The main logic that can take input from model association declaration and turn it into
13
+ # usable information.
14
+ class Association
15
+ attr_reader :on_class_name, :inflector, :name, :opts
16
+
17
+ def initialize(on_class_name, inflector, name, opts = {})
18
+ raise ArgumentError, 'on_class_name is required' if on_class_name.to_s.empty?
19
+ raise ArgumentError, 'inflector is required' unless inflector
20
+ raise ArgumentError, 'name is required' if name.to_s.empty?
21
+
22
+ @on_class_name = on_class_name
23
+ @inflector = inflector
24
+ @name = name.to_s
25
+ @opts = opts || {}
26
+
27
+ freeze
28
+ end
29
+
30
+ def model_constant
31
+ constantize(class_name)
32
+ end
33
+
34
+ def constraints
35
+ opts[:constraints] || []
36
+ end
37
+
38
+ private
39
+
40
+ def class_name
41
+ opts[:model] || relative_class_name
42
+ end
43
+
44
+ def relative_class_name
45
+ (on_class_name.split('::')[0...-1] + [inflector.classify(name)]).join('::')
46
+ end
47
+
48
+ def constantize(value)
49
+ value.is_a?(String) || value.is_a?(Symbol) ? Object.const_get(value) : value
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Dbee
11
+ module Dsl
12
+ # This is really syntactic sugar built on top of Association. It can handle two main
13
+ # use-cases when declaring associations:
14
+ # - parent: create an association to a parent model (foreign key is on immediate table)
15
+ # - child: create an association to a child model (foreign key is on child table)
16
+ class AssociationBuilder
17
+ attr_reader :inflector
18
+
19
+ def initialize(inflector)
20
+ raise ArgumentError, 'inflector is required' unless inflector
21
+
22
+ @inflector = inflector
23
+
24
+ freeze
25
+ end
26
+
27
+ def parent_association(on_class_name, name, opts = {})
28
+ reference_constraint = {
29
+ name: opts[:foreign_key] || :id,
30
+ parent: opts[:primary_key] || inflector.foreign_key(name)
31
+ }
32
+
33
+ association(on_class_name, name, opts, reference_constraint)
34
+ end
35
+
36
+ def child_association(on_class_name, name, opts = {})
37
+ reference_constraint = {
38
+ name: opts[:foreign_key] || inflector.foreign_key(on_class_name),
39
+ parent: opts[:primary_key] || :id
40
+ }
41
+
42
+ association(on_class_name, name, opts, reference_constraint)
43
+ end
44
+
45
+ private
46
+
47
+ def association(on_class_name, name, opts, reference_constraint)
48
+ association_opts = {
49
+ model: opts[:model],
50
+ constraints: [reference_constraint] + make_constraints(opts)
51
+ }
52
+
53
+ Association.new(on_class_name, inflector, name, association_opts)
54
+ end
55
+
56
+ def make_constraints(opts = {})
57
+ array(opts[:constraints]) + array(opts[:static]).map { |c| c.merge(type: :static) }
58
+ end
59
+
60
+ def array(value)
61
+ value.is_a?(Hash) ? [value] : Array(value)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Dbee
11
+ module Dsl
12
+ # This mixin contains all the reader/writers for model meta-data declared through the DSL.
13
+ module Methods
14
+ def partitioner(name, value)
15
+ partitioners << { name: name, value: value }
16
+ end
17
+
18
+ def table(name)
19
+ tap { @table_name = name.to_s }
20
+ end
21
+
22
+ def parent(name, opts = {})
23
+ association = association_builder.parent_association(self.name, name, opts)
24
+
25
+ association(name, association)
26
+ end
27
+
28
+ def child(name, opts = {})
29
+ association = association_builder.child_association(self.name, name, opts)
30
+
31
+ association(name, association)
32
+ end
33
+
34
+ def association(name, opts = {})
35
+ value =
36
+ if opts.is_a?(Dsl::Association)
37
+ opts
38
+ else
39
+ Dsl::Association.new(self.name, inflector, name, opts)
40
+ end
41
+
42
+ tap do
43
+ associations_by_name[name.to_s] = value
44
+ end
45
+ end
46
+
47
+ def table_name
48
+ @table_name || ''
49
+ end
50
+
51
+ def associations_by_name
52
+ @associations_by_name ||= {}
53
+ end
54
+
55
+ def partitioners
56
+ @partitioners ||= []
57
+ end
58
+
59
+ def table_name?
60
+ !table_name.empty?
61
+ end
62
+
63
+ private
64
+
65
+ def inflector
66
+ Dbee.inflector
67
+ end
68
+
69
+ def association_builder
70
+ @association_builder ||= Dsl::AssociationBuilder.new(inflector)
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/dbee/version.rb CHANGED
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Dbee
11
- VERSION = '1.2.1'
11
+ VERSION = '2.0.0-alpha'
12
12
  end
data/lib/dbee.rb CHANGED
@@ -8,6 +8,7 @@
8
8
  #
9
9
 
10
10
  require 'acts_as_hashable'
11
+ require 'dry/inflector'
11
12
  require 'forwardable'
12
13
 
13
14
  require_relative 'dbee/base'
@@ -20,6 +21,15 @@ require_relative 'dbee/providers'
20
21
  # Top-level namespace that provides the main public API.
21
22
  module Dbee
22
23
  class << self
24
+ # Use this to override the built in Dry::Inflector instance.
25
+ # This is useful is you have your own grammar/overrides you need to load.
26
+ # See the referenced gem here: https://github.com/dry-rb/dry-inflector
27
+ attr_writer :inflector
28
+
29
+ def inflector
30
+ @inflector ||= Dry::Inflector.new
31
+ end
32
+
23
33
  def sql(model, query, provider)
24
34
  query = Query.make(query)
25
35
  model =
@@ -33,7 +33,7 @@ describe Dbee::Base do
33
33
 
34
34
  key_chain = Dbee::KeyChain.new(key_paths)
35
35
 
36
- actual_model = Models::Theaters.to_model(key_chain)
36
+ actual_model = Models::Theater.to_model(key_chain)
37
37
 
38
38
  expect(actual_model).to eq(expected_model)
39
39
  end
@@ -82,7 +82,7 @@ describe Dbee::Base do
82
82
  key_paths = %w[id]
83
83
  key_chain = Dbee::KeyChain.new(key_paths)
84
84
 
85
- actual_model = PartitionerExamples::Dogs.to_model(key_chain)
85
+ actual_model = PartitionerExamples::Dog.to_model(key_chain)
86
86
 
87
87
  expect(actual_model).to eq(expected_model)
88
88
  end
@@ -95,7 +95,7 @@ describe Dbee::Base do
95
95
  key_paths = %w[id dogs.id]
96
96
  key_chain = Dbee::KeyChain.new(key_paths)
97
97
 
98
- actual_model = PartitionerExamples::Owners.to_model(key_chain)
98
+ actual_model = PartitionerExamples::Owner.to_model(key_chain)
99
99
 
100
100
  expect(actual_model).to eq(expected_model)
101
101
  end
@@ -123,7 +123,7 @@ describe Dbee::Model do
123
123
  patients.fax_phone_number.e
124
124
  ])
125
125
 
126
- code_model = ReadmeDataModels::Practices.to_model(key_chain)
126
+ code_model = ReadmeDataModels::Practice.to_model(key_chain)
127
127
 
128
128
  expect(config_model).to eq(code_model)
129
129
  end
data/spec/dbee_spec.rb CHANGED
@@ -45,7 +45,7 @@ describe Dbee do
45
45
  end
46
46
 
47
47
  it 'accepts a Dbee::Base constant as a model and passes a Model instance to provider#sql' do
48
- model_constant = Models::Theaters
48
+ model_constant = Models::Theater
49
49
 
50
50
  expect(provider).to receive(:sql).with(model_constant.to_model(query.key_chain), query)
51
51
 
@@ -53,7 +53,7 @@ describe Dbee do
53
53
  end
54
54
 
55
55
  it 'accepts a Dbee::Query instance as a query and passes a Query instance to provider#sql' do
56
- model = Models::Theaters.to_model(query.key_chain)
56
+ model = Models::Theater.to_model(query.key_chain)
57
57
 
58
58
  expect(provider).to receive(:sql).with(model, query)
59
59
 
@@ -61,7 +61,7 @@ describe Dbee do
61
61
  end
62
62
 
63
63
  it 'accepts a hash as a query and passes a Query instance to provider#sql' do
64
- model = Models::Theaters.to_model(query.key_chain)
64
+ model = Models::Theater.to_model(query.key_chain)
65
65
 
66
66
  expect(provider).to receive(:sql).with(model, query)
67
67
 
@@ -8,60 +8,44 @@
8
8
  #
9
9
 
10
10
  module Models
11
- class Movies < Dbee::Base
12
- end
11
+ class Movie < Dbee::Base; end
13
12
 
14
- class PhoneNumbers < Dbee::Base
15
- end
13
+ class PhoneNumber < Dbee::Base; end
16
14
 
17
- class Demographics < Dbee::Base
18
- association :phone_numbers, model: 'Models::PhoneNumbers',
19
- constraints: {
20
- name: :demographic_id,
21
- parent: :id
22
- }
15
+ class Demographic < Dbee::Base
16
+ child :phone_numbers
23
17
  end
24
18
 
25
- class MembersBase < Dbee::Base
26
- table 'members'
19
+ class MemberBase < Dbee::Base
20
+ child :demos, model: 'Models::Demographic', foreign_key: :member_id
27
21
 
28
- association :demos, model: Demographics,
29
- constraints: { type: :reference, name: :member_id, parent: :id }
30
-
31
- association :movies, model: Movies,
32
- constraints: { name: :member_id, parent: :id }
22
+ child :movies, foreign_key: :member_id
33
23
  end
34
24
 
35
- class Members < MembersBase
36
- association :favorite_comic_movies, model: Movies, constraints: [
37
- { type: :reference, name: :member_id, parent: :id },
38
- { type: :static, name: :genre, value: 'comic' }
39
- ]
25
+ class Member < MemberBase
26
+ table 'members'
27
+
28
+ child :favorite_comic_movies, model: Movie, static: { name: :genre, value: 'comic' }
40
29
 
41
- association :favorite_mystery_movies, model: Movies, constraints: [
42
- { type: :reference, name: :member_id, parent: :id },
30
+ child :favorite_mystery_movies, model: Movie, constraints: [
43
31
  { type: :static, name: :genre, value: 'mystery' }
44
32
  ]
45
33
 
46
- association :favorite_comedy_movies, model: Movies, constraints: [
47
- { type: :reference, name: :member_id, parent: :id },
48
- { type: :static, name: :genre, value: 'comedy' }
49
- ]
34
+ child :favorite_comedy_movies, model: Movie, constraints: {
35
+ type: :static, name: :genre, value: 'comedy'
36
+ }
50
37
  end
51
38
 
52
- class TheatersBase < Dbee::Base
53
- association :members, model: Members, constraints: [
54
- { type: :reference, name: :tid, parent: :id },
39
+ class TheaterBase < Dbee::Base
40
+ child :members, foreign_key: :tid, constraints: [
55
41
  { type: :reference, name: :partition, parent: :partition }
56
42
  ]
57
43
  end
58
44
 
59
- class Theaters < TheatersBase
45
+ class Theater < TheaterBase
60
46
  table 'theaters'
61
47
 
62
- association :parent_theater, model: self, constraints: [
63
- { type: :reference, name: :id, parent: :parent_theater_id }
64
- ]
48
+ parent :parent_theater, model: self
65
49
  end
66
50
 
67
51
  class A < Dbee::Base
@@ -72,8 +56,7 @@ module Models
72
56
  table 'table_set_to_b'
73
57
  end
74
58
 
75
- class C < A
76
- end
59
+ class C < A; end
77
60
 
78
61
  class D < A
79
62
  table ''
@@ -85,36 +68,30 @@ module Models
85
68
  end
86
69
 
87
70
  module ReadmeDataModels
88
- class PhoneNumbers < Dbee::Base
71
+ class PhoneNumber < Dbee::Base
89
72
  table :phones
90
73
  end
91
74
 
92
- class Notes < Dbee::Base
93
- end
75
+ class Note < Dbee::Base; end
94
76
 
95
- class Patients < Dbee::Base
96
- association :notes, model: Notes, constraints: {
97
- type: :reference, name: :patient_id, parent: :id
98
- }
77
+ class Patient < Dbee::Base
78
+ child :notes
99
79
 
100
- association :work_phone_number, model: PhoneNumbers, constraints: [
101
- { type: :reference, name: :patient_id, parent: :id },
102
- { type: :static, name: :phone_number_type, value: 'work' }
103
- ]
80
+ child :work_phone_number, model: PhoneNumber, static: {
81
+ name: :phone_number_type, value: 'work'
82
+ }
104
83
 
105
- association :cell_phone_number, model: PhoneNumbers, constraints: [
106
- { type: :reference, name: :patient_id, parent: :id },
84
+ child :cell_phone_number, model: PhoneNumber, constraints: [
107
85
  { type: :static, name: :phone_number_type, value: 'cell' }
108
86
  ]
109
87
 
110
- association :fax_phone_number, model: PhoneNumbers, constraints: [
111
- { type: :reference, name: :patient_id, parent: :id },
88
+ child :fax_phone_number, model: PhoneNumber, constraints: [
112
89
  { type: :static, name: :phone_number_type, value: 'fax' }
113
90
  ]
114
91
  end
115
92
 
116
- class Practices < Dbee::Base
117
- association :patients, model: Patients, constraints: {
93
+ class Practice < Dbee::Base
94
+ association :patients, constraints: {
118
95
  type: :reference, name: :practice_id, parent: :id
119
96
  }
120
97
  end
@@ -126,36 +103,35 @@ module Cycles
126
103
  end
127
104
 
128
105
  class A < BaseA
106
+ table :as
107
+
129
108
  association :b2, model: 'Cycles::B'
130
109
  end
131
110
 
132
111
  class B < Dbee::Base
133
- association :c, model: 'Cycles::C'
112
+ association :c
134
113
 
135
- association :d, model: 'Cycles::D'
114
+ association :d
136
115
  end
137
116
 
138
117
  class C < Dbee::Base
139
- association :a, model: 'Cycles::A'
118
+ association :a
140
119
  end
141
120
 
142
121
  class D < Dbee::Base
143
- association :a, model: 'Cycles::A'
122
+ association :a
144
123
  end
145
124
  end
146
125
 
147
126
  # Examples of Rails Single Table Inheritance.
148
127
  module PartitionerExamples
149
- class Owners < Dbee::Base
150
- association :dogs, model: 'PartitionerExamples::Dogs', constraints: {
151
- name: :owner_id, parent: :id
152
- }
128
+ class Owner < Dbee::Base
129
+ child :dogs
153
130
  end
154
131
 
155
- class Animals < Dbee::Base
156
- end
132
+ class Animal < Dbee::Base; end
157
133
 
158
- class Dogs < Animals
134
+ class Dog < Animal
159
135
  partitioner :type, 'Dog'
160
136
 
161
137
  partitioner :deleted, false
@@ -1,5 +1,5 @@
1
1
  Theaters, Members, and Movies:
2
- name: theaters
2
+ name: theater
3
3
  table: theaters
4
4
  models:
5
5
  - name: members
@@ -120,7 +120,8 @@ Theaters, Members, and Movies:
120
120
  name: genre
121
121
  value: comedy
122
122
  Readme:
123
- name: practices
123
+ name: practice
124
+ table: practices
124
125
  models:
125
126
  - name: patients
126
127
  constraints:
@@ -162,38 +163,43 @@ Readme:
162
163
  value: fax
163
164
  Cycle Example:
164
165
  name: a
165
- table: base_a
166
+ table: as
166
167
  models:
167
168
  - name: b1
168
- table: b
169
+ table: bs
169
170
  models:
170
171
  - name: c
172
+ table: cs
171
173
  models:
172
174
  - name: a
173
- table: base_a
175
+ table: as
174
176
  - name: d
177
+ table: ds
175
178
  models:
176
179
  - name: a
177
- table: base_a
180
+ table: as
178
181
  - name: b2
179
- table: b
182
+ table: bs
180
183
  models:
181
184
  - name: c
185
+ table: cs
182
186
  models:
183
187
  - name: a
184
- table: base_a
188
+ table: as
185
189
  - name: d
190
+ table: ds
186
191
  models:
187
192
  - name: a
188
- table: base_a
193
+ table: as
189
194
  models:
190
195
  - name: b1
191
- table: b
196
+ table: bs
192
197
  models:
193
198
  - name: c
199
+ table: cs
194
200
 
195
201
  Partitioner Example 1:
196
- name: dogs
202
+ name: dog
197
203
  table: animals
198
204
  partitioners:
199
205
  - name: type
@@ -202,7 +208,8 @@ Partitioner Example 1:
202
208
  value: false
203
209
 
204
210
  Partitioner Example 2:
205
- name: owners
211
+ name: owner
212
+ table: owners
206
213
  models:
207
214
  - name: dogs
208
215
  table: animals
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-02 00:00:00.000000000 Z
11
+ date: 2019-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_hashable
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: dry-inflector
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: guard-rspec
35
49
  requirement: !ruby/object:Gem::Requirement
@@ -155,7 +169,9 @@ files:
155
169
  - dbee.gemspec
156
170
  - lib/dbee.rb
157
171
  - lib/dbee/base.rb
158
- - lib/dbee/dsl/inflectable.rb
172
+ - lib/dbee/dsl/association.rb
173
+ - lib/dbee/dsl/association_builder.rb
174
+ - lib/dbee/dsl/methods.rb
159
175
  - lib/dbee/dsl/reflectable.rb
160
176
  - lib/dbee/key_chain.rb
161
177
  - lib/dbee/key_path.rb
@@ -221,9 +237,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
221
237
  version: 2.4.6
222
238
  required_rubygems_version: !ruby/object:Gem::Requirement
223
239
  requirements:
224
- - - ">="
240
+ - - ">"
225
241
  - !ruby/object:Gem::Version
226
- version: '0'
242
+ version: 1.3.1
227
243
  requirements: []
228
244
  rubygems_version: 3.0.3
229
245
  signing_key:
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
- #
6
- # This source code is licensed under the MIT license found in the
7
- # LICENSE file in the root directory of this source tree.
8
- #
9
-
10
- module Dbee
11
- module Dsl
12
- # Provides methods for dealing with naming grammar.
13
- module Inflectable
14
- def constantize(value)
15
- value.is_a?(String) || value.is_a?(Symbol) ? Object.const_get(value) : value
16
- end
17
-
18
- def tableize(value)
19
- value.split('::')
20
- .last
21
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
22
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
23
- .tr('-', '_')
24
- .downcase
25
- end
26
- end
27
- end
28
- end