arkenstone-open 3.0.1

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 (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