active_house 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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