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.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/active_house.gemspec +1 -0
- data/lib/active_house.rb +1 -1
- data/lib/active_house/connection.rb +3 -3
- data/lib/active_house/exceptions.rb +9 -0
- data/lib/active_house/{logger.rb → logging.rb} +1 -1
- data/lib/active_house/model.rb +14 -12
- data/lib/active_house/modeling/attributes.rb +102 -0
- data/lib/active_house/modeling/connection.rb +38 -0
- data/lib/active_house/modeling/query.rb +29 -0
- data/lib/active_house/modeling/scope.rb +42 -0
- data/lib/active_house/prepared_statement.rb +34 -2
- data/lib/active_house/query_builder.rb +38 -0
- data/lib/active_house/querying/array_join.rb +42 -0
- data/lib/active_house/querying/collect.rb +80 -0
- data/lib/active_house/querying/from.rb +30 -0
- data/lib/active_house/querying/group_by.rb +30 -0
- data/lib/active_house/querying/having.rb +41 -0
- data/lib/active_house/querying/limit.rb +35 -0
- data/lib/active_house/querying/order_by.rb +58 -0
- data/lib/active_house/querying/scope.rb +45 -0
- data/lib/active_house/querying/select.rb +35 -0
- data/lib/active_house/querying/union.rb +67 -0
- data/lib/active_house/querying/where.rb +50 -0
- data/lib/active_house/version.rb +1 -1
- metadata +34 -22
- data/lib/active_house/array_joinable.rb +0 -31
- data/lib/active_house/chainable.rb +0 -151
- data/lib/active_house/collectable.rb +0 -44
- data/lib/active_house/connecting.rb +0 -36
- data/lib/active_house/connection_error.rb +0 -6
- data/lib/active_house/error.rb +0 -4
- data/lib/active_house/fromable.rb +0 -29
- data/lib/active_house/groupable.rb +0 -23
- data/lib/active_house/havingable.rb +0 -50
- data/lib/active_house/limitable.rb +0 -27
- data/lib/active_house/modeling.rb +0 -100
- data/lib/active_house/orderable.rb +0 -45
- data/lib/active_house/query.rb +0 -22
- data/lib/active_house/querying.rb +0 -21
- data/lib/active_house/scopeable.rb +0 -42
- data/lib/active_house/scoping.rb +0 -37
- data/lib/active_house/selectable.rb +0 -35
- data/lib/active_house/unionable.rb +0 -43
- 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
|