arkenstone-open 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/codacy-analysis.yml +46 -0
  3. data/.gitignore +19 -0
  4. data/.rubocop.yml +5 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +16 -0
  7. data/Gemfile +21 -0
  8. data/Gemfile.lock +101 -0
  9. data/LICENSE.txt +11 -0
  10. data/README.md +87 -0
  11. data/Rakefile +36 -0
  12. data/arkenstone.gemspec +27 -0
  13. data/lib/arkenstone/associations/resources.rb +76 -0
  14. data/lib/arkenstone/associations.rb +389 -0
  15. data/lib/arkenstone/document.rb +289 -0
  16. data/lib/arkenstone/enumerable/query_list.rb +20 -0
  17. data/lib/arkenstone/errors/no_url_error.rb +14 -0
  18. data/lib/arkenstone/helpers.rb +18 -0
  19. data/lib/arkenstone/network/env.rb +19 -0
  20. data/lib/arkenstone/network/hook.rb +74 -0
  21. data/lib/arkenstone/network/network.rb +72 -0
  22. data/lib/arkenstone/query_builder.rb +84 -0
  23. data/lib/arkenstone/queryable.rb +38 -0
  24. data/lib/arkenstone/timestamps.rb +25 -0
  25. data/lib/arkenstone/validation/validation_error.rb +34 -0
  26. data/lib/arkenstone/validation/validations.rb +192 -0
  27. data/lib/arkenstone/version.rb +5 -0
  28. data/lib/arkenstone.rb +22 -0
  29. data/test/associations/test_document_overrides.rb +50 -0
  30. data/test/associations/test_has_and_belongs_to_many.rb +80 -0
  31. data/test/dummy/app/models/association.rb +35 -0
  32. data/test/dummy/app/models/superuser.rb +8 -0
  33. data/test/dummy/app/models/user.rb +10 -0
  34. data/test/spec_helper.rb +16 -0
  35. data/test/test_arkenstone.rb +354 -0
  36. data/test/test_arkenstone_hook_inheritance.rb +39 -0
  37. data/test/test_associations.rb +327 -0
  38. data/test/test_enumerables.rb +36 -0
  39. data/test/test_environment.rb +14 -0
  40. data/test/test_helpers.rb +18 -0
  41. data/test/test_hooks.rb +104 -0
  42. data/test/test_query_builder.rb +163 -0
  43. data/test/test_queryable.rb +59 -0
  44. data/test/test_validations.rb +197 -0
  45. metadata +133 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class HasAndBelongsToManyTest < Test::Unit::TestCase
