spyke 5.3.1 → 5.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a8f605cd12c22641c85ffdaf643cead8ef8eab997e347075596a7b7c9149e0c
4
- data.tar.gz: 97caf4cac49eb5e66164193845e5a5bb60069cc910f349de38eaf01e0d7fd57c
3
+ metadata.gz: 46c56a1975e770acc9b1e9d40abdfbfd7d59b15705da333d293d0ecb18678628
4
+ data.tar.gz: bbc4a65e7cc6293ce9e93410eae69dd4003ede73364207c17bff79c1736770a2
5
5
  SHA512:
6
- metadata.gz: 1afad1b576ca01dd62b642da238ad80f2048da50a73ca387ed8df541a005601ba55a2c4769c881e991f79d40adbfa0992377989881dc49ce12c58174265ffd36
7
- data.tar.gz: 6e99ad81b53c136acadb85bc5363510067cb7704bbdc74a8ad1c84c0b79ff0079ac5029e5aa6aa65b74e24446068d01e568fb4258080bdf4202ccb4c75303be9
6
+ metadata.gz: 6e741d1302b7a404d2f052329814b74af2aab61f17a5edff94b06f1bb5fadf78a8bbb43c93dbd2ed4a48064721dad8e57a28d53fde35b5d14f7a44f19fe01a31
7
+ data.tar.gz: 9a7cbdf9fd0b504c9772dd5388ff949b1ab5abe666277e11efc8e347e3aae22e5337cba0b506388f90f9cc2b68f5ddb97bf4dd8b08b50694a43e1f2d99c32a9a
data/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  notifications:
2
2
  email: false
3
3
  rvm:
4
- - 2.3.1
5
- - 2.5.1
4
+ - 2.6.6
6
5
  sudo: false
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Spyke
2
2
 
3
3
  <p align="center">
4
- <img src="http://upload.wikimedia.org/wikipedia/en/thumb/2/21/Spyke.jpg/392px-Spyke.jpg" width="20%" />
4
+ <img src="http://upload.wikimedia.org/wikipedia/en/thumb/2/21/Spyke.jpg/392px-Spyke.jpg" width="15%" />
5
5
  <br/>
6
6
  Interact with remote <strong>REST services</strong> in an <strong>ActiveRecord-like</strong> manner.
7
7
  <br /><br />
@@ -30,6 +30,7 @@ Add this line to your application's Gemfile:
30
30
 
31
31
  ```ruby
32
32
  gem 'spyke'
33
+ gem 'multi_json' # or whatever is needed to parse responses
33
34
  ```
34
35
 
35
36
  Spyke uses Faraday to handle requests and expects it to parse the response body into a hash in the following format:
@@ -152,6 +153,19 @@ Post.request(:post, 'posts/3/log', time: '12:00')
152
153
  # => POST http://api.com/posts/3/log - { time: '12:00' }
