dbee 1.0.0.pre.alpha

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +8 -0
  3. data/.gitignore +6 -0
  4. data/.rubocop.yml +23 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +26 -0
  7. data/CHANGELOG.md +7 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +5 -0
  10. data/Guardfile +16 -0
  11. data/LICENSE +7 -0
  12. data/README.md +368 -0
  13. data/Rakefile +15 -0
  14. data/bin/console +18 -0
  15. data/dbee.gemspec +33 -0
  16. data/lib/dbee/base.rb +109 -0
  17. data/lib/dbee/model/constraints/base.rb +36 -0
  18. data/lib/dbee/model/constraints/reference.rb +42 -0
  19. data/lib/dbee/model/constraints/static.rb +40 -0
  20. data/lib/dbee/model/constraints.rb +26 -0
  21. data/lib/dbee/model.rb +79 -0
  22. data/lib/dbee/providers/null_provider.rb +21 -0
  23. data/lib/dbee/providers.rb +10 -0
  24. data/lib/dbee/query/field.rb +41 -0
  25. data/lib/dbee/query/filters/base.rb +39 -0
  26. data/lib/dbee/query/filters/contains.rb +19 -0
  27. data/lib/dbee/query/filters/equals.rb +19 -0
  28. data/lib/dbee/query/filters/greater_than.rb +19 -0
  29. data/lib/dbee/query/filters/greater_than_or_equal_to.rb +19 -0
  30. data/lib/dbee/query/filters/less_than.rb +19 -0
  31. data/lib/dbee/query/filters/less_than_or_equal_to.rb +19 -0
  32. data/lib/dbee/query/filters/not_contain.rb +19 -0
  33. data/lib/dbee/query/filters/not_equals.rb +19 -0
  34. data/lib/dbee/query/filters/not_start_with.rb +19 -0
  35. data/lib/dbee/query/filters/starts_with.rb +19 -0
  36. data/lib/dbee/query/filters.rb +40 -0
  37. data/lib/dbee/query/key_path.rb +54 -0
  38. data/lib/dbee/query/sorter.rb +53 -0
  39. data/lib/dbee/query.rb +45 -0
  40. data/lib/dbee/version.rb +12 -0
  41. data/lib/dbee.rb +28 -0
  42. data/spec/dbee/base_spec.rb +35 -0
  43. data/spec/dbee/model/constraints/base_spec.rb +42 -0
  44. data/spec/dbee/model/constraints/reference_spec.rb +37 -0
  45. data/spec/dbee/model/constraints/static_spec.rb +29 -0
  46. data/spec/dbee/model/constraints_spec.rb +25 -0
  47. data/spec/dbee/model_spec.rb +113 -0
  48. data/spec/dbee/providers/null_provider_spec.rb +22 -0
  49. data/spec/dbee/query/field_spec.rb +42 -0
  50. data/spec/dbee/query/filters/base_spec.rb +42 -0
  51. data/spec/dbee/query/filters_spec.rb +33 -0
  52. data/spec/dbee/query/key_path_spec.rb +35 -0
  53. data/spec/dbee/query/sorter_spec.rb +72 -0
  54. data/spec/dbee/query_spec.rb +94 -0
  55. data/spec/dbee_spec.rb +60 -0
  56. data/spec/fixtures/models.rb +115 -0
  57. data/spec/fixtures/models.yaml +101 -0
  58. data/spec/spec_helper.rb +52 -0
  59. metadata +239 -0
