restorm 1.0.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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +31 -0
  5. data/.rubocop_todo.yml +232 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +55 -0
  8. data/.yardopts +2 -0
  9. data/CONTRIBUTING.md +26 -0
  10. data/Gemfile +10 -0
  11. data/HER_README.md +1065 -0
  12. data/LICENSE +7 -0
  13. data/README.md +7 -0
  14. data/Rakefile +11 -0
  15. data/UPGRADE.md +101 -0
  16. data/gemfiles/Gemfile.activemodel-4.2 +6 -0
  17. data/gemfiles/Gemfile.activemodel-5.0 +6 -0
  18. data/gemfiles/Gemfile.activemodel-5.1 +6 -0
  19. data/gemfiles/Gemfile.activemodel-5.2 +6 -0
  20. data/gemfiles/Gemfile.faraday-1.0 +6 -0
  21. data/lib/restorm/api.rb +121 -0
  22. data/lib/restorm/collection.rb +13 -0
  23. data/lib/restorm/errors.rb +29 -0
  24. data/lib/restorm/json_api/model.rb +42 -0
  25. data/lib/restorm/middleware/accept_json.rb +18 -0
  26. data/lib/restorm/middleware/first_level_parse_json.rb +37 -0
  27. data/lib/restorm/middleware/json_api_parser.rb +37 -0
  28. data/lib/restorm/middleware/parse_json.rb +22 -0
  29. data/lib/restorm/middleware/second_level_parse_json.rb +37 -0
  30. data/lib/restorm/middleware.rb +12 -0
  31. data/lib/restorm/model/associations/association.rb +128 -0
  32. data/lib/restorm/model/associations/association_proxy.rb +44 -0
  33. data/lib/restorm/model/associations/belongs_to_association.rb +95 -0
  34. data/lib/restorm/model/associations/has_many_association.rb +100 -0
  35. data/lib/restorm/model/associations/has_one_association.rb +79 -0
  36. data/lib/restorm/model/associations.rb +141 -0
  37. data/lib/restorm/model/attributes.rb +322 -0
  38. data/lib/restorm/model/base.rb +33 -0
  39. data/lib/restorm/model/deprecated_methods.rb +61 -0
  40. data/lib/restorm/model/http.rb +119 -0
  41. data/lib/restorm/model/introspection.rb +67 -0
  42. data/lib/restorm/model/nested_attributes.rb +45 -0
  43. data/lib/restorm/model/orm.rb +299 -0
  44. data/lib/restorm/model/parse.rb +223 -0
  45. data/lib/restorm/model/paths.rb +125 -0
  46. data/lib/restorm/model/relation.rb +209 -0
  47. data/lib/restorm/model.rb +75 -0
  48. data/lib/restorm/version.rb +3 -0
  49. data/lib/restorm.rb +19 -0
  50. data/restorm.gemspec +29 -0
  51. data/spec/api_spec.rb +120 -0
  52. data/spec/collection_spec.rb +41 -0
  53. data/spec/json_api/model_spec.rb +169 -0
  54. data/spec/middleware/accept_json_spec.rb +11 -0
  55. data/spec/middleware/first_level_parse_json_spec.rb +63 -0
  56. data/spec/middleware/json_api_parser_spec.rb +52 -0
  57. data/spec/middleware/second_level_parse_json_spec.rb +35 -0
  58. data/spec/model/associations/association_proxy_spec.rb +29 -0
  59. data/spec/model/associations_spec.rb +911 -0
  60. data/spec/model/attributes_spec.rb +354 -0
  61. data/spec/model/callbacks_spec.rb +176 -0
  62. data/spec/model/dirty_spec.rb +133 -0
  63. data/spec/model/http_spec.rb +201 -0
  64. data/spec/model/introspection_spec.rb +81 -0
  65. data/spec/model/nested_attributes_spec.rb +135 -0
  66. data/spec/model/orm_spec.rb +704 -0
  67. data/spec/model/parse_spec.rb +520 -0
  68. data/spec/model/paths_spec.rb +348 -0
  69. data/spec/model/relation_spec.rb +247 -0
  70. data/spec/model/validations_spec.rb +43 -0
  71. data/spec/model_spec.rb +45 -0
  72. data/spec/spec_helper.rb +25 -0
  73. data/spec/support/macros/her_macros.rb +17 -0
  74. data/spec/support/macros/model_macros.rb +36 -0
  75. data/spec/support/macros/request_macros.rb +27 -0
  76. metadata +203 -0