153
154
  ```
154
155
 
156
+ ### Custom primary keys
157
+
158
+ Custom primary keys can be defined with `self.primary_key = :custom_key`:
159
+
160
+ ```ruby
161
+ class User < Spyke::Base
162
+ self.primary_key = :user_id
163
+
164
+ # When using custom URIs the :id parameter also has to be adjusted
165
+ uri 'people(/:user_id)'
166
+ end
167
+ ```
168
+
155
169
  ### API-side validations
156
170
 
157
171
  Spyke expects errors to be formatted in the same way as the
@@ -6,7 +6,6 @@ module Spyke
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- attr_reader :attributes
10
9
  delegate :[], :[]=, to: :attributes
11
10
  end
12
11
 
@@ -40,11 +39,19 @@ module Spyke
40
39
  yield self if block_given?
41
40
  end
42
41
 
42
+ def attributes
43
+ @spyke_attributes
44
+ end
45
+
43
46
  def attributes=(new_attributes)
44
- @attributes ||= Attributes.new(scope.params)
47
+ @spyke_attributes ||= Attributes.new(scope.params)
45
48
  use_setters(new_attributes) if new_attributes
46
49
  end
47
50
 
51
+ def id?
52
+ id.present?
53
+ end
54
+
48
55
  def id
49
56
  attributes[primary_key]
50
57
  end
@@ -76,7 +83,7 @@ module Spyke
76
83
  # Set id attribute directly if using a custom primary key alongside an attribute named "id" to avoid clobbering
77
84
  set_attribute :id, attributes.delete(:id) if conflicting_ids?(attributes)
78
85
 
79
- attributes.each do |key, value|
86
+ attributes.to_h.each do |key, value|
80
87
  send "#{key}=", value
81
88
  end
82
89
  end
data/lib/spyke/http.rb CHANGED
@@ -41,20 +41,20 @@ module Spyke
41
41
  @uri ||= uri_template || default_uri
42
42
  end
43
43
 
44
- private
45
-
46
- def send_request(method, path, params)
47
- connection.send(method) do |request|
48
- if method == :get
49
- request.url path.to_s, params
50
- else
51
- request.url path.to_s
52
- request.body = params
53
- end
44
+ def send_request(method, path, params)
45
+ connection.send(method) do |request|
46
+ if method == :get
47
+ request.url path.to_s, params
48
+ else
49
+ request.url path.to_s
50
+ request.body = params
54
51
  end
55
- rescue Faraday::ConnectionFailed, Faraday::TimeoutError
56
- raise ConnectionError
57
52
  end
53
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError
54
+ raise ConnectionError
55
+ end
56
+
57
+ private
58
58
 
59
59
  def scoped_request(method)
60
60
  uri = new.uri
data/lib/spyke/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spyke
2
- VERSION = '5.3.1'
2
+ VERSION = '5.4.1'
3
3
  end
data/spyke.gemspec CHANGED
@@ -18,12 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'activesupport', '>= 4.0.0', '< 6.0'
22
- spec.add_dependency 'activemodel', '>= 4.0.0', '< 6.0'
21
+ spec.add_dependency 'activesupport', '>= 4.0.0'
22
+ spec.add_dependency 'activemodel', '>= 4.0.0'
23
23
  spec.add_dependency 'faraday', '>= 0.9.0', '< 2.0'
24
24
  spec.add_dependency 'faraday_middleware', '>= 0.9.1', '< 2.0'
25
25
  spec.add_dependency 'addressable', '>= 2.5.2'
26
26
 
27
+ spec.add_development_dependency 'actionpack', '>= 4.0.0'
27
28
  spec.add_development_dependency 'bundler', '~> 1.6'
28
29
  spec.add_development_dependency 'coveralls', '~> 0.7'
29
30
  spec.add_development_dependency 'minitest'
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ module Spyke
4
+ class ActiveModelDirtyTest < MiniTest::Test
5
+ def test_attributes_instance_var_compatibility
6
+ recipe = RecipeWithDirty.new(title: 'Cheeseburger')
7
+
8
+ # If @attributes is set on recipe ActiveModel::Dirty will crash
9
+ assert_equal false, recipe.attribute_changed_in_place?(:title)
10
+ end
11
+ end
12
+ end
@@ -301,6 +301,22 @@ module Spyke
301
301
  assert_equal %w{ starter sauce }, recipe.groups.map(&:title)
302
302
  end
303
303
 
304
+ def test_nested_attributes_has_one_using_strong_params
305
+ recipe = Recipe.new(image_attributes: strong_params(file: 'bob.jpg').permit!)
306
+ assert_equal 'bob.jpg', recipe.image.file
307
+ end
308
+
309
+ def test_nested_attributes_belongs_to_using_strong_params
310
+ recipe = Recipe.new(user_attributes: strong_params({ name: 'Bob' }).permit!)
311
+ assert_equal 'Bob', recipe.user.name
312
+ end
313
+
314
+ def test_nested_attributes_has_many_using_strong_params
315
+ params = strong_params(groups_attributes: [strong_params(title: 'starter').permit!, strong_params(title: 'sauce').permit!]).permit!
316
+ recipe = Recipe.new(params)
317
+ assert_equal %w{ starter sauce }, recipe.groups.map(&:title)
318
+ end
319
+
304
320
  def test_nested_attributes_replacing_existing_when_no_ids_present
305
321
  recipe = Recipe.new(groups_attributes: [{ title: 'starter' }, { title: 'sauce' }])
306
322
  recipe.attributes = { groups_attributes: [{ title: 'flavor' }] }
@@ -325,6 +341,17 @@ module Spyke
325
341
  assert_equal %w{ starter sauce }, recipe.groups.map(&:title)
326
342
  end
327
343
 
344
+ def test_nested_attributes_has_many_using_strong_params_with_hash_syntax
345
+ params = strong_params(
346
+ groups_attributes: strong_params(
347
+ '0' => strong_params(title: 'starter').permit!,
348
+ '1' => strong_params(title: 'sauce').permit!,
349
+ ).permit!
350
+ ).permit!
351
+ recipe = Recipe.new(params)
352
+ assert_equal %w{ starter sauce }, recipe.groups.map(&:title)
353
+ end
354
+
328
355
  def test_deeply_nested_attributes_has_many_using_array_syntax
329
356
  params = { groups_attributes: [{ id: 1, ingredients_attributes: [{ id: 1, name: 'Salt' }, { id: 2, name: 'Pepper' } ]}] }
330
357
  recipe = Recipe.new(params)
@@ -357,6 +384,40 @@ module Spyke
357
384
  assert_equal %w{ Salt Pepper }, recipe.ingredients.map(&:name)
358
385
  end
359
386
 
387
+ def test_deeply_nested_attributes_has_many_using_strong_params_with_array_syntax
388
+ params = strong_params(
389
+ groups_attributes: [
390
+ strong_params(
391
+ ingredients_attributes: [
392
+ strong_params(id: '', name: 'Salt').permit!,
393
+ strong_params(id: '', name: 'Pepper').permit!,
394
+ ]
395
+ ).permit!
396
+ ]
397
+ ).permit!
398
+ recipe = Recipe.new(params)
399
+ assert_equal %w{ Salt Pepper }, recipe.ingredients.map(&:name)
400
+ recipe.attributes = params
401
+ assert_equal %w{ Salt Pepper }, recipe.ingredients.map(&:name)
402
+ end
403
+
404
+ def test_deeply_nested_attributes_has_many_using_strong_params_with_hash_syntax
405
+ params = strong_params(
406
+ groups_attributes: strong_params(
407
+ '0' => strong_params(
408
+ ingredients_attributes: strong_params(
409
+ '0' => strong_params(id: '', name: 'Salt').permit!,
410
+ '1' => strong_params(id: '', name: 'Pepper').permit!,
411
+ ).permit!
412
+ ).permit!
413
+ ).permit!
414
+ ).permit!
415
+ recipe = Recipe.new(params)
416
+ assert_equal %w{ Salt Pepper }, recipe.ingredients.map(&:name)
417
+ recipe.attributes = params
418
+ assert_equal %w{ Salt Pepper }, recipe.ingredients.map(&:name)
419
+ end
420
+
360
421
  def test_reflect_on_association
361
422
  assert_equal Group, Recipe.reflect_on_association(:group).klass
362
423
  assert_equal Cookbook::Like, Cookbook::Tip.reflect_on_association(:like).klass
@@ -11,6 +11,18 @@ module Spyke
11
11
  assert_equal 'Tasty', recipe.description
12
12
  end
13
13
 
14
+ def test_initializing_using_permitted_strong_params
15
+ ingredient = Ingredient.new(strong_params(name: 'Flour').permit!)
16
+
17
+ assert_equal 'Flour', ingredient.name
18
+ end
19
+
20
+ def test_initializing_using_unpermitted_strong_params
21
+ assert_raises ActionController::UnfilteredParameters do
22
+ Ingredient.new(strong_params(name: 'Flour'))
23
+ end
24
+ end
25
+
14
26
  def test_to_params
15
27
  attr = Attributes.new(id: 3, 'title' => 'Fish', groups: [ Group.new(name: 'Starter'), { name: 'Dessert' } ])
16
28
  assert_equal({ 'id' => 3 , 'title' => 'Fish', 'groups' => [{ 'name' => 'Starter' }, { 'name' => 'Dessert' }] }, attr.to_params)
@@ -0,0 +1,11 @@
1
+ require 'test_helper'
2
+
3
+ module Spyke
4
+ class ConfigConnectionWarnTest < MiniTest::Test
5
+ def test_config_connection_warn
6
+ assert_output '', "[DEPRECATION] `Spyke::Config.connection=` is deprecated. Please use `Spyke::Base.connection=` instead.\n" do
7
+ Spyke::Config.connection = Spyke::Base.connection
8
+ end
9
+ end
10
+ end
11
+ end
@@ -63,9 +63,16 @@ module Spyke
63
63
  end
64
64
 
65
65
  def test_multiple_apis
66
- endpoint = stub_request(:get, 'http://sashimi.com/other_recipes')
66
+ endpoint = stub_request(:get, 'http://sashimi.com/recipes')
67
67
  OtherRecipe.all.to_a
68
68
  assert_requested endpoint
69
69
  end
70
+
71
+ def test_multiple_apis_with_custom_fallback
72
+ fallback_endpoint = stub_request(:get, 'http://sushi.com/recipes')
73
+ primary_endpoint = stub_request(:get, 'http://sashimi.com/recipes').to_timeout
74
+ OtherRecipe.all.to_a
75
+ assert_requested fallback_endpoint
76
+ end
70
77
  end
71
78
  end
data/test/orm_test.rb CHANGED
@@ -54,6 +54,22 @@ module Spyke
54
54
  assert_requested endpoint
55
55
  end
56
56
 
57
+ def test_save_hash_attribute
58
+ endpoint = stub_request(:post, 'http://sushi.com/recipes').with(body: { recipe: { meta: { foo: 'bar' } } })
59
+
60
+ Recipe.create(meta: { foo: 'bar' })
61
+
62
+ assert_requested endpoint
63
+ end
64
+
65
+ def test_save_hash_attribute_with_strong_params
66
+ endpoint = stub_request(:post, 'http://sushi.com/recipes').with(body: { recipe: { meta: { foo: 'bar' } } })
67
+
68
+ Recipe.create(strong_params(meta: { foo: 'bar' }).permit!)
69
+
70
+ assert_requested endpoint
71
+ end
72
+
57
73
  def test_update_attributes
58
74
  endpoint = stub_request(:put, 'http://sushi.com/recipes/1').with(body: { recipe: { title: 'Sushi' } }).to_return_json(result: { id: 1, title: 'Sushi (saved)' })
59
75
 
@@ -206,5 +222,13 @@ module Spyke
206
222
  assert_equal 1, user[:uuid]
207
223
  assert_equal 42, user[:id]
208
224
  end
225
+
226
+ def test_custom_primary_key_used_for_persistence_check
227
+ user = User.new
228
+ refute user.persisted?
229
+
230
+ user.uuid = 1
231
+ assert user.persisted?
232
+ end
209
233
  end
210
234
  end
@@ -116,7 +116,15 @@ class OtherApi < Spyke::Base
116
116
  end
117
117
  end
118
118
 
119
- class OtherRecipe < OtherApi; end
119
+ class OtherRecipe < OtherApi
120
+ uri 'recipes/(:id)'
121
+
122
+ def self.send_request(method, path, params)
123
+ super
124
+ rescue Spyke::ConnectionError
125
+ Recipe.send_request(method, path, params)
126
+ end
127
+ end
120
128
 
121
129
  class Search
122
130
  def initialize(query)
@@ -152,3 +160,10 @@ module Cookbook
152
160
  include_root_in_json :foto
153
161
  end
154
162
  end
163
+
164
+ class RecipeWithDirty < Recipe
165
+ # NOTE: Simply including ActiveModel::Dirty doesn't provide all the dirty
166
+ # functionality. This is left intentionally incomplete as it's all we need
167
+ # for testing compatibility.
168
+ include ActiveModel::Dirty
169
+ end
data/test/test_helper.rb CHANGED
@@ -9,7 +9,7 @@ end
9
9
  require 'spyke'
10
10
  require 'minitest/autorun'
11
11
  require 'minitest/reporters'
12
- require 'mocha/mini_test'
12
+ require 'mocha/minitest'
13
13
  require 'multi_json'
14
14
  require 'pry'
15
15
 
@@ -18,3 +18,10 @@ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
18
18
 
19
19
  # Pretty colors
20
20
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
21
+
22
+ # For testing strong params
23
+ require 'action_controller/metal/strong_parameters'
24
+
25
+ def strong_params(params)
26
+ ActionController::Parameters.new(params)
27
+ 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: 5.3.1
4
+ version: 5.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Balvig
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-07 00:00:00.000000000 Z
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 4.0.0
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '6.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: 4.0.0
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '6.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: activemodel
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +31,6 @@ dependencies:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
33
  version: 4.0.0
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '6.0'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,9 +38,6 @@ dependencies:
47
38
  - - ">="
48
39
  - !ruby/object:Gem::Version
49
40
  version: 4.0.0
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: '6.0'
53
41
  - !ruby/object:Gem::Dependency
54
42
  name: faraday
55
43
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +92,20 @@ dependencies:
104
92
  - - ">="
105
93
  - !ruby/object:Gem::Version
106
94
  version: 2.5.2
95
+ - !ruby/object:Gem::Dependency
96
+ name: actionpack
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 4.0.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 4.0.0
107
109
  - !ruby/object:Gem::Dependency
108
110
  name: bundler
109
111
  requirement: !ruby/object:Gem::Requirement
@@ -257,7 +259,6 @@ files:
257
259
  - LICENSE.txt
258
260
  - README.md
259
261
  - Rakefile
260
- - circle.yml
261
262
  - lib/spyke.rb
262
263
  - lib/spyke/associations.rb
263
264
  - lib/spyke/associations/association.rb
@@ -285,9 +286,11 @@ files:
285
286
  - lib/spyke/scoping.rb
286
287
  - lib/spyke/version.rb
287
288
  - spyke.gemspec
289
+ - test/activemodel_dirty_test.rb
288
290
  - test/associations_test.rb
289
291
  - test/attributes_test.rb
290
292
  - test/callbacks_test.rb
293
+ - test/config_test.rb
291
294
  - test/custom_request_test.rb
292
295
  - test/fallbacks_test.rb
293
296
  - test/orm_test.rb
@@ -300,7 +303,7 @@ homepage: https://github.com/balvig/spyke
300
303
  licenses:
301
304
  - MIT
302
305
  metadata: {}
303
- post_install_message:
306
+ post_install_message:
304
307
  rdoc_options: []
305
308
  require_paths:
306
309
  - lib
@@ -315,15 +318,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
315
318
  - !ruby/object:Gem::Version
316
319
  version: '0'
317
320
  requirements: []
318
- rubyforge_project:
319
- rubygems_version: 2.7.6
320
- signing_key:
321
+ rubygems_version: 3.0.3
322
+ signing_key:
321
323
  specification_version: 4
322
324
  summary: Interact with REST services in an ActiveRecord-like manner
323
325
  test_files:
326
+ - test/activemodel_dirty_test.rb
324
327
  - test/associations_test.rb
325
328
  - test/attributes_test.rb
326
329
  - test/callbacks_test.rb
330
+ - test/config_test.rb
327
331
  - test/custom_request_test.rb
328
332
  - test/fallbacks_test.rb
329
333
  - test/orm_test.rb
data/circle.yml DELETED
@@ -1,3 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.2.2