data/lib/dbee/base.rb ADDED
@@ -0,0 +1,109 @@
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
+ # Instead of using the configuration-first approach, you could use this super class for
12
+ # Model declaration.
13
+ class Base
14
+ class << self
15
+ def table(name)
16
+ @table_name = name.to_s
17
+
18
+ self
19
+ end
20
+
21
+ def association(name, opts = {})
22
+ associations_by_name[name.to_s] = opts.merge(name: name)
23
+
24
+ self
25
+ end
26
+
27
+ def to_model(name = nil, constraints = [])
28
+ name = derive_name(name)
29
+ key = [name, constraints]
30
+
31
+ to_models[key] ||= Model.make(model_config(name, constraints))
32
+ end
33
+
34
+ def table_name
35
+ @table_name.to_s
36
+ end
37
+
38
+ def associations_by_name
39
+ @associations_by_name ||= {}
40
+ end
41
+
42
+ def inherited_table_name
43
+ subclasses.each do |subclass|
44
+ return subclass.table_name unless subclass.table_name.empty?
45
+ end
46
+
47
+ ''
48
+ end
49
+
50
+ def inherited_associations_by_name
51
+ reversed_subclasses.each_with_object({}) do |subclass, memo|
52
+ memo.merge!(subclass.associations_by_name)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def subclasses
59
+ ancestors.select { |a| a < Dbee::Base }
60
+ end
61
+
62
+ def reversed_subclasses
63
+ subclasses.reverse
64
+ end
65
+
66
+ def model_config(name, constraints)
67
+ {
68
+ constraints: constraints,
69
+ models: associations,
70
+ name: name,
71
+ table: derive_table
72
+ }
73
+ end
74
+
75
+ def derive_name(name)
76
+ name.to_s.empty? ? inflected_name : name.to_s
77
+ end
78
+
79
+ def derive_table
80
+ inherited_table = inherited_table_name
81
+
82
+ inherited_table.empty? ? inflected_name : inherited_table
83
+ end
84
+
85
+ def associations
86
+ inherited_associations_by_name.values.each_with_object([]) do |config, memo|
87
+ model_klass = config[:model]
88
+ associated_constraints = config[:constraints]
89
+ name = config[:name]
90
+
91
+ memo << model_klass.to_model(name, associated_constraints)
92
+ end
93
+ end
94
+
95
+ def to_models
96
+ @to_models ||= {}
97
+ end
98
+
99
+ def inflected_name
100
+ name.split('::')
101
+ .last
102
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
103
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
104
+ .tr('-', '_')
105
+ .downcase
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,36 @@
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
+ class Model
12
+ class Constraints
13
+ # Base class for all constraints.
14
+ class Base
15
+ acts_as_hashable
16
+
17
+ attr_reader :name
18
+
19
+ def initialize(name:)
20
+ raise ArgumentError, 'name is required' if name.to_s.empty?
21
+
22
+ @name = name.to_s
23
+ end
24
+
25
+ def hash
26
+ name.hash
27
+ end
28
+
29
+ def ==(other)
30
+ other.name == name
31
+ end
32
+ alias eql? ==
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,42 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Model
14
+ class Constraints
15
+ # A Reference constraint is a constraint between two data models. In DB terms:
16
+ # the name represents the column name on the child and the parent represents the
17
+ # column name on the parent table.
18
+ class Reference < Base
19
+ attr_reader :parent
20
+
21
+ def initialize(name:, parent:)
22
+ super(name: name)
23
+
24
+ raise ArgumentError, 'parent is required' if parent.to_s.empty?
25
+
26
+ @parent = parent.to_s
27
+
28
+ freeze
29
+ end
30
+
31
+ def hash
32
+ "#{super}#{parent}".hash
33
+ end
34
+
35
+ def ==(other)
36
+ super && other.parent == parent
37
+ end
38
+ alias eql? ==
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Model
14
+ class Constraints
15
+ # A static constraint is a equality constraint on a child column to a static value.
16
+ # It is usually used in conjunction with a ReferenceConstraint, further giving it more
17
+ # scoping.
18
+ class Static < Base
19
+ attr_reader :value
20
+
21
+ def initialize(name:, value: nil)
22
+ super(name: name)
23
+
24
+ @value = value
25
+
26
+ freeze
27
+ end
28
+
29
+ def hash
30
+ "#{super}#{value}".hash
31
+ end
32
+
33
+ def ==(other)
34
+ super && other.value == value
35
+ end
36
+ alias eql? ==
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
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
+ require_relative 'constraints/reference'
11
+ require_relative 'constraints/static'
12
+
13
+ module Dbee
14
+ class Model
15
+ # Top-level class that allows for the making of constraints. For example,
16
+ # you can call this as:
17
+ # - Constraints.make(type: :reference, name: :id, parent: some_id)
18
+ # - Constraints.make(type: :static, name: :genre, value: :comedy)
19
+ class Constraints
20
+ acts_as_hashable_factory
21
+
22
+ register 'reference', Reference
23
+ register 'static', Static
24
+ end
25
+ end
26
+ end
data/lib/dbee/model.rb ADDED
@@ -0,0 +1,79 @@
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
+ require_relative 'model/constraints'
11
+
12
+ module Dbee
13
+ # In DB terms, a Model is usually a table, but it does not have to be. You can also re-model
14
+ # your DB schema using Dbee::Models.
15
+ class Model
16
+ acts_as_hashable
17
+
18
+ JOIN_CHAR = '.'
19
+
20
+ class ModelNotFound < StandardError; end
21
+
22
+ attr_reader :constraints, :name
23
+
24
+ def initialize(name:, constraints: [], models: [], table: '')
25
+ raise ArgumentError, 'name is required' if name.to_s.empty?
26
+
27
+ @name = name.to_s
28
+ @constraints = Constraints.array(constraints)
29
+ @models_by_name = name_hash(Model.array(models))
30
+ @table = table.to_s
31
+
32
+ freeze
33
+ end
34
+
35
+ def name_hash(array)
36
+ array.map { |a| [a.name, a] }.to_h
37
+ end
38
+
39
+ def table
40
+ @table.to_s.empty? ? name : @table
41
+ end
42
+
43
+ def models
44
+ models_by_name.values
45
+ end
46
+
47
+ def ancestors(parts = [], alias_chain = [], found = {})
48
+ return found if Array(parts).empty?
49
+
50
+ alias_chain = [] if Array(alias_chain).empty?
51
+
52
+ model_name = parts.first
53
+
54
+ model = models_by_name[model_name.to_s]
55
+
56
+ raise ModelNotFound, "Cannot traverse: #{model_name}" unless model
57
+
58
+ new_alias_chain = alias_chain + [model_name]
59
+
60
+ new_alias = new_alias_chain.join(JOIN_CHAR)
61
+
62
+ found[new_alias] = model
63
+
64
+ model.ancestors(parts[1..-1], new_alias_chain, found)
65
+ end
66
+
67
+ def ==(other)
68
+ other.name == name &&
69
+ other.table == table &&
70
+ other.models == models &&
71
+ other.constraints == constraints
72
+ end
73
+ alias eql? ==
74
+
75
+ private
76
+
77
+ attr_reader :models_by_name
78
+ end
79
+ end
@@ -0,0 +1,21 @@
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 Providers
12
+ # Default stand-in provider that ships with Dbee. The main use-case would be to plug in a
13
+ # provider or provide your own implementation. There really is no real-world use
14
+ # of this provider.
15
+ class NullProvider
16
+ def sql(_model, _query)
17
+ 'SELECT NULL'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
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
+ require_relative 'providers/null_provider'
@@ -0,0 +1,41 @@
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
+ require_relative 'key_path'
11
+
12
+ module Dbee
13
+ class Query
14
+ # This class is an abstraction of the SELECT part of a SQL statement.
15
+ # The key_path is the relative path to the column while the display is the AS part of the
16
+ # SQL SELECT statement.
17
+ class Field
18
+ acts_as_hashable
19
+
20
+ attr_reader :key_path, :display
21
+
22
+ def initialize(key_path:, display: nil)
23
+ raise ArgumentError, 'key_path is required' if key_path.to_s.empty?
24
+
25
+ @key_path = KeyPath.get(key_path)
26
+ @display = (display.to_s.empty? ? key_path : display).to_s
27
+
28
+ freeze
29
+ end
30
+
31
+ def hash
32
+ "#{key_path}#{display}".hash
33
+ end
34
+
35
+ def ==(other)
36
+ other.key_path == key_path && other.display == display
37
+ end
38
+ alias eql? ==
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,39 @@
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
+ class Query
12
+ class Filters
13
+ # Defines the shared implementation for all filters.
14
+ class Base
15
+ acts_as_hashable
16
+
17
+ attr_reader :key_path, :value
18
+
19
+ def initialize(key_path:, value: nil)
20
+ raise ArgumentError, 'key_path is required' if key_path.to_s.empty?
21
+
22
+ @key_path = KeyPath.get(key_path)
23
+ @value = value
24
+
25
+ freeze
26
+ end
27
+
28
+ def hash
29
+ "#{key_path}#{value}".hash
30
+ end
31
+
32
+ def ==(other)
33
+ other.key_path == key_path && other.value == value
34
+ end
35
+ alias eql? ==
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x LIKE '%value%' statement.
16
+ class Contains < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL WHERE x = 'y' statement.
16
+ class Equals < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x > 123 statement.
16
+ class GreaterThan < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x >= 123 statement.
16
+ class GreaterThanOrEqualTo < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x < 123 statement.
16
+ class LessThan < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x <= 123 statement.
16
+ class LessThanOrEqualTo < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x NOT LIKE '%value%' statement.
16
+ class NotContain < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL WHERE x != 'y' statement.
16
+ class NotEquals < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x NOT LIKE 'value%' statement.
16
+ class NotStartWith < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
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
+ require_relative 'base'
11
+
12
+ module Dbee
13
+ class Query
14
+ class Filters
15
+ # Equivalent to a SQL x LIKE 'value%' statement.
16
+ class StartsWith < Base; end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
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
+ require_relative 'filters/contains'
11
+ require_relative 'filters/equals'
12
+ require_relative 'filters/greater_than_or_equal_to'
13
+ require_relative 'filters/greater_than'
14
+ require_relative 'filters/less_than_or_equal_to'
15
+ require_relative 'filters/less_than'
16
+ require_relative 'filters/not_contain'
17
+ require_relative 'filters/not_equals'
18
+ require_relative 'filters/not_start_with'
19
+ require_relative 'filters/starts_with'
20
+
21
+ module Dbee
22
+ class Query
23
+ # Top-level class that allows for the making of filters. For example, you can call this as:
24
+ # - Filters.make(type: :contains, value: 'something')
25
+ class Filters
26
+ acts_as_hashable_factory
27
+
28
+ register 'contains', Contains
29
+ register 'equals', Equals
30
+ register 'greater_than_or_equal_to', GreaterThanOrEqualTo
31
+ register 'greater_than', GreaterThan
32
+ register 'less_than_or_equal_to', LessThanOrEqualTo
33
+ register 'less_than', LessThan
34
+ register 'not_contain', NotContain
35
+ register 'not_equals', NotEquals
36
+ register 'not_start_with', NotStartWith
37
+ register 'starts_with', StartsWith
38
+ end
39
+ end
40
+ end