sunstone 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -2
- data/ext/active_record/associations/builder/has_and_belongs_to_many.rb +52 -48
- data/ext/active_record/batches.rb +2 -2
- data/ext/active_record/calculations.rb +28 -28
- data/ext/active_record/finder_methods.rb +36 -22
- data/ext/active_record/query_methods.rb +23 -0
- data/ext/active_record/relation.rb +6 -8
- data/ext/active_record/relation/predicate_builder.rb +29 -23
- data/ext/active_record/statement_cache.rb +21 -8
- data/lib/active_record/connection_adapters/sunstone_adapter.rb +0 -2
- data/lib/arel/visitors/sunstone.rb +9 -5
- data/lib/sunstone.rb +9 -3
- data/lib/sunstone/connection.rb +2 -3
- data/lib/sunstone/version.rb +1 -1
- data/sunstone.gemspec +3 -3
- data/test/models/ship.rb +14 -0
- data/test/query_test.rb +128 -0
- data/test/sunstone_test.rb +296 -305
- data/test/test_helper.rb +11 -25
- metadata +13 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7571ad32bf7197a480c1f59c5a5158ce0f1d492
|
4
|
+
data.tar.gz: d10ec4207fc78c2e6ec8d3dee815469a7b4b868a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 121532495ba520b5e66de7cc054e9fc1958076c6953c2dc10e710816d9944fc76e940a4dd6018cea3a5599aa43faecdee9c81259fd7d569ea659abcbc6ccb4d9
|
7
|
+
data.tar.gz: 815d5f4f6a5c89a1552b8dc8cde45ac8cf508608b5403c75f4516569fd834f2f3becf1fa3af3cf25c622cba2d3ade471dca215029d4aa2230a9b46fc916e730b
|
data/README.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
|
1
|
+
Sunstone
|
2
2
|
========
|
3
3
|
|
4
|
-
|
4
|
+
An [ActiveRecord](https://rubygems.org/gems/activerecord) adapter for quering
|
5
|
+
APIs over Standard API (https://github.com/waratuman/standardapi).
|
6
|
+
|
7
|
+
|
8
|
+
TODO:
|
9
|
+
=====
|
10
|
+
Make `cookie_store` and optional
|
11
|
+
stream building model instances with wankel
|
@@ -1,48 +1,52 @@
|
|
1
|
-
module ActiveRecord::Associations::Builder
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
1
|
+
# module ActiveRecord::Associations::Builder
|
2
|
+
# class HasAndBelongsToMany # :nodoc:
|
3
|
+
#
|
4
|
+
# def through_model
|
5
|
+
# habtm = JoinTableResolver.build lhs_model, association_name, options
|
6
|
+
#
|
7
|
+
# join_model = Class.new(lhs_model.base_class.superclass) { # Don't use ActiveRecord::Base here because that might not be the abstract class that we need and the connection may be different
|
8
|
+
# class << self;
|
9
|
+
# attr_accessor :left_model
|
10
|
+
# attr_accessor :name
|
11
|
+
# attr_accessor :table_name_resolver
|
12
|
+
# attr_accessor :left_reflection
|
13
|
+
# attr_accessor :right_reflection
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def self.table_name
|
17
|
+
# table_name_resolver.join_table
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def self.compute_type(class_name)
|
21
|
+
# left_model.compute_type class_name
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def self.add_left_association(name, options)
|
25
|
+
# belongs_to name, options
|
26
|
+
# self.left_reflection = _reflect_on_association(name)
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def self.add_right_association(name, options)
|
30
|
+
# rhs_name = name.to_s.singularize.to_sym
|
31
|
+
# belongs_to rhs_name, options
|
32
|
+
# self.right_reflection = _reflect_on_association(rhs_name)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def self.retrieve_connection
|
36
|
+
# left_model.retrieve_connection
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# }
|
40
|
+
#
|
41
|
+
# join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
42
|
+
# join_model.table_name_resolver = habtm
|
43
|
+
# join_model.class_resolver = lhs_model
|
44
|
+
# join_model.primary_key = nil
|
45
|
+
#
|
46
|
+
# join_model.add_left_association :left_side, anonymous_class: lhs_model
|
47
|
+
# join_model.add_right_association association_name, belongs_to_options(options)
|
48
|
+
# join_model
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# end
|
52
|
+
# end
|
@@ -1,28 +1,28 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
1
|
+
# module ActiveRecord
|
2
|
+
# module Calculations
|
3
|
+
# def pluck(*column_names)
|
4
|
+
#
|
5
|
+
# # column_names.map! do |column_name|
|
6
|
+
# # if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
7
|
+
# # attribute_alias(column_name)
|
8
|
+
# # else
|
9
|
+
# # column_name.to_s
|
10
|
+
# # end
|
11
|
+
# # end
|
12
|
+
#
|
13
|
+
# if has_include?(column_names.first)
|
14
|
+
# construct_relation_for_association_calculations.pluck(*column_names)
|
15
|
+
# else
|
16
|
+
# relation = spawn
|
17
|
+
# relation.select_values = column_names.map { |cn|
|
18
|
+
# columns_hash.key?(cn) ? arel_table[cn] : cn
|
19
|
+
# }
|
20
|
+
#
|
21
|
+
# result = klass.connection.exec_query(relation.arel, nil, relation.arel.bind_values + bind_values)
|
22
|
+
# result.cast_values(klass.column_types)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
@@ -1,22 +1,36 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module FinderMethods
|
3
|
-
|
4
|
-
def find_with_associations
|
5
|
-
arel.eager_load = Arel::Nodes::EagerLoad.new(eager_load_values)
|
6
3
|
|
4
|
+
def find_with_associations
|
5
|
+
join_dependency = nil
|
6
|
+
aliases = nil
|
7
|
+
relation = if connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
8
|
+
arel.eager_load = Arel::Nodes::EagerLoad.new(eager_load_values)
|
9
|
+
self
|
10
|
+
else
|
11
|
+
join_dependency = construct_join_dependency(joins_values)
|
12
|
+
aliases = join_dependency.aliases
|
13
|
+
apply_join_dependency(select(aliases.columns), join_dependency)
|
14
|
+
end
|
15
|
+
|
7
16
|
if block_given?
|
8
|
-
yield
|
17
|
+
yield relation
|
9
18
|
else
|
10
|
-
if ActiveRecord::NullRelation ===
|
19
|
+
if ActiveRecord::NullRelation === relation
|
11
20
|
[]
|
12
21
|
else
|
13
|
-
|
14
|
-
|
15
|
-
|
22
|
+
arel = relation.arel
|
23
|
+
rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
|
24
|
+
if join_dependency
|
25
|
+
join_dependency.instantiate(rows, aliases)
|
26
|
+
else
|
27
|
+
instantiate_with_associations(rows, relation)
|
28
|
+
end
|
29
|
+
|
16
30
|
end
|
17
31
|
end
|
18
32
|
end
|
19
|
-
|
33
|
+
|
20
34
|
def instantiate_with_associations(result_set, klass)
|
21
35
|
seen = Hash.new { |h, parent_klass|
|
22
36
|
h[parent_klass] = Hash.new { |i, parent_id|
|
@@ -26,20 +40,20 @@ module ActiveRecord
|
|
26
40
|
|
27
41
|
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
28
42
|
parents = model_cache[self.base_class]
|
29
|
-
|
43
|
+
|
30
44
|
result_set.each { |row_hash|
|
31
45
|
parent = parents[row_hash[primary_key]] ||= instantiate(row_hash.select{|k,v| column_names.include?(k.to_s) })
|
32
46
|
construct(parent, row_hash.select{|k,v| !column_names.include?(k.to_s) }, seen, model_cache)
|
33
47
|
}
|
34
|
-
|
48
|
+
|
35
49
|
parents.values
|
36
50
|
end
|
37
|
-
|
51
|
+
|
38
52
|
def construct(parent, relations, seen, model_cache)
|
39
53
|
relations.each do |key, attributes|
|
40
54
|
reflection = parent.class.reflect_on_association(key)
|
41
55
|
next unless reflection
|
42
|
-
|
56
|
+
|
43
57
|
if reflection.collection?
|
44
58
|
other = parent.association(reflection.name)
|
45
59
|
other.loaded!
|
@@ -49,7 +63,7 @@ module ActiveRecord
|
|
49
63
|
construct(model, attributes.select{|k,v| !reflection.klass.column_names.include?(k.to_s) }, seen, model_cache)
|
50
64
|
end
|
51
65
|
end
|
52
|
-
|
66
|
+
|
53
67
|
if !reflection.collection?
|
54
68
|
construct_association(parent, reflection, attributes, seen, model_cache)
|
55
69
|
else
|
@@ -57,13 +71,13 @@ module ActiveRecord
|
|
57
71
|
construct_association(parent, reflection, row, seen, model_cache)
|
58
72
|
end
|
59
73
|
end
|
60
|
-
|
74
|
+
|
61
75
|
end
|
62
76
|
end
|
63
|
-
|
77
|
+
|
64
78
|
def construct_association(parent, reflection, attributes, seen, model_cache)
|
65
79
|
return if attributes.nil?
|
66
|
-
|
80
|
+
|
67
81
|
klass = if reflection.polymorphic?
|
68
82
|
parent.send(reflection.foreign_type).constantize.base_class
|
69
83
|
else
|
@@ -71,12 +85,12 @@ module ActiveRecord
|
|
71
85
|
end
|
72
86
|
id = attributes[klass.primary_key]
|
73
87
|
model = seen[parent.class.base_class][parent.id][klass][id]
|
74
|
-
|
88
|
+
|
75
89
|
if model
|
76
90
|
construct(model, attributes.select{|k,v| !klass.column_names.include?(k.to_s) }, seen, model_cache)
|
77
91
|
|
78
92
|
other = parent.association(reflection.name)
|
79
|
-
|
93
|
+
|
80
94
|
if reflection.collection?
|
81
95
|
other.target.push(model)
|
82
96
|
else
|
@@ -90,7 +104,7 @@ module ActiveRecord
|
|
90
104
|
construct(model, attributes.select{|k,v| !klass.column_names.include?(k.to_s) }, seen, model_cache)
|
91
105
|
end
|
92
106
|
end
|
93
|
-
|
107
|
+
|
94
108
|
|
95
109
|
def construct_model(record, reflection, id, attributes, seen, model_cache)
|
96
110
|
klass = if reflection.polymorphic?
|
@@ -98,7 +112,7 @@ module ActiveRecord
|
|
98
112
|
else
|
99
113
|
reflection.klass
|
100
114
|
end
|
101
|
-
|
115
|
+
|
102
116
|
model = model_cache[klass][id] ||= klass.instantiate(attributes)
|
103
117
|
other = record.association(reflection.name)
|
104
118
|
|
@@ -111,7 +125,7 @@ module ActiveRecord
|
|
111
125
|
other.set_inverse_instance(model)
|
112
126
|
model
|
113
127
|
end
|
114
|
-
|
128
|
+
|
115
129
|
end
|
116
130
|
|
117
131
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module QueryMethods
|
3
|
+
|
4
|
+
def reverse_sql_order(order_query)
|
5
|
+
order_query = [arel_table[primary_key].asc] if order_query.empty?
|
6
|
+
|
7
|
+
order_query.flat_map do |o|
|
8
|
+
case o
|
9
|
+
when Arel::Nodes::Ordering
|
10
|
+
o.reverse
|
11
|
+
when String
|
12
|
+
o.to_s.split(',').map! do |s|
|
13
|
+
s.strip!
|
14
|
+
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
15
|
+
end
|
16
|
+
else
|
17
|
+
o
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class Relation
|
3
|
+
|
3
4
|
def to_sql
|
4
5
|
@to_sql ||= begin
|
5
6
|
relation = self
|
6
7
|
connection = klass.connection
|
7
|
-
visitor = connection.visitor
|
8
|
-
collector = connection.collector
|
8
|
+
visitor = connection.visitor.is_a?(Arel::Visitors::Sunstone) ? Arel::Visitors::ToSql.new(connection) : connection.visitor
|
9
9
|
|
10
10
|
if eager_loading?
|
11
11
|
find_with_associations { |rel| relation = rel }
|
@@ -14,13 +14,11 @@ module ActiveRecord
|
|
14
14
|
arel = relation.arel
|
15
15
|
binds = (arel.bind_values + relation.bind_values).dup
|
16
16
|
binds.map! { |bv| connection.quote(*bv.reverse) }
|
17
|
-
collect = visitor.accept(arel.ast,
|
18
|
-
|
19
|
-
collect.compile(binds)
|
20
|
-
else
|
21
|
-
collect.substitute_binds(binds).join
|
22
|
-
end
|
17
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
18
|
+
collect.substitute_binds(binds).join
|
23
19
|
end
|
24
20
|
end
|
21
|
+
|
22
|
+
|
25
23
|
end
|
26
24
|
end
|
@@ -1,23 +1,29 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
1
|
+
# module ActiveRecord
|
2
|
+
# class PredicateBuilder # :nodoc:
|
3
|
+
#
|
4
|
+
# def self.expand(klass, table, column, value)
|
5
|
+
# queries = []
|
6
|
+
#
|
7
|
+
# # In standard Rails where takes :table => { columns }, but in sunstone we
|
8
|
+
# # can can do nested tables eg: where(:properties => { :regions => {:id => 1}})
|
9
|
+
# if klass && reflection = klass._reflect_on_association(column)
|
10
|
+
# base_class = polymorphic_base_class_from_value(value)
|
11
|
+
#
|
12
|
+
# if reflection.polymorphic? && base_class
|
13
|
+
# queries << build(table[reflection.foreign_type], base_class)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# column = reflection.foreign_key
|
17
|
+
#
|
18
|
+
# if base_class
|
19
|
+
# primary_key = reflection.association_primary_key(base_class)
|
20
|
+
# value = convert_value_to_association_ids(value, primary_key)
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# queries << build(table[column], value)
|
25
|
+
# queries
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# end
|
29
|
+
# end
|
@@ -1,23 +1,36 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class StatementCache
|
3
3
|
class PartialQuery
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
|
5
|
+
def initialize(values, sunstone=false)
|
6
|
+
@values = values
|
7
|
+
if sunstone
|
8
|
+
@indexes = values.value.find_all { |thing|
|
9
|
+
Arel::Nodes::BindParam === thing
|
10
|
+
}
|
11
|
+
else
|
12
|
+
@indexes = values.each_with_index.find_all { |thing,i|
|
13
|
+
Arel::Nodes::BindParam === thing
|
14
|
+
}.map(&:last)
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
def sql_for(binds, connection)
|
13
19
|
binds = binds.dup
|
14
|
-
|
20
|
+
|
21
|
+
if connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
22
|
+
@values.compile(binds)
|
23
|
+
else
|
24
|
+
val = @values.dup
|
25
|
+
@indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
|
26
|
+
val.join
|
27
|
+
end
|
15
28
|
end
|
16
29
|
end
|
17
30
|
|
18
31
|
def self.partial_query(visitor, ast, collector)
|
19
32
|
collected = visitor.accept(ast, collector)
|
20
|
-
PartialQuery.new collected
|
33
|
+
PartialQuery.new(visitor.is_a?(Arel::Visitors::Sunstone) ? collected : collected.value, visitor.is_a?(Arel::Visitors::Sunstone))
|
21
34
|
end
|
22
35
|
|
23
36
|
end
|