@@ -0,0 +1,209 @@
1
+ module Restorm
2
+ module Model
3
+ class Relation
4
+
5
+ # @private
6
+ attr_accessor :params
7
+ attr_writer :parent
8
+
9
+ # @private
10
+ def initialize(parent)
11
+ @parent = parent
12
+ @params = {}
13
+ end
14
+
15
+ # @private
16
+ def apply_to(attributes)
17
+ @params.merge(attributes)
18
+ end
19
+
20
+ # Build a new resource
21
+ def build(attributes = {})
22
+ @parent.build(@params.merge(attributes))
23
+ end
24
+
25
+ # Add a query string parameter
26
+ #
27
+ # @example
28
+ # @users = User.all
29
+ # # Fetched via GET "/users"
30
+ #
31
+ # @example
32
+ # @users = User.where(:approved => 1).all
33
+ # # Fetched via GET "/users?approved=1"
34
+ def where(params = {})
35
+ return self if params.blank? && !@_fetch.nil?
36
+ clone.tap do |r|
37
+ r.params = r.params.merge(params)
38
+ r.clear_fetch_cache!
39
+ end
40
+ end
41
+ alias all where
42
+
43
+ # Bubble all methods to the fetched collection
44
+ #
45
+ # @private
46
+ def method_missing(method, *args, &blk)
47
+ fetch.send(method, *args, &blk)
48
+ end
49
+
50
+ # @private
51
+ def respond_to?(method, *args)
52
+ super || fetch.respond_to?(method, *args)
53
+ end
54
+
55
+ # @private
56
+ def nil?
57
+ fetch.nil?
58
+ end
59
+
60
+ # @private
61
+ def kind_of?(thing)
62
+ fetch.is_a?(thing)
63
+ end
64
+
65
+ # Fetch a collection of resources
66
+ #
67
+ # @private
68
+ def fetch
69
+ @_fetch ||= begin
70
+ path = @parent.build_request_path(@parent.collection_path, @params)
71
+ method = @parent.method_for(:find)
72
+ @parent.request(@params.merge(:_method => method, :_path => path)) do |parsed_data, _|
73
+ @parent.new_collection(parsed_data)
74
+ end
75
+ end
76
+ end
77
+
78
+ # Fetch specific resource(s) by their ID
79
+ #
80
+ # @example
81
+ # @user = User.find(1)
82
+ # # Fetched via GET "/users/1"
83
+ #
84
+ # @example
85
+ # @users = User.find([1, 2])
86
+ # # Fetched via GET "/users/1" and GET "/users/2"
87
+ def find(*ids)
88
+ params = @params.merge(ids.last.is_a?(Hash) ? ids.pop : {})
89
+ ids = Array(params[@parent.primary_key]) if params.key?(@parent.primary_key)
90
+
91
+ results = ids.flatten.compact.uniq.map do |id|
92
+ resource = nil
93
+ request_params = params.merge(
94
+ :_method => @parent.method_for(:find),
95
+ :_path => @parent.build_request_path(params.merge(@parent.primary_key => id))
96
+ )
97
+
98
+ @parent.request(request_params) do |parsed_data, response|
99
+ if response.success?
100
+ resource = @parent.new_from_parsed_data(parsed_data)
101
+ resource.run_callbacks :find
102
+ else
103
+ return nil
104
+ end
105
+ end
106
+
107
+ resource
108
+ end
109
+
110
+ ids.length > 1 || ids.first.is_a?(Array) ? results : results.first
111
+ end
112
+
113
+ # Fetch first resource with the given attributes.
114
+ #
115
+ # If no resource is found, returns <tt>nil</tt>.
116
+ #
117
+ # @example
118
+ # @user = User.find_by(name: "Tobias", age: 42)
119
+ # # Called via GET "/users?name=Tobias&age=42"
120
+ def find_by(params)
121
+ where(params).first
122
+ end
123
+
124
+ # Fetch first resource with the given attributes, or create a resource
125
+ # with the attributes if one is not found.
126
+ #
127
+ # @example
128
+ # @user = User.find_or_create_by(email: "remi@example.com")
129
+ #
130
+ # # Returns the first item in the collection if present:
131
+ # # Called via GET "/users?email=remi@example.com"
132
+ #
133
+ # # If collection is empty:
134
+ # # POST /users with `email=remi@example.com`
135
+ # @user.email # => "remi@example.com"
136
+ # @user.new? # => false
137
+ def find_or_create_by(attributes)
138
+ find_by(attributes) || create(attributes)
139
+ end
140
+
141
+ # Fetch first resource with the given attributes, or initialize a resource
142
+ # with the attributes if one is not found.
143
+ #
144
+ # @example
145
+ # @user = User.find_or_initialize_by(email: "remi@example.com")
146
+ #
147
+ # # Returns the first item in the collection if present:
148
+ # # Called via GET "/users?email=remi@example.com"
149
+ #
150
+ # # If collection is empty:
151
+ # @user.email # => "remi@example.com"
152
+ # @user.new? # => true
153
+ def find_or_initialize_by(attributes)
154
+ find_by(attributes) || build(attributes)
155
+ end
156
+
157
+ # Create a resource and return it
158
+ #
159
+ # @example
160
+ # @user = User.create(:fullname => "Tobias Fünke")
161
+ # # Called via POST "/users/1" with `&fullname=Tobias+Fünke`
162
+ #
163
+ # @example
164
+ # @user = User.where(:email => "tobias@bluth.com").create(:fullname => "Tobias Fünke")
165
+ # # Called via POST "/users/1" with `&email=tobias@bluth.com&fullname=Tobias+Fünke`
166
+ def create(attributes = {})
167
+ attributes ||= {}
168
+ resource = @parent.new(@params.merge(attributes))
169
+ resource.save
170
+
171
+ resource
172
+ end
173
+
174
+ # Fetch a resource and create it if it's not found
175
+ #
176
+ # @example
177
+ # @user = User.where(:email => "remi@example.com").find_or_create
178
+ #
179
+ # # Returns the first item of the collection if present:
180
+ # # GET "/users?email=remi@example.com"
181
+ #
182
+ # # If collection is empty:
183
+ # # POST /users with `email=remi@example.com`
184
+ def first_or_create(attributes = {})
185
+ fetch.first || create(attributes)
186
+ end
187
+
188
+ # Fetch a resource and build it if it's not found
189
+ #
190
+ # @example
191
+ # @user = User.where(:email => "remi@example.com").find_or_initialize
192
+ #
193
+ # # Returns the first item of the collection if present:
194
+ # # GET "/users?email=remi@example.com"
195
+ #
196
+ # # If collection is empty:
197
+ # @user.email # => "remi@example.com"
198
+ # @user.new? # => true
199
+ def first_or_initialize(attributes = {})
200
+ fetch.first || build(attributes)
201
+ end
202
+
203
+ # @private
204
+ def clear_fetch_cache!
205
+ instance_variable_set(:@_fetch, nil)
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,75 @@
1
+ require "restorm/model/base"
2
+ require "restorm/model/deprecated_methods"
3
+ require "restorm/model/http"
4
+ require "restorm/model/attributes"
5
+ require "restorm/model/relation"
6
+ require "restorm/model/orm"
7
+ require "restorm/model/parse"
8
+ require "restorm/model/associations"
9
+ require "restorm/model/introspection"
10
+ require "restorm/model/paths"
11
+ require "restorm/model/nested_attributes"
12
+ require "active_model"
13
+
14
+ module Restorm
15
+ # This module is the main element of Restorm. After creating a Restorm::API object,
16
+ # include this module in your models to get a few magic methods defined in them.
17
+ #
18
+ # @example
19
+ # class User
20
+ # include Restorm::Model
21
+ # end
22
+ #
23
+ # @user = User.new(:name => "Rémi")
24
+ # @user.save
25
+ module Model
26
+ extend ActiveSupport::Concern
27
+
28
+ # Restorm modules
29
+ include Restorm::Model::Base
30
+ include Restorm::Model::DeprecatedMethods
31
+ include Restorm::Model::Attributes
32
+ include Restorm::Model::ORM
33
+ include Restorm::Model::HTTP
34
+ include Restorm::Model::Parse
35
+ include Restorm::Model::Introspection
36
+ include Restorm::Model::Paths
37
+ include Restorm::Model::Associations
38
+ include Restorm::Model::NestedAttributes
39
+
40
+ # Supported ActiveModel modules
41
+ include ActiveModel::AttributeMethods
42
+ include ActiveModel::Validations
43
+ include ActiveModel::Validations::Callbacks
44
+ include ActiveModel::Conversion
45
+ include ActiveModel::Dirty
46
+
47
+ # Class methods
48
+ included do
49
+ # Assign the default API
50
+ use_api Restorm::API.default_api
51
+ method_for :create, :post
52
+ method_for :update, :put
53
+ method_for :find, :get
54
+ method_for :destroy, :delete
55
+ method_for :new, :get
56
+
57
+ # Define the default primary key
58
+ primary_key :id
59
+
60
+ # Define default storage accessors for errors and metadata
61
+ store_response_errors :response_errors
62
+ store_metadata :metadata
63
+
64
+ # Include ActiveModel naming methods
65
+ extend ActiveModel::Translation
66
+
67
+ # Configure ActiveModel callbacks
68
+ extend ActiveModel::Callbacks
69
+ define_model_callbacks :create, :update, :save, :find, :destroy, :initialize
70
+
71
+ # Define matchers for attr? and attr= methods
72
+ define_attribute_method_matchers
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module Restorm
2
+ VERSION = "1.0.0"
3
+ end
data/lib/restorm.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "restorm/version"
2
+
3
+ require "multi_json"
4
+ require "faraday"
5
+ require "active_support"
6
+ require "active_support/inflector"
7
+ require "active_support/core_ext/hash"
8
+
9
+ require "restorm/model"
10
+ require "restorm/api"
11
+ require "restorm/middleware"
12
+ require "restorm/errors"
13
+ require "restorm/collection"
14
+
15
+ module Restorm
16
+ module JsonApi
17
+ autoload :Model, 'restorm/json_api/model'
18
+ end
19
+ end
data/restorm.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+ require "restorm/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "restorm"
8
+ s.version = Restorm::VERSION
9
+ s.authors = ["Rémi Prévost", "Kates Gasis"]
10
+ s.email = ["katesgasis@gmail.com"]
11
+ s.homepage = "https://github.com/kates/restorm"
12
+ s.license = "MIT"
13
+ s.summary = "A simple Representational State Transfer-based Hypertext Transfer Protocol-powered Object Relational Mapper."
14
+ s.description = "Restorm is an ORM that maps REST resources and collections to Ruby objects"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "json", "~> 2.6.3"
22
+ s.add_development_dependency "rake", "~> 13.0.6"
23
+ s.add_development_dependency "rspec", "~> 3.12"
24
+
25
+ s.add_runtime_dependency "activemodel", ">= 4.2.1"
26
+ s.add_runtime_dependency "faraday", "~> 2.7.4"
27
+
28
+ s.add_runtime_dependency "multi_json", "~> 1.15.0"
29
+ end
data/spec/api_spec.rb ADDED
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
4
+
5
+ describe Restorm::API do
6
+ subject { Restorm::API.new }
7
+
8
+ context "initialization" do
9
+ describe "#setup" do
10
+ context "when setting custom middleware" do
11
+ before do
12
+ class Foo; end
13
+ class Bar; end
14
+
15
+ subject.setup url: "https://api.example.com" do |connection|
16
+ connection.use Foo
17
+ connection.use Bar
18
+ end
19
+ end
20
+
21
+ specify { expect(subject.connection.builder.handlers).to eq([Foo, Bar]) }
22
+ end
23
+
24
+ context "when setting custom options" do
25
+ before { subject.setup foo: { bar: "baz" }, url: "https://api.example.com" }
26
+
27
+ describe "#options" do
28
+ it { expect(subject.options).to eq(foo: { bar: "baz" }, url: "https://api.example.com") }
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "#request" do
34
+ before do
35
+ class SimpleParser < Faraday::Middleware
36
+
37
+ def on_complete(env)
38
+ env[:body] = { data: env[:body] }
39
+ end
40
+ end
41
+ end
42
+
43
+ context "making HTTP requests" do
44
+ let(:parsed_data) { subject.request(_method: :get, _path: "/foo")[:parsed_data] }
45
+ before do
46
+ subject.setup url: "https://api.example.com" do |builder|
47
+ builder.use SimpleParser
48
+ builder.adapter(:test) { |stub| stub.get("/foo") { [200, {}, "Foo, it is."] } }
49
+ end
50
+ end
51
+
52
+ specify { expect(parsed_data[:data]).to eq("Foo, it is.") }
53
+ end
54
+
55
+ context "making HTTP requests while specifying custom HTTP headers" do
56
+ let(:parsed_data) { subject.request(_method: :get, _path: "/foo", _headers: { "X-Page" => 2 })[:parsed_data] }
57
+
58
+ before do
59
+ subject.setup url: "https://api.example.com" do |builder|
60
+ builder.use SimpleParser
61
+ builder.adapter(:test) { |stub| stub.get("/foo") { |env| [200, {}, "Foo, it is page #{env[:request_headers]['X-Page']}."] } }
62
+ end
63
+ end
64
+
65
+ specify { expect(parsed_data[:data]).to eq("Foo, it is page 2.") }
66
+ end
67
+
68
+ context "parsing a request with the default parser" do
69
+ let(:parsed_data) { subject.request(_method: :get, _path: "users/1")[:parsed_data] }
70
+ before do
71
+ subject.setup url: "https://api.example.com" do |builder|
72
+ builder.use Restorm::Middleware::FirstLevelParseJSON
73
+ builder.adapter :test do |stub|
74
+ stub.get("/users/1") { [200, {}, MultiJson.dump(id: 1, name: "George Michael Bluth", errors: ["This is a single error"], metadata: { page: 1, per_page: 10 })] }
75
+ end
76
+ end
77
+ end
78
+
79
+ specify do
80
+ expect(parsed_data[:data]).to eq(id: 1, name: "George Michael Bluth")
81
+ expect(parsed_data[:errors]).to eq(["This is a single error"])
82
+ expect(parsed_data[:metadata]).to eq(page: 1, per_page: 10)
83
+ end
84
+ end
85
+
86
+ context "parsing a request with a custom parser" do
87
+ let(:parsed_data) { subject.request(_method: :get, _path: "users/1")[:parsed_data] }
88
+ before do
89
+ class CustomParser < Faraday::Middleware
90
+
91
+ def on_complete(env)
92
+ json = MultiJson.load(env[:body], symbolize_keys: true)
93
+ errors = json.delete(:errors) || []
94
+ metadata = json.delete(:metadata) || {}
95
+ env[:body] = {
96
+ data: json,
97
+ errors: errors,
98
+ metadata: metadata
99
+ }
100
+ end
101
+ end
102
+
103
+ subject.setup url: "https://api.example.com" do |builder|
104
+ builder.use CustomParser
105
+ builder.use Faraday::Request::UrlEncoded
106
+ builder.adapter :test do |stub|
107
+ stub.get("/users/1") { [200, {}, MultiJson.dump(id: 1, name: "George Michael Bluth")] }
108
+ end
109
+ end
110
+ end
111
+
112
+ specify do
113
+ expect(parsed_data[:data]).to eq(id: 1, name: "George Michael Bluth")
114
+ expect(parsed_data[:errors]).to eq([])
115
+ expect(parsed_data[:metadata]).to eq({})
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe Restorm::Collection do
4
+ let(:items) { [1, 2, 3, 4] }
5
+ let(:metadata) { { name: "Testname" } }
6
+ let(:errors) { { name: ["not_present"] } }
7
+
8
+ describe "#new" do
9
+ context "without parameters" do
10
+ subject { Restorm::Collection.new }
11
+
12
+ it { is_expected.to eq([]) }
13
+
14
+ describe "#metadata" do
15
+ subject { super().metadata }
16
+ it { is_expected.to eq({}) }
17
+ end
18
+
19
+ describe "#errors" do
20
+ subject { super().errors }
21
+ it { is_expected.to eq({}) }
22
+ end
23
+ end
24
+
25
+ context "with parameters" do
26
+ subject { Restorm::Collection.new(items, metadata, errors) }
27
+
28
+ it { is_expected.to eq([1, 2, 3, 4]) }
29
+
30
+ describe "#metadata" do
31
+ subject { super().metadata }
32
+ it { is_expected.to eq(name: "Testname") }
33
+ end
34
+
35
+ describe "#errors" do
36
+ subject { super().errors }
37
+ it { is_expected.to eq(name: ["not_present"]) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,169 @@
1
+ require "spec_helper"
2
+
3
+ describe Restorm::JsonApi::Model do
4
+ before do
5
+ Restorm::API.setup url: "https://api.example.com" do |connection|
6
+ connection.use Restorm::Middleware::JsonApiParser
7
+ connection.adapter :test do |stub|
8
+ stub.get("/users/1") do
9
+ [
10
+ 200,
11
+ {},
12
+ {
13
+ data: {
14
+ id: 1,
15
+ type: "users",
16
+ attributes: {
17
+ name: "Roger Federer"
18
+ }
19
+ }
20
+
21
+ }.to_json
22
+ ]
23
+ end
24
+
25
+ stub.get("/users") do
26
+ [
27
+ 200,
28
+ {},
29
+ {
30
+ data: [
31
+ {
32
+ id: 1,
33
+ type: "users",
34
+ attributes: {
35
+ name: "Roger Federer"
36
+ }
37
+ },
38
+ {
39
+ id: 2,
40
+ type: "users",
41
+ attributes: {
42
+ name: "Kei Nishikori"
43
+ }
44
+ }
45
+ ]
46
+ }.to_json
47
+ ]
48
+ end
49
+
50
+ stub.post("/users", data:
51
+ {
52
+ type: "users",
53
+ attributes: {
54
+ name: "Jeremy Lin"
55
+ }
56
+ }) do
57
+ [
58
+ 201,
59
+ {},
60
+ {
61
+ data: {
62
+ id: 3,
63
+ type: "users",
64
+ attributes: {
65
+ name: "Jeremy Lin"
66
+ }
67
+ }
68
+
69
+ }.to_json
70
+ ]
71
+ end
72
+
73
+ stub.patch("/users/1", data:
74
+ {
75
+ type: "users",
76
+ id: 1,
77
+ attributes: {
78
+ name: "Fed GOAT"
79
+ }
80
+ }) do
81
+ [
82
+ 200,
83
+ {},
84
+ {
85
+ data: {
86
+ id: 1,
87
+ type: "users",
88
+ attributes: {
89
+ name: "Fed GOAT"
90
+ }
91
+ }
92
+
93
+ }.to_json
94
+ ]
95
+ end
96
+
97
+ stub.delete("/users/1") do
98
+ [204, {}, {}]
99
+ end
100
+ end
101
+ end
102
+
103
+ spawn_model("Foo::User", type: Restorm::JsonApi::Model)
104
+ end
105
+
106
+ it "allows configuration of type" do
107
+ spawn_model("Foo::Bar", type: Restorm::JsonApi::Model) do
108
+ type :foobars
109
+ end
110
+
111
+ expect(Foo::Bar.instance_variable_get("@type")).to eql("foobars")
112
+ end
113
+
114
+ it "finds models by id" do
115
+ user = Foo::User.find(1)
116
+ expect(user.attributes).to eql(
117
+ "id" => 1,
118
+ "name" => "Roger Federer"
119
+ )
120
+ end
121
+
122
+ it "finds a collection of models" do
123
+ users = Foo::User.all
124
+ expect(users.map(&:attributes)).to match_array(
125
+ [
126
+ {
127
+ "id" => 1,
128
+ "name" => "Roger Federer"
129
+ },
130
+ {
131
+ "id" => 2,
132
+ "name" => "Kei Nishikori"
133
+ }
134
+ ]
135
+ )
136
+ end
137
+
138
+ it "creates a Foo::User" do
139
+ user = Foo::User.new(name: "Jeremy Lin")
140
+ user.save
141
+ expect(user.attributes).to eql(
142
+ "id" => 3,
143
+ "name" => "Jeremy Lin"
144
+ )
145
+ end
146
+
147
+ it "updates a Foo::User" do
148
+ user = Foo::User.find(1)
149
+ user.name = "Fed GOAT"
150
+ user.save
151
+ expect(user.attributes).to eql(
152
+ "id" => 1,
153
+ "name" => "Fed GOAT"
154
+ )
155
+ end
156
+
157
+ it "destroys a Foo::User" do
158
+ user = Foo::User.find(1)
159
+ expect(user.destroy).to be_destroyed
160
+ end
161
+
162
+ context "undefined methods" do
163
+ it "removes methods that are not compatible with json api" do
164
+ [:parse_root_in_json, :include_root_in_json, :root_element, :primary_key].each do |method|
165
+ expect { Foo::User.new.send(method, :foo) }.to raise_error NoMethodError, "Restorm::JsonApi::Model does not support the #{method} configuration option"
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Restorm::Middleware::AcceptJSON do
6
+ it "adds an Accept header" do
7
+ described_class.new.add_header({}).tap do |headers|
8
+ expect(headers["Accept"]).to eq("application/json")
9
+ end
10
+ end
11
+ end