active_house 0.4.0 → 0.5.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -0
  3. data/active_house.gemspec +1 -0
  4. data/lib/active_house.rb +1 -1
  5. data/lib/active_house/connection.rb +3 -3
  6. data/lib/active_house/exceptions.rb +9 -0
  7. data/lib/active_house/{logger.rb → logging.rb} +1 -1
  8. data/lib/active_house/model.rb +14 -12
  9. data/lib/active_house/modeling/attributes.rb +102 -0
  10. data/lib/active_house/modeling/connection.rb +38 -0
  11. data/lib/active_house/modeling/query.rb +29 -0
  12. data/lib/active_house/modeling/scope.rb +42 -0
  13. data/lib/active_house/prepared_statement.rb +34 -2
  14. data/lib/active_house/query_builder.rb +38 -0
  15. data/lib/active_house/querying/array_join.rb +42 -0
  16. data/lib/active_house/querying/collect.rb +80 -0
  17. data/lib/active_house/querying/from.rb +30 -0
  18. data/lib/active_house/querying/group_by.rb +30 -0
  19. data/lib/active_house/querying/having.rb +41 -0
  20. data/lib/active_house/querying/limit.rb +35 -0
  21. data/lib/active_house/querying/order_by.rb +58 -0
  22. data/lib/active_house/querying/scope.rb +45 -0
  23. data/lib/active_house/querying/select.rb +35 -0
  24. data/lib/active_house/querying/union.rb +67 -0
  25. data/lib/active_house/querying/where.rb +50 -0
  26. data/lib/active_house/version.rb +1 -1
  27. metadata +34 -22
  28. data/lib/active_house/array_joinable.rb +0 -31
  29. data/lib/active_house/chainable.rb +0 -151
  30. data/lib/active_house/collectable.rb +0 -44
  31. data/lib/active_house/connecting.rb +0 -36
  32. data/lib/active_house/connection_error.rb +0 -6
  33. data/lib/active_house/error.rb +0 -4
  34. data/lib/active_house/fromable.rb +0 -29
  35. data/lib/active_house/groupable.rb +0 -23
  36. data/lib/active_house/havingable.rb +0 -50
  37. data/lib/active_house/limitable.rb +0 -27
  38. data/lib/active_house/modeling.rb +0 -100
  39. data/lib/active_house/orderable.rb +0 -45
  40. data/lib/active_house/query.rb +0 -22
  41. data/lib/active_house/querying.rb +0 -21
  42. data/lib/active_house/scopeable.rb +0 -42
  43. data/lib/active_house/scoping.rb +0 -37
  44. data/lib/active_house/selectable.rb +0 -35
  45. data/lib/active_house/unionable.rb +0 -43
  46. data/lib/active_house/whereable.rb +0 -57
