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
@@ -0,0 +1,80 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/string/filters'
3
+ require 'active_support/core_ext/module/delegation'
4
+
5
+ module ActiveHouse
6
+ module Querying
7
+ module Collect
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ private :collection, :fetch_collection, :query_parts, :build_query
12
+ instance_delegate [
13
+ :each,
14
+ :size,
15
+ :count,
16
+ :map,
17
+ :collect,
18
+ :detect,
19
+ :filter,
20
+ :reject,
21
+ :inject,
22
+ :reduce,
23
+ :all?,
24
+ :any?
25
+ ] => :to_a
26
+ end
27
+
28
+ def initialize(*)
29
+ @collection = nil
30
+ super
31
+ end
32
+
33
+ def to_a
34
+ collection
35
+ end
36
+
37
+ def reset
38
+ @collection = nil
39
+ end
40
+
41
+ def loaded?
42
+ !@collection.nil?
43
+ end
44
+
45
+ def to_hashes
46
+ connection.select_rows(build_query.squish)
47
+ end
48
+
49
+ def to_query
50
+ build_query
51
+ end
52
+
53
+ def collection
54
+ @collection ||= fetch_collection
55
+ end
56
+
57
+ def fetch_collection
58
+ to_hashes.map { |row| model_class.load!(row) }
59
+ end
60
+
61
+ def query_parts
62
+ [
63
+ build_select_query_part,
64
+ build_from_query_part,
65
+ build_array_join_query_part,
66
+ build_where_query_part,
67
+ build_group_by_query_part,
68
+ build_having_query_part,
69
+ build_order_by_query_part,
70
+ build_limit_query_part,
71
+ build_union_query_part
72
+ ]
73
+ end
74
+
75
+ def build_query
76
+ query_parts.reject(&:nil?).join("\n")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveHouse
2
+ module Querying
3
+ module From
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ private :build_from_query_part, :from_subquery
8
+ end
9
+
10
+ def from_subquery
11
+ return model_class._table_name if values[:subquery].nil?
12
+ query = values[:subquery].is_a?(ActiveHouse::QueryBuilder) ? values[:subquery].to_query : values[:subquery].to_s
13
+ "( #{query} )"
14
+ end
15
+
16
+ def build_from_query_part
17
+ "FROM #{from_subquery}"
18
+ end
19
+
20
+ def from!(table_or_subquery)
21
+ values[:subquery] = table_or_subquery.dup
22
+ self
23
+ end
24
+
25
+ def from(table_or_subquery)
26
+ dup.from!(table_or_subquery)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveHouse
2
+ module Querying
3
+ module GroupBy
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ private :build_group_by_query_part
8
+ end
9
+
10
+ def build_group_by_query_part
11
+ "GROUP BY #{values[:group_by].join(', ')}" unless values[:group_by].empty?
12
+ end
13
+
14
+ def initial_values
15
+ super.merge group_by: []
16
+ end
17
+
18
+ def group_by!(*fields)
19
+ raise ArgumentError, 'wrong number of arguments' if fields.empty?
20
+ formatted_fields = fields.map(&:to_s)
21
+ values[:group_by] = (values[:group_by] + formatted_fields).uniq
22
+ self
23
+ end
24
+
25
+ def group_by(*fields)
26
+ dup.group_by!(*fields)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../prepared_statement'
2
+ require 'active_support/core_ext/array/wrap'
3
+
4
+ module ActiveHouse
5
+ module Querying
6
+ module Having
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ private :build_having_query_part, :format_having
11
+ end
12
+
13
+ def build_having_query_part
14
+ "HAVING\n" + values[:having].join(" AND\n") unless values[:having].empty?
15
+ end
16
+
17
+ def initial_values
18
+ super.merge having: []
19
+ end
20
+
21
+ def having!(*conditions)
22
+ formatted_conditions = format_having(conditions)
23
+ values[:having] = (values[:having] + formatted_conditions).uniq
24
+ self
25
+ end
26
+
27
+ def having(*conditions)
28
+ dup.having!(*conditions)
29
+ end
30
+
31
+ def format_having(conditions)
32
+ raise ArgumentError, 'wrong number of arguments' if conditions.empty?
33
+ raise ArgumentError, 'wrong number of arguments' if conditions.empty?
34
+
35
+ return [ActiveHouse::PreparedStatement.prepare_sql(*conditions)] if conditions.size > 1
36
+
37
+ ActiveHouse::PreparedStatement.build_condition(conditions.first)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveHouse
2
+ module Querying
3
+ module Limit
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ private :build_limit_query_part
8
+ end
9
+
10
+ def build_limit_query_part
11
+ return if values[:limit].nil?
12
+ if values[:offset]
13
+ "LIMIT #{values[:limit]}, #{values[:offset]}"
14
+ else
15
+ "LIMIT #{values[:limit]}"
16
+ end
17
+ end
18
+
19
+ def initial_values
20
+ super.merge offset: nil, limit: nil
21
+ super
22
+ end
23
+
24
+ def limit!(limit_value, offset_value = nil)
25
+ values[:limit] = limit_value
26
+ values[:offset] = offset_value unless offset_value.nil?
27
+ self
28
+ end
29
+
30
+ def limit(limit_value, offset_value = nil)
31
+ dup.limit!(limit_value, offset_value)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,58 @@
1
+ require 'active_support/core_ext/object/try'
2
+
3
+ module ActiveHouse
4
+ module Querying
5
+ module OrderBy
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ private :build_order_by_query_part
10
+ end
11
+
12
+ def build_order_by_query_part
13
+ "ORDER BY #{values[:order_by].join(', ')}" unless values[:order_by].empty?
14
+ end
15
+
16
+ def initial_values
17
+ super.merge order_by: []
18
+ end
19
+
20
+ def order_by!(*clauses)
21
+ formatter_clauses = format_order_clauses(clauses)
22
+ values[:order_by] = (values[:order_by] + formatter_clauses).uniq
23
+ self
24
+ end
25
+
26
+ def order_by(*clauses)
27
+ dup.order_by!(*clauses)
28
+ end
29
+
30
+ def format_order_clauses(clauses)
31
+ raise ArgumentError, 'wrong number of arguments' if clauses.empty?
32
+
33
+ clauses.map do |clause|
34
+ if clause.is_a?(Hash)
35
+ format_order_hash(clause)
36
+ else
37
+ clause.to_s
38
+ end
39
+ end
40
+ end
41
+
42
+ def format_order_hash(clause)
43
+ if clause.keys.one?
44
+ direction = clause.values.first
45
+ raise ArgumentError, 'direction must be asc or desc' unless [:asc, :desc].include?(direction.try!(:to_sym))
46
+ "#{clause.keys.first} #{direction.to_s.upcase}"
47
+ else
48
+ clause.assert_valid_keys(:field, :direction, :collate)
49
+ [
50
+ clause.fetch(:field),
51
+ clause[:direction].try!(:to_s).try!(:upcase),
52
+ clause.key?(:collate) ? "COLLATE '#{clause[:collate]}'" : nil
53
+ ].reject(&:nil?).join(' ')
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,45 @@
1
+ require 'active_support/concern'
2
+
3
+ module ActiveHouse
4
+ module Querying
5
+ module Scope
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ private :apply_scope, :scope?
10
+ end
11
+
12
+ class_methods do
13
+ def new(*)
14
+ super._apply_default_scope
15
+ end
16
+ end
17
+
18
+ def _apply_default_scope
19
+ return self if model_class._default_scope.nil?
20
+ apply_scope(model_class._default_scope)
21
+ end
22
+
23
+ def respond_to_missing?(method_name, *_args)
24
+ scope?(method_name) || super
25
+ end
26
+
27
+ def method_missing(method_name, *args, &_block)
28
+ if scope?(method_name)
29
+ apply_scope(method_name, *args)
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def apply_scope(name, *args)
36
+ scope = model_class._scopes.fetch(name)
37
+ instance_exec(*args, &scope)
38
+ end
39
+
40
+ def scope?(name)
41
+ model_class.scope?(name.to_sym)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../prepared_statement'
2
+
3
+ module ActiveHouse
4
+ module Querying
5
+ module Select
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ private :build_select_query_part
10
+ end
11
+
12
+ def build_select_query_part
13
+ if !values[:fields].empty?
14
+ "SELECT\n#{values[:fields].join(",\n")}"
15
+ else
16
+ 'SELECT *'
17
+ end
18
+ end
19
+
20
+ def initial_values
21
+ super.merge fields: []
22
+ end
23
+
24
+ def select(*fields)
25
+ dup.select!(*fields)
26
+ end
27
+
28
+ def select!(*fields)
29
+ formatted_fields = ActiveHouse::PreparedStatement.format_fields(model_class, fields)
30
+ values[:fields] = (values[:fields] + formatted_fields).uniq
31
+ self
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,67 @@
1
+ module ActiveHouse
2
+ module Querying
3
+ module Union
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ private :build_union_query_part, :format_unions
8
+ end
9
+
10
+ def build_union_query_part
11
+ return if values[:union].values.empty?
12
+
13
+ "UNION ALL\n#{values[:union].values.map(&:to_query).join("\n")}"
14
+ end
15
+
16
+ def initial_values
17
+ super.merge union: {}
18
+ end
19
+
20
+ # @param queries [Hash] - hash where key is union name and value is a query
21
+ # key needed for possibility to update/replace union query
22
+ def union!(queries)
23
+ formatted_queries = format_unions(queries)
24
+ values[:union] = formatted_queries
25
+ self
26
+ end
27
+
28
+ # @param queries [Hash] - hash where key is union name and value is a query
29
+ # key needed for possibility to update/replace union query
30
+ def union(queries)
31
+ dup.union!(queries)
32
+ end
33
+
34
+ def update_union(name)
35
+ name = name.to_sym
36
+ raise ArgumentError, "can't find union by name #{name}" unless values[:union].key?(name)
37
+ new_union = yield values[:union][name.to_sym]
38
+ union(name.to_sym => new_union)
39
+ end
40
+
41
+ def except_union!(name)
42
+ new_unions = values[:union].map { |n, q| [n, q.dup] }.to_h
43
+ new_unions.delete(name.to_sym)
44
+ values[:union] = new_unions
45
+ self
46
+ end
47
+
48
+ def except_union(name)
49
+ dup.except_union!(name)
50
+ end
51
+
52
+ def format_unions(queries)
53
+ raise ArgumentError, 'unions must be a Hash' unless queries.is_a?(Hash)
54
+ raise ArgumentError, 'unions hash is empty' if queries.empty?
55
+
56
+ new_unions = values[:union].map { |n, q| [n, q.dup] }.to_h
57
+
58
+ queries.each do |name, query|
59
+ query = query.all if query.is_a?(ActiveHouse::Model)
60
+ new_unions[name.to_sym] = query.dup
61
+ end
62
+
63
+ new_unions
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,50 @@
1
+ require_relative '../prepared_statement'
2
+
3
+ module ActiveHouse
4
+ module Querying
5
+ module Where
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ private :build_where_query_part
10
+ end
11
+
12
+ def initial_values
13
+ super.merge where: []
14
+ end
15
+
16
+ def build_where_query_part
17
+ "WHERE\n" + values[:where].join(" AND\n") unless values[:where].empty?
18
+ end
19
+
20
+ def where!(*conditions)
21
+ formatted_conditions = format_where_clauses(conditions)
22
+ values[:where] = (values[:where] + formatted_conditions).uniq
23
+ self
24
+ end
25
+
26
+ def where_not!(*conditions)
27
+ formatted_conditions = format_where_clauses(conditions)
28
+ negative_condition = "NOT (#{formatted_conditions.join(' AND ')})"
29
+ values[:where] = (values[:where] + [negative_condition]).uniq
30
+ self
31
+ end
32
+
33
+ def where(*conditions)
34
+ dup.where!(*conditions)
35
+ end
36
+
37
+ def where_not(*conditions)
38
+ dup.where_not!(*conditions)
39
+ end
40
+
41
+ def format_where_clauses(conditions)
42
+ raise ArgumentError, 'wrong number of arguments' if conditions.empty?
43
+
44
+ return [ActiveHouse::PreparedStatement.prepare_sql(*conditions)] if conditions.size > 1
45
+
46
+ ActiveHouse::PreparedStatement.build_condition(conditions.first)
47
+ end
48
+ end
49
+ end
50
+ end