spyke 1.5.0 → 1.6.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/lib/spyke/associations/association.rb +22 -10
- data/lib/spyke/associations/has_many.rb +25 -17
- data/lib/spyke/attribute_assignment.rb +111 -0
- data/lib/spyke/attributes.rb +6 -112
- data/lib/spyke/base.rb +5 -4
- data/lib/spyke/http.rb +0 -3
- data/lib/spyke/orm.rb +2 -2
- data/lib/spyke/relation.rb +6 -8
- data/lib/spyke/{scopes.rb → scoping.rb} +9 -5
- data/lib/spyke/version.rb +1 -1
- data/test/associations_test.rb +54 -4
- data/test/attributes_test.rb +6 -0
- data/test/relation_test.rb +26 -0
- data/test/support/fixtures.rb +26 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8c05691c5a62ac3b717844d0c3f87890ab7c902
|
4
|
+
data.tar.gz: 7edc244c8975b0ec3c5f1887e53866fafb8dde04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05a0f43e35a3411bfab96a7825ad75a9721d3177fc90d1c6b26a29c9d68e43e5476448c14bbba8972ac4d7458efd64a89a04acac929340551d0378f64717b5cb
|
7
|
+
data.tar.gz: e1a53e75a003f0a7dba3c0ecf5151a7b40477b97ab5cc85ddce8e1535ca875ba79c00a04ab679dd3d1904d70049ddf29163a9c2cac6c22ddbd6fffd21ca8a991
|
@@ -15,8 +15,19 @@ module Spyke
|
|
15
15
|
find_one # Override for plural associations that return an association object
|
16
16
|
end
|
17
17
|
|
18
|
+
def find_one
|
19
|
+
result = super
|
20
|
+
update_parent(result) if result
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_some
|
24
|
+
result = super
|
25
|
+
update_parent(result) if result.any?
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
18
29
|
def assign_nested_attributes(attributes)
|
19
|
-
|
30
|
+
update_parent new(attributes)
|
20
31
|
end
|
21
32
|
|
22
33
|
def create(attributes = {})
|
@@ -27,15 +38,12 @@ module Spyke
|
|
27
38
|
add_to_parent super
|
28
39
|
end
|
29
40
|
|
30
|
-
|
31
|
-
new(*args)
|
32
|
-
end
|
41
|
+
alias :build :new
|
33
42
|
|
34
43
|
private
|
35
44
|
|
36
45
|
def add_to_parent(record)
|
37
|
-
|
38
|
-
record
|
46
|
+
update_parent record
|
39
47
|
end
|
40
48
|
|
41
49
|
def foreign_key
|
@@ -47,15 +55,19 @@ module Spyke
|
|
47
55
|
end
|
48
56
|
|
49
57
|
def fetch_embedded
|
50
|
-
if
|
51
|
-
Result.new(data:
|
58
|
+
if embedded_params
|
59
|
+
Result.new(data: embedded_params)
|
52
60
|
elsif !uri
|
53
61
|
Result.new(data: nil)
|
54
62
|
end
|
55
63
|
end
|
56
64
|
|
57
|
-
def
|
58
|
-
parent.attributes[name]
|
65
|
+
def embedded_params
|
66
|
+
@embedded_params ||= parent.attributes.to_params[name]
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_parent(value)
|
70
|
+
parent.attributes[name] = value
|
59
71
|
end
|
60
72
|
end
|
61
73
|
end
|
@@ -11,36 +11,44 @@ module Spyke
|
|
11
11
|
self
|
12
12
|
end
|
13
13
|
|
14
|
-
def assign_nested_attributes(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
existing.merge!(attributes)
|
21
|
-
else
|
22
|
-
build(attributes)
|
23
|
-
end
|
14
|
+
def assign_nested_attributes(incoming)
|
15
|
+
incoming = incoming.values if incoming.is_a?(Hash)
|
16
|
+
combined_attributes = combine_with_existing(incoming)
|
17
|
+
clear_existing!
|
18
|
+
combined_attributes.each do |attributes|
|
19
|
+
build(attributes)
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
27
23
|
private
|
28
24
|
|
29
|
-
def
|
30
|
-
|
25
|
+
def combine_with_existing(incoming)
|
26
|
+
return incoming unless primary_keys_present_in_existing?
|
27
|
+
combined = embedded_params + incoming
|
28
|
+
group_by_primary_key(combined).flat_map do |primary_key, hashes|
|
29
|
+
if primary_key.present?
|
30
|
+
hashes.reduce(:merge)
|
31
|
+
else
|
32
|
+
hashes
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def group_by_primary_key(array)
|
38
|
+
array.group_by { |h| h.with_indifferent_access[:id].to_s }
|
31
39
|
end
|
32
40
|
|
33
|
-
def
|
34
|
-
|
41
|
+
def primary_keys_present_in_existing?
|
42
|
+
embedded_params && embedded_params.any? { |attr| attr.has_key?('id') }
|
35
43
|
end
|
36
44
|
|
37
|
-
def
|
38
|
-
|
45
|
+
def clear_existing!
|
46
|
+
update_parent []
|
39
47
|
end
|
40
48
|
|
41
49
|
def add_to_parent(record)
|
42
50
|
parent.attributes[name] ||= []
|
43
|
-
parent.attributes[name] << record
|
51
|
+
parent.attributes[name] << record
|
44
52
|
record
|
45
53
|
end
|
46
54
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spyke/collection'
|
2
|
+
require 'spyke/attributes'
|
3
|
+
|
4
|
+
module Spyke
|
5
|
+
module AttributeAssignment
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_reader :attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def attributes(*args)
|
14
|
+
args.each do |attr|
|
15
|
+
define_method attr do
|
16
|
+
attribute(attr)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(attributes = {})
|
23
|
+
self.attributes = attributes
|
24
|
+
@uri_template = scope.uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def attributes=(new_attributes)
|
28
|
+
@attributes ||= Attributes.new(scope.params)
|
29
|
+
use_setters(new_attributes) if new_attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def id
|
33
|
+
attributes[:id]
|
34
|
+
end
|
35
|
+
|
36
|
+
def id=(value)
|
37
|
+
attributes[:id] = value if value.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
other.is_a?(Spyke::Base) && id == other.id
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
"#<#{self.class}(#{uri}) id: #{id.inspect} #{inspect_attributes}>"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def use_setters(attributes)
|
51
|
+
attributes.each do |key, value|
|
52
|
+
send "#{key}=", value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(name, *args, &block)
|
57
|
+
case
|
58
|
+
when association?(name) then association(name).load
|
59
|
+
when attribute?(name) then attribute(name)
|
60
|
+
when predicate?(name) then predicate(name)
|
61
|
+
when setter?(name) then set_attribute(name, args.first)
|
62
|
+
else super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def respond_to_missing?(name, include_private = false)
|
67
|
+
association?(name) || attribute?(name) || predicate?(name) || super
|
68
|
+
end
|
69
|
+
|
70
|
+
def association?(name)
|
71
|
+
associations.has_key?(name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def association(name)
|
75
|
+
options = associations[name]
|
76
|
+
options[:type].new(self, name, options)
|
77
|
+
end
|
78
|
+
|
79
|
+
def attribute?(name)
|
80
|
+
attributes.has_key?(name)
|
81
|
+
end
|
82
|
+
|
83
|
+
def attribute(name)
|
84
|
+
attributes[name]
|
85
|
+
end
|
86
|
+
|
87
|
+
def predicate?(name)
|
88
|
+
name.to_s.end_with?('?')
|
89
|
+
end
|
90
|
+
|
91
|
+
def predicate(name)
|
92
|
+
!!attribute(depredicate(name))
|
93
|
+
end
|
94
|
+
|
95
|
+
def depredicate(name)
|
96
|
+
name.to_s.chomp('?').to_sym
|
97
|
+
end
|
98
|
+
|
99
|
+
def setter?(name)
|
100
|
+
name.to_s.end_with?('=')
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_attribute(name, value)
|
104
|
+
attributes[name.to_s.chomp('=')] = value
|
105
|
+
end
|
106
|
+
|
107
|
+
def inspect_attributes
|
108
|
+
attributes.except(:id).map { |k, v| "#{k}: #{v.inspect}" }.join(' ')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/spyke/attributes.rb
CHANGED
@@ -1,126 +1,20 @@
|
|
1
|
-
require 'spyke/collection'
|
2
|
-
|
3
1
|
module Spyke
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
def attributes(*args)
|
13
|
-
args.each do |attr|
|
14
|
-
define_method attr do
|
15
|
-
attribute(attr)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(attributes = {})
|
22
|
-
self.attributes = attributes
|
23
|
-
@uri_template = current_scope.uri
|
24
|
-
end
|
25
|
-
|
26
|
-
def attributes=(new_attributes)
|
27
|
-
@attributes ||= current_scope.params.with_indifferent_access
|
28
|
-
use_setters parse(new_attributes) if new_attributes
|
29
|
-
end
|
30
|
-
|
31
|
-
def id
|
32
|
-
attributes[:id]
|
33
|
-
end
|
34
|
-
|
35
|
-
def id=(value)
|
36
|
-
attributes[:id] = value if value.present?
|
37
|
-
end
|
38
|
-
|
39
|
-
def ==(other)
|
40
|
-
other.is_a?(Spyke::Base) && id == other.id
|
41
|
-
end
|
42
|
-
|
43
|
-
def inspect
|
44
|
-
"#<#{self.class}(#{uri}) id: #{id.inspect} #{inspect_attributes}>"
|
2
|
+
class Attributes < HashWithIndifferentAccess
|
3
|
+
def to_params
|
4
|
+
each_with_object({}) do |(key, value), parameters|
|
5
|
+
parameters[key] = parse_value(value)
|
6
|
+
end.with_indifferent_access
|
45
7
|
end
|
46
8
|
|
47
9
|
private
|
48
10
|
|
49
|
-
def parse(attributes)
|
50
|
-
attributes.each_with_object({}) do |(key, value), parameters|
|
51
|
-
parameters[key] = parse_value(value)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
11
|
def parse_value(value)
|
56
12
|
case
|
57
|
-
when value.is_a?(Spyke::Base) then
|
58
|
-
when value.is_a?(Hash) then parse(value)
|
13
|
+
when value.is_a?(Spyke::Base) then value.attributes.to_params
|
59
14
|
when value.is_a?(Array) then value.map { |v| parse_value(v) }
|
60
15
|
when value.respond_to?(:content_type) then Faraday::UploadIO.new(value.path, value.content_type)
|
61
16
|
else value
|
62
17
|
end
|
63
18
|
end
|
64
|
-
|
65
|
-
def use_setters(attributes)
|
66
|
-
attributes.each do |key, value|
|
67
|
-
send "#{key}=", value
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def method_missing(name, *args, &block)
|
72
|
-
case
|
73
|
-
when association?(name) then association(name).load
|
74
|
-
when attribute?(name) then attribute(name)
|
75
|
-
when predicate?(name) then predicate(name)
|
76
|
-
when setter?(name) then set_attribute(name, args.first)
|
77
|
-
else super
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def respond_to_missing?(name, include_private = false)
|
82
|
-
association?(name) || attribute?(name) || predicate?(name) || super
|
83
|
-
end
|
84
|
-
|
85
|
-
def association?(name)
|
86
|
-
associations.has_key?(name)
|
87
|
-
end
|
88
|
-
|
89
|
-
def association(name)
|
90
|
-
options = associations[name]
|
91
|
-
options[:type].new(self, name, options)
|
92
|
-
end
|
93
|
-
|
94
|
-
def attribute?(name)
|
95
|
-
attributes.has_key?(name)
|
96
|
-
end
|
97
|
-
|
98
|
-
def attribute(name)
|
99
|
-
attributes[name]
|
100
|
-
end
|
101
|
-
|
102
|
-
def predicate?(name)
|
103
|
-
name.to_s.end_with?('?')
|
104
|
-
end
|
105
|
-
|
106
|
-
def predicate(name)
|
107
|
-
!!attribute(depredicate(name))
|
108
|
-
end
|
109
|
-
|
110
|
-
def depredicate(name)
|
111
|
-
name.to_s.chomp('?').to_sym
|
112
|
-
end
|
113
|
-
|
114
|
-
def setter?(name)
|
115
|
-
name.to_s.end_with?('=')
|
116
|
-
end
|
117
|
-
|
118
|
-
def set_attribute(name, value)
|
119
|
-
attributes[name.to_s.chomp('=')] = value
|
120
|
-
end
|
121
|
-
|
122
|
-
def inspect_attributes
|
123
|
-
attributes.except(:id).map { |k, v| "#{k}: #{v.inspect}" }.join(' ')
|
124
|
-
end
|
125
19
|
end
|
126
20
|
end
|
data/lib/spyke/base.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'active_model'
|
2
2
|
require 'spyke/associations'
|
3
|
-
require 'spyke/
|
3
|
+
require 'spyke/attribute_assignment'
|
4
4
|
require 'spyke/orm'
|
5
5
|
require 'spyke/http'
|
6
|
-
require 'spyke/
|
6
|
+
require 'spyke/scoping'
|
7
|
+
|
7
8
|
|
8
9
|
module Spyke
|
9
10
|
class Base
|
@@ -17,9 +18,9 @@ module Spyke
|
|
17
18
|
|
18
19
|
# Spyke
|
19
20
|
include Associations
|
20
|
-
include
|
21
|
+
include AttributeAssignment
|
21
22
|
include Http
|
22
23
|
include Orm
|
23
|
-
include
|
24
|
+
include Scoping
|
24
25
|
end
|
25
26
|
end
|
data/lib/spyke/http.rb
CHANGED
data/lib/spyke/orm.rb
CHANGED
@@ -50,9 +50,9 @@ module Spyke
|
|
50
50
|
|
51
51
|
def to_params
|
52
52
|
if include_root?
|
53
|
-
{ self.class.model_name.param_key => attributes.except(*uri.variables)
|
53
|
+
{ self.class.model_name.param_key => attributes.to_params.except(*uri.variables)}
|
54
54
|
else
|
55
|
-
attributes.except(*uri.variables)
|
55
|
+
attributes.to_params.except(*uri.variables)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
data/lib/spyke/relation.rb
CHANGED
@@ -5,19 +5,17 @@ module Spyke
|
|
5
5
|
include Enumerable
|
6
6
|
|
7
7
|
attr_reader :klass, :params
|
8
|
+
attr_writer :params
|
8
9
|
delegate :to_ary, :[], :empty?, :last, :size, :metadata, to: :find_some
|
9
10
|
|
10
11
|
def initialize(klass, options = {})
|
11
12
|
@klass, @options, @params = klass, options, {}
|
12
13
|
end
|
13
14
|
|
14
|
-
def all
|
15
|
-
where
|
16
|
-
end
|
17
|
-
|
18
15
|
def where(conditions = {})
|
19
|
-
|
20
|
-
|
16
|
+
relation = clone
|
17
|
+
relation.params = params.merge(conditions)
|
18
|
+
relation
|
21
19
|
end
|
22
20
|
|
23
21
|
# Overrides Enumerable find
|
@@ -53,10 +51,10 @@ module Spyke
|
|
53
51
|
|
54
52
|
# Keep hold of current scope while running a method on the class
|
55
53
|
def scoping
|
56
|
-
klass.current_scope = self
|
54
|
+
previous, klass.current_scope = klass.current_scope, self
|
57
55
|
yield
|
58
56
|
ensure
|
59
|
-
klass.current_scope =
|
57
|
+
klass.current_scope = previous
|
60
58
|
end
|
61
59
|
end
|
62
60
|
end
|
@@ -2,11 +2,15 @@ require 'spyke/relation'
|
|
2
2
|
require 'spyke/scope_registry'
|
3
3
|
|
4
4
|
module Spyke
|
5
|
-
module
|
5
|
+
module Scoping
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
delegate :
|
9
|
+
delegate :where, :build, to: :all
|
10
|
+
|
11
|
+
def all
|
12
|
+
current_scope || Relation.new(self, uri: uri)
|
13
|
+
end
|
10
14
|
|
11
15
|
def scope(name, code)
|
12
16
|
define_singleton_method name, code
|
@@ -17,14 +21,14 @@ module Spyke
|
|
17
21
|
end
|
18
22
|
|
19
23
|
def current_scope
|
20
|
-
ScopeRegistry.value_for(:current_scope, name)
|
24
|
+
ScopeRegistry.value_for(:current_scope, name)
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
28
|
private
|
25
29
|
|
26
|
-
def
|
27
|
-
self.class.
|
30
|
+
def scope
|
31
|
+
@scope ||= self.class.all
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
data/lib/spyke/version.rb
CHANGED
data/test/associations_test.rb
CHANGED
@@ -118,12 +118,37 @@ module Spyke
|
|
118
118
|
assert_equal 1, recipe.groups.first.recipe_id
|
119
119
|
end
|
120
120
|
|
121
|
+
def test_multiple_builds
|
122
|
+
recipe = Recipe.new
|
123
|
+
recipe.groups.build(name: 'Condiments')
|
124
|
+
recipe.groups.build(name: 'Tools')
|
125
|
+
assert_equal %w{ Condiments Tools }, recipe.groups.map(&:name)
|
126
|
+
end
|
127
|
+
|
121
128
|
def test_new_has_many_association
|
122
129
|
recipe = Recipe.new(id: 1)
|
123
130
|
recipe.groups.new
|
124
131
|
assert_equal 1, recipe.groups.first.recipe_id
|
125
132
|
end
|
126
133
|
|
134
|
+
def test_changing_attributes_directly_after_build_on_has_many_association
|
135
|
+
recipe = Recipe.new(id: 1)
|
136
|
+
recipe.groups.build(name: 'Dessert')
|
137
|
+
recipe.groups.first.name = 'Starter'
|
138
|
+
|
139
|
+
assert_equal 'Starter', recipe.groups.first.name
|
140
|
+
assert_equal({ 'recipe' => { 'groups' => [{ 'recipe_id' => 1, 'name' => 'Starter' }] } }, recipe.to_params)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_changing_attributes_on_reference_after_build_on_has_many_association
|
144
|
+
recipe = Recipe.new(id: 1)
|
145
|
+
group = recipe.groups.build(name: 'Dessert')
|
146
|
+
group.name = 'Starter'
|
147
|
+
|
148
|
+
assert_equal 'Starter', recipe.groups.first.name
|
149
|
+
assert_equal({ 'recipe' => { 'groups' => [{ 'recipe_id' => 1, 'name' => 'Starter' }] } }, recipe.to_params)
|
150
|
+
end
|
151
|
+
|
127
152
|
def test_deep_build_has_many_association
|
128
153
|
recipe = Recipe.new(id: 1)
|
129
154
|
recipe.groups.build(ingredients: [Ingredient.new(name: 'Salt')])
|
@@ -132,6 +157,16 @@ module Spyke
|
|
132
157
|
assert_equal({ 'recipe' => { 'groups' => [{ 'recipe_id' => 1, 'ingredients' => [{ 'name' => 'Salt' }] }] } }, recipe.to_params)
|
133
158
|
end
|
134
159
|
|
160
|
+
def test_sequential_deep_build_has_many_association
|
161
|
+
recipe = Recipe.new(id: 1)
|
162
|
+
recipe.groups.build
|
163
|
+
recipe.groups.first.ingredients.build(name: 'Salt')
|
164
|
+
|
165
|
+
assert_equal %w{ Salt }, recipe.ingredients.map(&:name)
|
166
|
+
assert_equal({ 'recipe' => { 'groups' => [{ 'recipe_id' => 1, 'ingredients' => [{ 'group_id' => nil, 'name' => 'Salt' }] }] } }, recipe.to_params)
|
167
|
+
assert_equal({ 'group' => { 'recipe_id' => 1, 'ingredients' => [{ 'group_id' => nil, 'name' => 'Salt' }] } }, recipe.groups.first.to_params)
|
168
|
+
end
|
169
|
+
|
135
170
|
def test_deep_build_has_many_association_with_scope
|
136
171
|
recipe = User.new(id: 1).recipes.published.build
|
137
172
|
|
@@ -168,7 +203,7 @@ module Spyke
|
|
168
203
|
assert_equal Image, recipe.background_image.class
|
169
204
|
end
|
170
205
|
|
171
|
-
def
|
206
|
+
def test_cached_result_for_associations
|
172
207
|
endpoint_1 = stub_request(:get, 'http://sushi.com/recipes/1/groups?per_page=3')
|
173
208
|
endpoint_2 = stub_request(:get, 'http://sushi.com/recipes/1/groups')
|
174
209
|
|
@@ -250,9 +285,15 @@ module Spyke
|
|
250
285
|
|
251
286
|
def test_nested_attributes_merging_with_existing_when_ids_present?
|
252
287
|
recipe = Recipe.new(groups_attributes: [{ id: 1, title: 'starter', description: 'nice' }, { id: 2, title: 'sauce', description: 'spicy' }])
|
253
|
-
recipe.attributes = { groups_attributes: [{ 'id' => '2', 'title' => 'flavor' }] }
|
254
|
-
assert_equal %w{ starter flavor }, recipe.groups.map(&:title)
|
255
|
-
assert_equal %w{ nice spicy }, recipe.groups.map(&:description)
|
288
|
+
recipe.attributes = { groups_attributes: [{ 'id' => '2', 'title' => 'flavor' }, { 'title' => 'spices', 'description' => 'lovely' }, { 'title' => 'sweetener', 'description' => 'sweet' }] }
|
289
|
+
assert_equal %w{ starter flavor spices sweetener }, recipe.groups.map(&:title)
|
290
|
+
assert_equal %w{ nice spicy lovely sweet }, recipe.groups.map(&:description)
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_nested_attributes_appending_to_existing_when_ids_present?
|
294
|
+
recipe = Recipe.new(groups_attributes: [{ id: 1, title: 'starter' }, { id: 2, title: 'sauce' }])
|
295
|
+
recipe.attributes = { groups_attributes: [{ title: 'flavor' }] }
|
296
|
+
assert_equal %w{ starter sauce flavor }, recipe.groups.map(&:title)
|
256
297
|
end
|
257
298
|
|
258
299
|
def test_nested_attributes_has_many_using_hash_syntax
|
@@ -307,5 +348,14 @@ module Spyke
|
|
307
348
|
assert_equal [], Group.new.ingredients.to_a
|
308
349
|
assert_equal [1], Group.new(ingredients: [{ id: 1 }]).ingredients.map(&:id)
|
309
350
|
end
|
351
|
+
|
352
|
+
def test_class_methods_for_associations
|
353
|
+
recipe = Recipe.new
|
354
|
+
recipe.groups.build_default
|
355
|
+
|
356
|
+
assert_equal({ 'recipe' => { 'groups' => [{ 'recipe_id' => nil, 'name' => 'Condiments', 'ingredients' => [{ 'group_id' => nil, 'name' => 'Salt' }] }, { 'recipe_id' => nil, 'name' => 'Tools', 'ingredients' => [{ 'group_id' => nil, 'name' => 'Spoon' }] }] } }, recipe.to_params)
|
357
|
+
assert_equal %w{ Condiments Tools }, recipe.groups.map(&:name)
|
358
|
+
assert_equal %w{ Salt Spoon }, recipe.ingredients.map(&:name)
|
359
|
+
end
|
310
360
|
end
|
311
361
|
end
|
data/test/attributes_test.rb
CHANGED
@@ -2,6 +2,12 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
module Spyke
|
4
4
|
class AttributesTest < MiniTest::Test
|
5
|
+
|
6
|
+
def test_basics
|
7
|
+
attr = Attributes.new(id: 3, 'title' => 'Fish', groups: [ Group.new(name: 'Starter'), { name: 'Dessert' } ])
|
8
|
+
assert_equal({ 'id' => 3 , 'title' => 'Fish', 'groups' => [{ 'name' => 'Starter' }, { 'name' => 'Dessert' }] }, attr.to_params)
|
9
|
+
end
|
10
|
+
|
5
11
|
def test_predicate_methods
|
6
12
|
stub_request(:get, 'http://sushi.com/recipes/1').to_return_json(result: { id: 1, title: 'Sushi' })
|
7
13
|
|
data/test/relation_test.rb
CHANGED
@@ -12,6 +12,32 @@ module Spyke
|
|
12
12
|
assert_equal 'meta', recipes.metadata
|
13
13
|
end
|
14
14
|
|
15
|
+
def test_scope_independence
|
16
|
+
endpoint = stub_request(:get, 'http://sushi.com/recipes?query=chicken')
|
17
|
+
wrong_endpoint = stub_request(:get, 'http://sushi.com/recipes?query=chicken&page=1')
|
18
|
+
|
19
|
+
search = Search.new('chicken')
|
20
|
+
variant = search.recipes.where(page: 1)
|
21
|
+
original = search.recipes
|
22
|
+
|
23
|
+
refute_same variant, original
|
24
|
+
|
25
|
+
original.to_a
|
26
|
+
|
27
|
+
assert_not_requested wrong_endpoint
|
28
|
+
assert_requested endpoint, times: 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_scope_not_firing_twice_for_duplicate_scope
|
32
|
+
endpoint = stub_request(:get, 'http://sushi.com/recipes?query=chicken')
|
33
|
+
|
34
|
+
search = Search.new('chicken')
|
35
|
+
search.recipes.page.to_a
|
36
|
+
search.suggestions
|
37
|
+
|
38
|
+
assert_requested endpoint, times: 1
|
39
|
+
end
|
40
|
+
|
15
41
|
def test_scope_with_find
|
16
42
|
endpoint = stub_request(:get, 'http://sushi.com/recipes/1?status=published').to_return_json(result: { id: 1 })
|
17
43
|
|
data/test/support/fixtures.rb
CHANGED
@@ -15,12 +15,14 @@ class Recipe < Spyke::Base
|
|
15
15
|
|
16
16
|
accepts_nested_attributes_for :image, :user, :groups
|
17
17
|
|
18
|
-
def self.page(number)
|
19
|
-
|
18
|
+
def self.page(number = nil)
|
19
|
+
result = all
|
20
|
+
result = result.where(page: number) if number
|
21
|
+
result
|
20
22
|
end
|
21
23
|
|
22
24
|
def ingredients
|
23
|
-
groups.
|
25
|
+
groups.flat_map(&:ingredients)
|
24
26
|
end
|
25
27
|
|
26
28
|
private
|
@@ -47,6 +49,13 @@ end
|
|
47
49
|
class Group < Spyke::Base
|
48
50
|
has_many :ingredients, uri: nil
|
49
51
|
accepts_nested_attributes_for :ingredients
|
52
|
+
|
53
|
+
def self.build_default
|
54
|
+
group_1 = build(name: 'Condiments')
|
55
|
+
group_1.ingredients.build(name: 'Salt')
|
56
|
+
group_2 = build(name: 'Tools')
|
57
|
+
group_2.ingredients.build(name: 'Spoon')
|
58
|
+
end
|
50
59
|
end
|
51
60
|
|
52
61
|
class Ingredient < Spyke::Base
|
@@ -64,3 +73,17 @@ end
|
|
64
73
|
class Comment < Spyke::Base
|
65
74
|
scope :approved, -> { where(comment_approved: true) }
|
66
75
|
end
|
76
|
+
|
77
|
+
class Search
|
78
|
+
def initialize(query)
|
79
|
+
@query = query
|
80
|
+
end
|
81
|
+
|
82
|
+
def recipes
|
83
|
+
@recipes ||= Recipe.where(query: @query)
|
84
|
+
end
|
85
|
+
|
86
|
+
def suggestions
|
87
|
+
recipes.metadata[:suggestions]
|
88
|
+
end
|
89
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spyke
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Balvig
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -270,6 +270,7 @@ files:
|
|
270
270
|
- lib/spyke/associations/belongs_to.rb
|
271
271
|
- lib/spyke/associations/has_many.rb
|
272
272
|
- lib/spyke/associations/has_one.rb
|
273
|
+
- lib/spyke/attribute_assignment.rb
|
273
274
|
- lib/spyke/attributes.rb
|
274
275
|
- lib/spyke/base.rb
|
275
276
|
- lib/spyke/collection.rb
|
@@ -284,7 +285,7 @@ files:
|
|
284
285
|
- lib/spyke/relation.rb
|
285
286
|
- lib/spyke/result.rb
|
286
287
|
- lib/spyke/scope_registry.rb
|
287
|
-
- lib/spyke/
|
288
|
+
- lib/spyke/scoping.rb
|
288
289
|
- lib/spyke/version.rb
|
289
290
|
- spyke.gemspec
|
290
291
|
- test/associations_test.rb
|