@@ -1,27 +0,0 @@
1
- module ActiveHouse
2
- module Limitable
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- private
7
-
8
- def build_limit_query_part
9
- return if @limit[:limit].nil?
10
- if @limit[:offset]
11
- "LIMIT #{@limit[:limit]}, #{@limit[:offset]}"
12
- else
13
- "LIMIT #{@limit[:limit]}"
14
- end
15
- end
16
- end
17
-
18
- def initialize(*)
19
- @limit = { offset: nil, limit: nil }
20
- super
21
- end
22
-
23
- def limit(limit_value, offset_value = nil)
24
- chain_query limit: { offset: offset_value || @limit[:offset], limit: limit_value }
25
- end
26
- end
27
- end
@@ -1,100 +0,0 @@
1
- require 'active_support/concern'
2
-
3
- module ActiveHouse
4
- module Modeling
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_attribute_opts, instance_writer: false
9
- self._attribute_opts = {}
10
-
11
- private
12
-
13
- def parse_attribute_method_name(method_name)
14
- name, is_setter = method_name.to_s.match(/\A(.+)?(=)?\z/).captures
15
- name = name.to_sym
16
- is_setter = !is_setter.nil?
17
- [name, is_setter]
18
- end
19
-
20
- def attribute_method?(name, is_setter, *args)
21
- (_attribute_opts.key?(name) || @_attributes.key?(name)) && (is_setter ? args.size == 1 : true)
22
- end
23
-
24
- def get_attribute(name)
25
- @_attributes[name]
26
- end
27
-
28
- def set_attribute(name, value)
29
- opts = _attribute_opts.fetch(name, {})
30
- value = opts[:cast].call(value) if opts[:cast]
31
- @_attributes[name] = value
32
- end
33
- end
34
-
35
- class_methods do
36
- def attribute(name, options = {})
37
- name = name.to_sym
38
- self._attribute_opts = _attribute_opts.merge(name => options)
39
- define_method(name) do
40
- get_attribute(name)
41
- end
42
- define_method("#{name}=") do |value|
43
- set_attribute(name, value)
44
- end
45
- end
46
-
47
- def attributes(*names)
48
- options = names.extract_options!
49
- names.each { |name| attribute(name, options.dup) }
50
- end
51
-
52
- def load!(params)
53
- new.tap do |model|
54
- params.each { |name, value| model[name] = value }
55
- end
56
- end
57
- end
58
-
59
- def initialize(params = {})
60
- @_attributes = {}
61
- assign_attributes(params) unless params.nil?
62
- end
63
-
64
- def as_json(*_args)
65
- to_h
66
- end
67
-
68
- def to_h
69
- @_attributes.dup
70
- end
71
-
72
- def [](key)
73
- get_attribute(key.to_sym)
74
- end
75
-
76
- def []=(key, value)
77
- set_attribute(key.to_sym, value)
78
- end
79
-
80
- def assign_attributes(params)
81
- params.each do |key, val|
82
- public_send("#{key}=", val)
83
- end
84
- end
85
-
86
- def respond_to_missing?(method_name, *args)
87
- name, is_setter = parse_attribute_method_name(method_name)
88
- attribute_method?(name, is_setter, *args)
89
- end
90
-
91
- def method_missing(method_name, *args, &block)
92
- name, is_setter = parse_attribute_method_name(method_name)
93
- if attribute_method?(name, is_setter, *args)
94
- is_setter ? set_attribute(name, args.first) : get_attribute(name)
95
- else
96
- super
97
- end
98
- end
99
- end
100
- end
@@ -1,45 +0,0 @@
1
- require 'active_support/core_ext/object/try'
2
-
3
- module ActiveHouse
4
- module Orderable
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- private
9
-
10
- def build_order_by_query_part
11
- "ORDER BY #{@ordering.join(', ')}" unless @ordering.empty?
12
- end
13
- end
14
-
15
- def initialize(*)
16
- @ordering = []
17
- super
18
- end
19
-
20
- def order_by(*clauses)
21
- raise ArgumentError, 'wrong number of arguments' if clauses.empty?
22
- formatter_clauses = clauses.map do |clause|
23
- if clause.is_a?(String)
24
- clause
25
- elsif clause.is_a?(Symbol)
26
- clause.to_s
27
- elsif clause.is_a?(Hash)
28
- if clause.keys.one?
29
- direction = clause.values.first
30
- raise ArgumentError, 'direction must be asc or desc' unless [:asc, :desc].include?(direction.try!(:to_sym))
31
- "#{clause.keys.first} #{direction.to_s.upcase}"
32
- else
33
- clause.assert_valid_keys(:field, :direction, :collate)
34
- [
35
- clause.fetch(:field),
36
- clause[:direction].try!(:to_s).try!(:upcase),
37
- clause.key?(:collate) ? "COLLATE '#{clause[:collate]}'" : nil
38
- ].reject(&:nil?).join(' ')
39
- end
40
- end
41
- end
42
- chain_query ordering: (@ordering + formatter_clauses).uniq
43
- end
44
- end
45
- end
@@ -1,22 +0,0 @@
1
- require_relative 'chainable'
2
- require_relative 'scopeable'
3
- require_relative 'collectable'
4
-
5
- module ActiveHouse
6
- class Query
7
- attr_reader :model_class
8
-
9
- def initialize(model_class = ActiveHouse::Model)
10
- @model_class = model_class
11
- super()
12
- end
13
-
14
- def connection
15
- model_class.connection
16
- end
17
-
18
- include ActiveHouse::Chainable
19
- include ActiveHouse::Collectable
20
- include ActiveHouse::Scopeable
21
- end
22
- end
@@ -1,21 +0,0 @@
1
- require 'active_support/concern'
2
- require 'active_support/core_ext/module/delegation'
3
-
4
- module ActiveHouse
5
- module Querying
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- class_attribute :_query_class, instance_accessor: false
10
- self._query_class = ActiveHouse::Query
11
- end
12
-
13
- class_methods do
14
- delegate :to_a, :select, :where, :group_by, :limit, :order_by, :having, :from, to: :all
15
-
16
- def all
17
- _query_class.new(self)
18
- end
19
- end
20
- end
21
- end
@@ -1,42 +0,0 @@
1
- require 'active_support/concern'
2
-
3
- module ActiveHouse
4
- module Scopeable
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- private
9
-
10
- def apply_scope(name, *args)
11
- scope = model_class._scopes.fetch(name.to_sym)
12
- instance_exec(*args, &scope)
13
- end
14
-
15
- def scope?(name)
16
- model_class._scopes.key?(name.to_sym)
17
- end
18
-
19
- def apply_default_scope
20
- return if model_class._default_scope.nil?
21
- apply_scope(model_class._default_scope)
22
- end
23
- end
24
-
25
- def initialize(*)
26
- super
27
- with_current_query { apply_default_scope }
28
- end
29
-
30
- def respond_to_missing?(method_name, *_args)
31
- scope?(method_name) || super
32
- end
33
-
34
- def method_missing(method_name, *args, &_block)
35
- if scope?(method_name)
36
- apply_scope(method_name, *args)
37
- else
38
- super
39
- end
40
- end
41
- end
42
- end
@@ -1,37 +0,0 @@
1
- require 'active_support/concern'
2
-
3
- module ActiveHouse
4
- module Scoping
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_default_scope, instance_accessor: false
9
- class_attribute :_scopes, instance_accessor: false
10
- self._scopes = {}
11
- end
12
-
13
- class_methods do
14
- def default_scope(name)
15
- self._default_scope = name.to_sym
16
- end
17
-
18
- def scope(name, block)
19
- self._scopes = _scopes.merge(name.to_sym => block)
20
- end
21
-
22
- def respond_to_missing?(method_name, *_args)
23
- _scopes.key?(method_name.to_sym)
24
- end
25
-
26
- def method_missing(method_name, *args, &_block)
27
- method_name = method_name.to_sym
28
- if _scopes.key?(method_name)
29
- scope = _scopes.fetch(method_name)
30
- all.instance_exec(*args, &scope)
31
- else
32
- super
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,35 +0,0 @@
1
- module ActiveHouse
2
- module Selectable
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- private
7
-
8
- def build_select_query_part
9
- if !@fields.empty?
10
- "SELECT\n#{@fields.join(",\n")}"
11
- else
12
- 'SELECT *'
13
- end
14
- end
15
- end
16
-
17
- def initialize(*)
18
- @fields = []
19
- super
20
- end
21
-
22
- def select(*fields)
23
- raise ArgumentError, 'wrong number of arguments' if fields.empty?
24
- formatted_fields = fields.map do |field|
25
- if field.is_a?(Symbol) && model_class._attribute_opts.key?(field)
26
- opts = model_class._attribute_opts.fetch(field)
27
- opts.key?(:select) ? "#{opts[:select]} AS #{field}" : field.to_s
28
- else
29
- field.to_s
30
- end
31
- end
32
- chain_query fields: (@fields + formatted_fields).uniq
33
- end
34
- end
35
- end
@@ -1,43 +0,0 @@
1
- module ActiveHouse
2
- module Unionable
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- private
7
-
8
- def build_union_query_part
9
- "UNION ALL\n#{@unions.values.map(&:to_query).join("\n")}" unless @unions.values.empty?
10
- end
11
- end
12
-
13
- def initialize(*)
14
- @unions = {}
15
- super
16
- end
17
-
18
- def union(name, query)
19
- query = query.all if query.is_a?(ActiveHouse::Model)
20
- raise ArgumentError, 'argument must be model or query object' unless query.is_a?(ActiveHouse::Query)
21
- new_unions = @unions.map { |n, q| [n, q.dup] }.to_h
22
- new_unions[name] = query.dup
23
- chain_query unions: new_unions
24
- end
25
-
26
- def update_union(name)
27
- raise ArgumentError, "can't find union by name #{name}" unless @unions.key?(name)
28
- new_union = yield union_for(name)
29
- union(name, new_union)
30
- end
31
-
32
- def union_for(name)
33
- raise ArgumentError, "can't find union by name #{name}" unless @unions.key?(name)
34
- @unions[name].dup
35
- end
36
-
37
- def except_union(name)
38
- new_unions = @unions.map { |n, q| [n, q.dup] }.to_h
39
- new_unions.delete(name)
40
- chain_query unions: new_unions
41
- end
42
- end
43
- end
@@ -1,57 +0,0 @@
1
- require_relative 'prepared_statement'
2
- require 'active_support/core_ext/array/wrap'
3
-
4
- module ActiveHouse
5
- module Whereable
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- private
10
-
11
- def format_condition(*conditions)
12
- raise ArgumentError, 'wrong number of arguments' if conditions.empty?
13
- return ActiveHouse::PreparedStatement.prepare_sql(*conditions) if conditions.size > 1
14
- condition = conditions.first
15
- if condition.is_a?(Hash)
16
- condition.map do |field, value|
17
- "#{field} #{sign_for_condition(value)} #{ActiveHouse::PreparedStatement.format_value(value)}"
18
- end
19
- else
20
- condition.to_s
21
- end
22
- end
23
-
24
- def sign_for_condition(value)
25
- if value.is_a?(Array)
26
- 'IN'
27
- elsif value.nil?
28
- 'IS'
29
- else
30
- '='
31
- end
32
- end
33
-
34
- def build_where_query_part
35
- "WHERE\n" + @conditions.join(" AND\n") unless @conditions.empty?
36
- end
37
- end
38
-
39
- def initialize(*)
40
- @conditions = []
41
- super
42
- end
43
-
44
- def where(*conditions)
45
- raise ArgumentError, 'wrong number of arguments' if conditions.empty?
46
- formatted_conditions = Array.wrap(format_condition(*conditions))
47
- chain_query conditions: (@conditions + formatted_conditions).uniq
48
- end
49
-
50
- def where_not(*conditions)
51
- raise ArgumentError, 'wrong number of arguments' if conditions.empty?
52
- formatted_conditions = Array.wrap(format_condition(*conditions))
53
- negative_condition = "NOT (#{formatted_conditions.join(' AND ')})"
54
- chain_query conditions: (@conditions + [negative_condition]).uniq
55
- end
56
- end
57
- end