6
+ def test_has_and_belongs_to_many
7
+ eval %(
8
+ module BrewMaster
9
+ class Tag
10
+ include Arkenstone::Document
11
+ include Arkenstone::Queryable
12
+ url 'http://example.com/tag'
13
+
14
+ attributes :id, :name
15
+ has_and_belongs_to_many :beers
16
+ end
17
+
18
+ class Beer
19
+ include Arkenstone::Document
20
+ include Arkenstone::Queryable
21
+ url 'http://example.com/beer'
22
+
23
+ attributes :id, :brand, :filtered
24
+ has_and_belongs_to_many :tags
25
+ end
26
+ end
27
+ )
28
+
29
+ assert(BrewMaster::BeerTag)
30
+ %w[beer beer= beer_id beer_id= cached_beer tag tag= tag_id tag_id= cached_tag].each do |injected_method|
31
+ assert(BrewMaster::BeerTag.new.respond_to?(injected_method.to_sym))
32
+ end
33
+
34
+ assert(BrewMaster::Tag.new.respond_to?(:beer_tags))
35
+ assert(BrewMaster::Beer.new.respond_to?(:beer_tags))
36
+
37
+ stub_request(:post, "#{BrewMaster::Tag.arkenstone_url}/").to_return(status: '200', body: { id: 1 }.to_json)
38
+ stub_request(:post, "#{BrewMaster::Beer.arkenstone_url}/").to_return(status: '200', body: { id: 1 }.to_json)
39
+
40
+ %w[blonde pale ipa hot sour].each do |tag|
41
+ BrewMaster::Tag.create(name: tag)
42
+ end
43
+
44
+ beer = BrewMaster::Beer.create(brand: 'Full Sail', filtered: false)
45
+
46
+ stub_request(:post,
47
+ "#{BrewMaster::Tag.arkenstone_url}/query").with(body: { name: 'ipa' }.to_json).to_return(body: [{ id: 1,
48
+ name: 'ipa' }].to_json)
49
+
50
+ tag = BrewMaster::Tag.where(name: 'ipa').first
51
+
52
+ stub_request(:get, "#{BrewMaster::Tag.arkenstone_url}/1/beer_tags").to_return(status: 200, body: [].to_json)
53
+ stub_request(:get, "#{BrewMaster::Beer.arkenstone_url}/1/beer_tags").to_return(status: 200, body: [].to_json)
54
+
55
+ assert(beer.respond_to?(:tags))
56
+ assert(tag.respond_to?(:beers))
57
+
58
+ assert(beer.tags.empty?)
59
+ assert(tag.beers.empty?)
60
+
61
+ beer.tags << tag
62
+ assert(beer.tags.include?(tag))
63
+ assert(tag.beers.include?(beer))
64
+
65
+ stub_request(:put, "#{BrewMaster::Tag.arkenstone_url}/1").to_return(status: '200', body: { id: 1 }.to_json)
66
+ stub_request(:put, "#{BrewMaster::Beer.arkenstone_url}/1").to_return(status: '200', body: { id: 1 }.to_json)
67
+
68
+ beer.save
69
+ tag.save
70
+
71
+ stub_request(:get, "#{BrewMaster::Tag.arkenstone_url}/1").to_return(status: 200, body: { id: 1 }.to_json)
72
+ stub_request(:get, "#{BrewMaster::Beer.arkenstone_url}/1").to_return(status: 200, body: { id: 1 }.to_json)
73
+
74
+ beer.reload
75
+ tag.reload
76
+
77
+ assert(beer.tags.include?(tag))
78
+ assert(tag.beers.include?(beer))
79
+ end
80
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'user'
4
+
5
+ class Thing
6
+ include Arkenstone::Document
7
+ include Arkenstone::Timestamps
8
+
9
+ url 'http://example.com/things'
10
+ attributes :id, :name
11
+ end
12
+
13
+ class Role
14
+ include Arkenstone::Document
15
+ include Arkenstone::Timestamps
16
+ attributes :id, :name
17
+ end
18
+
19
+ class Resource
20
+ include Arkenstone::Document
21
+ include Arkenstone::Timestamps
22
+ attributes :id, :name
23
+ end
24
+
25
+ class AssociatedUser < User
26
+ url 'http://example.com/associated_users/'
27
+
28
+ has_many :things
29
+ has_many :roles
30
+ has_one :resource
31
+
32
+ def test_method
33
+ 'on AssociatedUser'
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'user'
4
+
5
+ class SuperUser < User
6
+ url 'http://example.com/superusers'
7
+ attributes :group_name
8
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class User
4
+ include Arkenstone::Document
5
+ include Arkenstone::Timestamps
6
+ include Arkenstone::Queryable
7
+
8
+ url 'http://example.com/users/'
9
+ attributes :name, :age, :gender, :bearded
10
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'webmock/test_unit'
5
+
6
+ # Add in some pry debuggin niceties
7
+ require 'pry'
8
+ require 'pry-nav'
9
+
10
+ # Include code coverage
11
+ require 'simplecov'
12
+ SimpleCov.start
13
+
14
+ # Make a simple document for test cases.
15
+ require_relative '../lib/arkenstone'
16
+ Dir['./test/dummy/app/models/**/*.rb'].each { |f| require f }
@@ -0,0 +1,354 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class DummyRequest
6
+ attr_accessor :body
7
+ end
8
+
9
+ class ArkenstoneTest < Test::Unit::TestCase
10
+ def test_arkenstone_url_set
11
+ eval %(
12
+ class ArkenstoneUrlTest
13
+ include Arkenstone::Document
14
+ url 'http://example.com'
15
+ end
16
+ )
17
+
18
+ assert(ArkenstoneUrlTest)
19
+ assert(ArkenstoneUrlTest.arkenstone_url == 'http://example.com')
20
+ end
21
+
22
+ def test_arkenstone_attributes_set
23
+ eval %(
24
+ class ArkenstoneAttributesTest
25
+ include Arkenstone::Document
26
+ attributes :name, :age
27
+ end
28
+ )
29
+ arkenstone = ArkenstoneAttributesTest.new
30
+
31
+ assert(ArkenstoneAttributesTest.arkenstone_attributes == %i[name age])
32
+ assert(arkenstone.respond_to?(:name))
33
+ assert(arkenstone.respond_to?(:age))
34
+ end
35
+
36
+ def test_new_record
37
+ user = User.build user_options
38
+ assert_equal true, user.new_record?
39
+ user.id = 100
40
+ assert_equal false, user.new_record?
41
+ end
42
+
43
+ def test_builds_from_params
44
+ user = User.build(user_options)
45
+ assert(user.instance_of?(User), 'user class was not User')
46
+ assert(user.age == 18, "user's age was not 18")
47
+ end
48
+
49
+ def tests_builds_from_nil_params
50
+ user = User.build(nil)
51
+ assert(user.instance_of?(User), 'user class was not User')
52
+ end
53
+
54
+ def test_returns_json
55
+ user = User.build(user_options)
56
+ json = user.to_json
57
+ assert(json, 'user#to_json method does not exist')
58
+ parsed = JSON.parse json
59
+ assert(parsed['name'] == user.name)
60
+ end
61
+
62
+ def test_to_json_accepts_options
63
+ # This case was added to give backwards compatibility to rails render_json
64
+ user = User.build(user_options)
65
+ assert(user.to_json, 'user#to_json fails without options')
66
+ assert(user.to_json(key: 'value'), 'user#to_json fails with options')
67
+ end
68
+
69
+ def test_attribute_changes_updates_json
70
+ user = User.build(user_options)
71
+ old_json = user.to_json
72
+ user.bearded = false
73
+ assert(user.to_json != old_json)
74
+ assert(user.to_json == user_options.merge(bearded: false).to_json)
75
+ end
76
+
77
+ def test_finds_instance_by_id
78
+ user_json = user_options.merge({ id: 1 }).to_json
79
+ stub_request(:get, "#{User.arkenstone_url}1").to_return(body: user_json)
80
+ user = User.find(1)
81
+ assert user
82
+ assert user.id == 1
83
+ end
84
+
85
+ def test_instance_not_found_is_nil
86
+ stub_request(:any, "#{User.arkenstone_url}1").to_return(body: '', status: 404)
87
+ user = User.find 1
88
+ assert_nil user
89
+ end
90
+
91
+ def test_finds_all_instances
92
+ users_json = [
93
+ user_options.merge({ id: 1 }),
94
+ user_options.merge({ id: 2 })
95
+ ].to_json
96
+
97
+ stub_request(:get, User.arkenstone_url).to_return(body: users_json)
98
+ users = User.all
99
+ assert(users.length == 2)
100
+ assert((users.select { |user| user.id == 1 }).length == 1)
101
+ assert((users.select { |user| user.id == 2 }).length == 1)
102
+ end
103
+
104
+ def test_save_new_record
105
+ user = User.build user_options
106
+ assert(!user.id, 'user has an id')
107
+
108
+ stub_request(:post, User.arkenstone_url).to_return(body: user_options.merge({ id: 1 }).to_json)
109
+ assert_equal(true, user.save)
110
+ assert(user.id == 1, 'user does not have an id')
111
+ end
112
+
113
+ def test_save_is_false_for_server_error
114
+ user = User.build user_options
115
+ stub_request(:post, User.arkenstone_url).to_return(body: { message: 'oh no' }.to_json, status: 500)
116
+ assert_equal(false, user.save)
117
+ end
118
+
119
+ def test_dont_set_readonly_attribute
120
+ eval %(
121
+ class ReadOnlyAttrs
122
+ include Arkenstone::Document
123
+ attributes :name
124
+
125
+ def readonly
126
+ 'oh hi'
127
+ end
128
+ end
129
+ )
130
+ obj = ReadOnlyAttrs.new
131
+ vals = { name: 'my name', readonly: 'uh oh' }
132
+ obj.attributes = vals
133
+ assert_equal('oh hi', obj.readonly)
134
+ end
135
+
136
+ def test_save_current_record
137
+ user = User.build user_options.merge({ id: 1, bearded: false })
138
+ assert(user.bearded != true)
139
+
140
+ stub_request(:put,
141
+ "#{User.arkenstone_url}#{user.id}").to_return(body: user_options.merge({ id: 1,
142
+ bearded: true }).to_json)
143
+ user.bearded = true
144
+ user.save
145
+
146
+ assert(user.bearded == true)
147
+
148
+ stub_request(:get,
149
+ "#{User.arkenstone_url}#{user.id}").to_return(body: user_options.merge({ id: 1,
150
+ bearded: true }).to_json)
151
+ user = User.find(user.id)
152
+ assert(user.bearded == true)
153
+ end
154
+
155
+ def test_save_throws_an_error_for_no_url
156
+ eval %(
157
+ class NoUrlModel
158
+ include Arkenstone::Document
159
+ attributes :name
160
+ end
161
+ )
162
+ model = NoUrlModel.new
163
+ model.name = 'No Save'
164
+ assert_raise NoUrlError do
165
+ model.save
166
+ end
167
+ end
168
+
169
+ def test_creates
170
+ stub_request(:post, User.arkenstone_url).to_return(body: user_options.merge({ id: 1 }).to_json)
171
+
172
+ user = User.create(user_options)
173
+ assert(user.age == 18, "user's age was not 18")
174
+ assert(user.id == 1, "user doesn't have an id")
175
+ assert(user.created_at, 'created_at is nil')
176
+ assert(user.updated_at, 'updated_at is nil')
177
+ end
178
+
179
+ def test_destroys
180
+ user = build_user 1
181
+ stub_request(:delete, "#{User.arkenstone_url}#{user.id}").to_return(status: 200)
182
+ result = user.destroy
183
+ assert(result == true, 'delete was not true')
184
+ end
185
+
186
+ def test_update_attributes
187
+ stub_request(:post, User.arkenstone_url).to_return(body: user_options.merge({ id: 1 }).to_json)
188
+
189
+ user = User.create(user_options)
190
+
191
+ stub_request(:put,
192
+ "#{User.arkenstone_url}#{user.id}").to_return(body: user_options.merge({ id: 1, name: 'Jack Doe',
193
+ age: 24 }).to_json)
194
+
195
+ result = user.update_attributes({ name: 'Jack Doe', age: 24 })
196
+ assert(result != false)
197
+ assert(user.name == 'Jack Doe', 'user#name is not eq Jack Doe')
198
+ assert(user.age == 24, 'user#age is not eq 24')
199
+ end
200
+
201
+ def test_set_request_data_uses_json
202
+ user = build_user 1
203
+ request = Net::HTTP::Post.new 'http://localhost'
204
+ Arkenstone::Network.set_request_data request, user.attributes
205
+ assert(request.content_type == 'application/json')
206
+ assert(request.body == '{"name":"John Doe","age":18,"gender":"Male","bearded":true}')
207
+ end
208
+
209
+ def test_set_request_data_double_json
210
+ request = Net::HTTP::Post.new 'http://localhost'
211
+ Arkenstone::Network.set_request_data request, { name: 'test' }.to_json
212
+ assert(request.body == '{"name":"test"}')
213
+ end
214
+
215
+ def test_set_request_headers_sets_headers
216
+ request = Net::HTTP::Post.new 'http://localhost'
217
+ headers = { 'Test' => 'foo', 'Other' => 'other' }
218
+ Arkenstone::Network.set_request_headers request, headers
219
+ assert(request['Test'] == 'foo')
220
+ assert(request['Other'] == 'other')
221
+ end
222
+
223
+ def test_update_attribute
224
+ stub_request(:post, User.arkenstone_url).to_return(body: user_options.merge({ id: 1 }).to_json)
225
+
226
+ user = User.create(user_options)
227
+
228
+ stub_request(:put,
229
+ "#{User.arkenstone_url}#{user.id}").to_return(body: user_options.merge({ id: 1,
230
+ name: 'Jack Doe' }).to_json)
231
+
232
+ user.update_attribute 'name', 'Jack Doe'
233
+ assert(user.name == 'Jack Doe', 'Jack doe is not alive')
234
+
235
+ stub_request(:put,
236
+ "#{User.arkenstone_url}#{user.id}").to_return(body: user_options.merge({ id: 1,
237
+ name: 'Jacked Doe' }).to_json)
238
+ user.update_attribute :name, 'Jacked Doe'
239
+ assert(user.name == 'Jacked Doe', 'Jack is not Jacked')
240
+ end
241
+
242
+ def test_inheritance
243
+ su = SuperUser.build({ group_name: 'some group' })
244
+ assert(su.attributes == { group_name: 'some group' })
245
+ assert(SuperUser.arkenstone_url == 'http://example.com/superusers')
246
+ end
247
+
248
+ def test_save_attributes
249
+ eval %(
250
+ class ArkenstoneAttrHook < Arkenstone::Hook
251
+ attr_accessor :called
252
+ def encode_attributes(attrs)
253
+ @called = true
254
+ attrs
255
+ end
256
+ end
257
+ )
258
+ hook = ArkenstoneAttrHook.new
259
+ User.add_hook hook
260
+ u = User.new
261
+ u.saveable_attributes
262
+ assert_equal(true, hook.called)
263
+ end
264
+
265
+ def test_parse_all_builds_array
266
+ json = '[{ "id": 100, "name": "test" }, { "id": 200, "name": "built" }]'
267
+ result = User.parse_all json
268
+ assert(result.count == 2)
269
+ end
270
+
271
+ def test_parse_all_handles_object
272
+ obj = [{ id: 100, name: 'test' }, { id: 200, name: 'built' }]
273
+ result = User.parse_all obj
274
+ assert(result.count == 2)
275
+ end
276
+
277
+ def test_parse_all_treats_single_object_as_array
278
+ obj = { id: 100, name: 'test' }
279
+ result = User.parse_all obj.to_json
280
+ assert_equal(1, result.count)
281
+ assert_equal('test', result.first.name)
282
+ end
283
+
284
+ def test_parse_all_catches_empty_json
285
+ result = User.parse_all ''
286
+ assert(result == [])
287
+ end
288
+
289
+ def test_handle_error_in_save
290
+ eval %(
291
+ class Rock
292
+ include Arkenstone::Document
293
+
294
+ url 'http://example.com/rocks'
295
+ attributes :name
296
+ end
297
+ )
298
+ stub_request(:post, "#{Rock.arkenstone_url}/").to_return(status: 500, body: { error: 'derp' }.to_json)
299
+ rock = Rock.create(name: 'err')
300
+ assert_equal(false, rock.arkenstone_server_errors.nil?)
301
+ end
302
+
303
+ def test_dup
304
+ u = User.build user_options
305
+ u.id = 1337
306
+ dup_u = u.dup
307
+ dup_u.attributes.each { |key, value| assert_equal value, u.attributes[key] }
308
+ assert_equal nil, dup_u.id
309
+ end
310
+
311
+ def test_reload
312
+ eval %(
313
+ class Ball
314
+ include Arkenstone::Document
315
+
316
+ url 'http://example.com/balls'
317
+ attributes :color
318
+ end
319
+ )
320
+
321
+ stub_request(:post, "#{Ball.arkenstone_url}/").to_return(status: '200', body: { id: 1, color: 'blue' }.to_json)
322
+
323
+ ball = Ball.create(color: 'blue')
324
+ assert(ball.color == 'blue')
325
+
326
+ ball.color = 'orange'
327
+ assert(ball.color == 'orange')
328
+
329
+ stub_request(:put, "#{Ball.arkenstone_url}/1").to_return(status: '200', body: { id: 1, color: 'orange' }.to_json)
330
+
331
+ ball.save
332
+
333
+ stub_request(:get, "#{Ball.arkenstone_url}/1").to_return(status: 200, body: { id: 1, color: 'orange' }.to_json)
334
+
335
+ ball.reload
336
+ assert(ball.color == 'orange')
337
+
338
+ found_ball = Ball.find(ball.id)
339
+ assert(found_ball.color == 'orange')
340
+ end
341
+ end
342
+
343
+ def build_user(id)
344
+ User.build user_options.merge({ id: })
345
+ end
346
+
347
+ def create_user(options, id)
348
+ stub_request(:post, User.arkenstone_url).to_return(body: options.merge({ id: }).to_json)
349
+ User.build(options).save
350
+ end
351
+
352
+ def user_options(options = {})
353
+ { name: 'John Doe', age: 18, gender: 'Male', bearded: true }.merge!(options)
354
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class TestHook < Arkenstone::Hook
6
+ attr_accessor :called
7
+
8
+ def before_request(_req)
9
+ @called = true
10
+ end
11
+ end
12
+
13
+ class ArkenstoneHookInheritanceTest < Test::Unit::TestCase
14
+ def test_hooks_do_inheritance
15
+ eval %(
16
+ class BaseModel
17
+ include Arkenstone::Document
18
+ end
19
+
20
+ class ChildModel < BaseModel
21
+ inherit_hooks
22
+ end
23
+ )
24
+ hook = TestHook.new
25
+ BaseModel.add_hook hook
26
+ Arkenstone::Hook.call_request_hooks ChildModel, nil
27
+ assert(hook.called)
28
+ end
29
+
30
+ def test_inherit_hooks
31
+ eval %(
32
+ class ArkenstoneHookAttr
33
+ include Arkenstone::Document
34
+ inherit_hooks
35
+ end
36
+ )
37
+ assert(ArkenstoneHookAttr.arkenstone_inherit_hooks)
38
+ end
39
+ end