populate-me 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +655 -0
- data/Rakefile +14 -0
- data/example/config.ru +100 -0
- data/lib/populate_me.rb +2 -0
- data/lib/populate_me/admin.rb +157 -0
- data/lib/populate_me/admin/__assets__/css/asmselect.css +63 -0
- data/lib/populate_me/admin/__assets__/css/jquery-ui.min.css +6 -0
- data/lib/populate_me/admin/__assets__/css/main.css +244 -0
- data/lib/populate_me/admin/__assets__/img/help/children.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/create.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/delete.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/edit.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/form.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/list.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/login.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/logout.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/menu.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/overview.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/save.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sort.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sublist.png +0 -0
- data/lib/populate_me/admin/__assets__/js/asmselect.js +412 -0
- data/lib/populate_me/admin/__assets__/js/columnav.js +87 -0
- data/lib/populate_me/admin/__assets__/js/jquery-ui.min.js +7 -0
- data/lib/populate_me/admin/__assets__/js/main.js +388 -0
- data/lib/populate_me/admin/__assets__/js/mustache.js +578 -0
- data/lib/populate_me/admin/__assets__/js/sortable.js +2 -0
- data/lib/populate_me/admin/views/help.erb +94 -0
- data/lib/populate_me/admin/views/page.erb +189 -0
- data/lib/populate_me/api.rb +124 -0
- data/lib/populate_me/attachment.rb +186 -0
- data/lib/populate_me/document.rb +192 -0
- data/lib/populate_me/document_mixins/admin_adapter.rb +149 -0
- data/lib/populate_me/document_mixins/callbacks.rb +125 -0
- data/lib/populate_me/document_mixins/outcasting.rb +83 -0
- data/lib/populate_me/document_mixins/persistence.rb +95 -0
- data/lib/populate_me/document_mixins/schema.rb +198 -0
- data/lib/populate_me/document_mixins/typecasting.rb +70 -0
- data/lib/populate_me/document_mixins/validation.rb +44 -0
- data/lib/populate_me/file_system_attachment.rb +40 -0
- data/lib/populate_me/grid_fs_attachment.rb +103 -0
- data/lib/populate_me/mongo.rb +160 -0
- data/lib/populate_me/s3_attachment.rb +120 -0
- data/lib/populate_me/variation.rb +38 -0
- data/lib/populate_me/version.rb +4 -0
- data/populate-me.gemspec +34 -0
- data/test/helper.rb +37 -0
- data/test/test_admin.rb +183 -0
- data/test/test_api.rb +246 -0
- data/test/test_attachment.rb +167 -0
- data/test/test_document.rb +128 -0
- data/test/test_document_admin_adapter.rb +221 -0
- data/test/test_document_callbacks.rb +151 -0
- data/test/test_document_outcasting.rb +247 -0
- data/test/test_document_persistence.rb +83 -0
- data/test/test_document_schema.rb +280 -0
- data/test/test_document_typecasting.rb +128 -0
- data/test/test_grid_fs_attachment.rb +239 -0
- data/test/test_mongo.rb +324 -0
- data/test/test_s3_attachment.rb +281 -0
- data/test/test_variation.rb +91 -0
- data/test/test_version.rb +11 -0
- metadata +294 -0
data/populate-me.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib/populate_me/version')
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
|
5
|
+
s.authors = ["Mickael Riga"]
|
6
|
+
s.email = ["mig@mypeplum.com"]
|
7
|
+
s.homepage = "https://github.com/mig-hub/populate-me"
|
8
|
+
s.licenses = ['MIT']
|
9
|
+
|
10
|
+
s.name = 'populate-me'
|
11
|
+
s.version = PopulateMe::VERSION
|
12
|
+
s.summary = "PopulateMe is an admin system for web applications."
|
13
|
+
s.description = "PopulateMe is an admin system for managing structured content of web applications. It is built on top of the Sinatra framework, but can be used along any framework using Rack."
|
14
|
+
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.files = `git ls-files`.split("\n").sort
|
17
|
+
s.test_files = s.files.grep(/^test\//)
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.add_dependency 'web-utils', '~> 0'
|
21
|
+
s.add_dependency 'sinatra', '~> 2'
|
22
|
+
s.add_dependency 'json', '~> 2.1'
|
23
|
+
|
24
|
+
s.add_development_dependency 'bundler', '~> 1.13'
|
25
|
+
s.add_development_dependency 'minitest', '~> 5.8'
|
26
|
+
s.add_development_dependency 'rack-test', '~> 0.6'
|
27
|
+
s.add_development_dependency 'rack-cerberus', '~> 1.0'
|
28
|
+
s.add_development_dependency 'mongo', '~> 2.0'
|
29
|
+
s.add_development_dependency 'rack-grid-serve', '~> 0.0.8'
|
30
|
+
s.add_development_dependency 'aws-sdk-s3', '~> 1'
|
31
|
+
s.add_development_dependency 'racksh', '~> 1.0'
|
32
|
+
s.add_development_dependency 'rake', '>= 12.3.3'
|
33
|
+
end
|
34
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'rack/test'
|
5
|
+
|
6
|
+
module Minitest::Assertions
|
7
|
+
def assert_json response
|
8
|
+
assert response.content_type=="application/json", "Expected #{response.inspect} to be a JSON response"
|
9
|
+
end
|
10
|
+
def assert_for_view json, view_name, title=nil
|
11
|
+
assert json['template']==view_name, "Expected #{json.inspect} to have 'template' set to '#{view_name}'"
|
12
|
+
unless title.nil?
|
13
|
+
assert json['page_title']==title, "Expected #{json.inspect} to have 'page_title' set to '#{title}'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_receive obj, meth, retval=nil, args=[]
|
18
|
+
mocked_meth = Minitest::Mock.new
|
19
|
+
mocked_meth.expect(:call, retval, args)
|
20
|
+
obj.stub meth, mocked_meth do
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
assert mocked_meth.verify, "Expected #{obj.inspect} to receive :#{meth}"
|
24
|
+
end
|
25
|
+
def refute_receive obj, meth
|
26
|
+
proof = nil
|
27
|
+
obj.stub meth, proc{proof = :received} do
|
28
|
+
yield
|
29
|
+
assert(proof!=:received, "Expected #{obj.inspect} not to receive :#{meth}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Minitest::Spec
|
35
|
+
include Rack::Test::Methods
|
36
|
+
end
|
37
|
+
|
data/test/test_admin.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'populate_me/admin'
|
3
|
+
|
4
|
+
class Admin < PopulateMe::Admin
|
5
|
+
enable :sessions
|
6
|
+
set :menu, [
|
7
|
+
['Home Details', '/admin/form/home-details/0'],
|
8
|
+
['Project Page', [
|
9
|
+
['Project Page Intro', '/admin/form/project-page-intro/0'],
|
10
|
+
['Projects', '/admin/list/project'],
|
11
|
+
['Checks', [
|
12
|
+
['Check 1', '/check/1'],
|
13
|
+
['Check 2', '/check/2']
|
14
|
+
]]
|
15
|
+
]]
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
class AdminWithCerberusPass < Admin
|
20
|
+
def self.cerberus_pass
|
21
|
+
'123'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class AdminCerberusNotAvailable < AdminWithCerberusPass
|
26
|
+
def self.cerberus_available?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class AdminCerberusDisabled < AdminWithCerberusPass
|
32
|
+
disable :cerberus
|
33
|
+
end
|
34
|
+
|
35
|
+
describe PopulateMe::Admin do
|
36
|
+
|
37
|
+
parallelize_me!
|
38
|
+
|
39
|
+
let(:app) { ::Admin.new }
|
40
|
+
|
41
|
+
let(:settings) { app.settings }
|
42
|
+
|
43
|
+
let(:json) { JSON.parse(last_response.body) }
|
44
|
+
|
45
|
+
describe 'Settings' do
|
46
|
+
it 'Sets paths based on the subclass file path' do
|
47
|
+
assert_equal __FILE__, settings.app_file
|
48
|
+
end
|
49
|
+
it 'Has a default value for the page title tag' do
|
50
|
+
assert_equal 'Populate Me', settings.meta_title
|
51
|
+
end
|
52
|
+
it 'Has a default index_path' do
|
53
|
+
assert_equal '/menu', settings.index_path
|
54
|
+
end
|
55
|
+
it 'Has cerberus enabled by default' do
|
56
|
+
assert settings.cerberus?
|
57
|
+
end
|
58
|
+
describe 'when ENV CERBERUS_PASS is not set' do
|
59
|
+
it 'Does not have cerberus_active' do
|
60
|
+
refute settings.cerberus_active
|
61
|
+
end
|
62
|
+
end
|
63
|
+
describe 'when ENV CERBERUS_PASS is set' do
|
64
|
+
let(:app) { AdminWithCerberusPass.new }
|
65
|
+
it 'Has cerberus_active' do
|
66
|
+
assert settings.cerberus_active
|
67
|
+
end
|
68
|
+
end
|
69
|
+
describe 'when ENV CERBERUS_PASS is set but gem not loaded' do
|
70
|
+
let(:app) { AdminCerberusNotAvailable.new }
|
71
|
+
it 'Does not have cerberus_active' do
|
72
|
+
refute settings.cerberus_active
|
73
|
+
end
|
74
|
+
end
|
75
|
+
describe 'when ENV CERBERUS_PASS is set but cerberus is disabled' do
|
76
|
+
let(:app) { AdminCerberusDisabled.new }
|
77
|
+
it 'Does not have cerberus_active' do
|
78
|
+
refute settings.cerberus_active
|
79
|
+
end
|
80
|
+
end
|
81
|
+
describe 'when Cerberus is active' do
|
82
|
+
let(:app) { AdminWithCerberusPass.new }
|
83
|
+
it 'Sets logout_path to /logout' do
|
84
|
+
assert_equal '/logout', settings.logout_path
|
85
|
+
end
|
86
|
+
end
|
87
|
+
describe 'when Cerberus is not active' do
|
88
|
+
it 'Sets logout_path to false' do
|
89
|
+
refute settings.logout_path
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'Middlewares' do
|
95
|
+
|
96
|
+
it 'Has API middleware mounted on /api' do
|
97
|
+
get '/api'
|
98
|
+
assert_predicate last_response, :ok?
|
99
|
+
assert_json last_response
|
100
|
+
assert json['success']
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'Has assets available on /__assets__' do
|
104
|
+
get('/__assets__/css/main.css')
|
105
|
+
assert_predicate last_response, :ok?
|
106
|
+
assert_equal 'text/css', last_response.content_type
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when cerberus is active' do
|
110
|
+
let(:app) { AdminWithCerberusPass.new }
|
111
|
+
it 'Uses Cerberus for authentication' do
|
112
|
+
get '/'
|
113
|
+
assert_equal 401, last_response.status
|
114
|
+
end
|
115
|
+
end
|
116
|
+
describe 'when cerberus is inactive' do
|
117
|
+
it 'Does not use Cerberus' do
|
118
|
+
get '/'
|
119
|
+
assert_predicate last_response, :ok?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'Handlers' do
|
126
|
+
|
127
|
+
let(:help_item) {
|
128
|
+
{ 'title' => '?', 'href' => '/help', 'new_page' => false }
|
129
|
+
}
|
130
|
+
|
131
|
+
describe '/menu' do
|
132
|
+
|
133
|
+
describe 'when url is root' do
|
134
|
+
it 'Returns the correct info' do
|
135
|
+
get '/menu'
|
136
|
+
assert_predicate last_response, :ok?
|
137
|
+
assert_json last_response
|
138
|
+
assert_for_view json, 'template_menu', 'Menu'
|
139
|
+
expected_h = {
|
140
|
+
'title' => 'Home Details',
|
141
|
+
'href' => '/admin/form/home-details/0',
|
142
|
+
'new_page' => false
|
143
|
+
}
|
144
|
+
assert_equal expected_h, json['items'][0]
|
145
|
+
expected_h = {
|
146
|
+
'title' => 'Project Page',
|
147
|
+
'href' => '/menu/project-page',
|
148
|
+
'new_page' => false
|
149
|
+
}
|
150
|
+
assert_equal expected_h, json['items'][1]
|
151
|
+
end
|
152
|
+
it 'Adds help link' do
|
153
|
+
get '/menu'
|
154
|
+
assert_equal 3, json['items'].size
|
155
|
+
assert_equal(help_item, json['items'].last)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
describe 'when url is nested' do
|
159
|
+
it 'Returns the correct info' do
|
160
|
+
get '/menu/project-page/checks'
|
161
|
+
assert_predicate last_response, :ok?
|
162
|
+
assert_json last_response
|
163
|
+
assert_for_view json, 'template_menu', 'Checks'
|
164
|
+
assert_equal 2, json['items'].size
|
165
|
+
expected_h = {
|
166
|
+
'title' => 'Check 1',
|
167
|
+
'href' => '/check/1',
|
168
|
+
'new_page' => false
|
169
|
+
}
|
170
|
+
assert_equal expected_h, json['items'][0]
|
171
|
+
end
|
172
|
+
it 'Does not add help link' do
|
173
|
+
get '/menu/project-page/checks'
|
174
|
+
refute_equal(help_item, json['items'].last)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
data/test/test_api.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'populate_me/document'
|
4
|
+
class Band < PopulateMe::Document
|
5
|
+
attr_accessor :name, :awsome, :position
|
6
|
+
def members; @members ||= []; end
|
7
|
+
def validate
|
8
|
+
error_on(:name,"WTF") if self.name=='ZZ Top'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class Band::Member < PopulateMe::Document
|
12
|
+
attr_accessor :name
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'populate_me/api'
|
16
|
+
|
17
|
+
describe 'PopulateMe::API' do
|
18
|
+
|
19
|
+
# This middleware has the CRUD interface for
|
20
|
+
# managing documents through a JSON-based API
|
21
|
+
#
|
22
|
+
# The API needs the Document class to implement these methods:
|
23
|
+
# - Class.admin_get
|
24
|
+
# - Needs to be able to accept the ID as a string
|
25
|
+
# - The class is responsible for conversion
|
26
|
+
# - So a mix of different classes of IDs is not possible
|
27
|
+
# - instance.to_h
|
28
|
+
# - instance.save
|
29
|
+
# - instance.delete
|
30
|
+
|
31
|
+
parallelize_me!
|
32
|
+
|
33
|
+
let(:app) {
|
34
|
+
PopulateMe::API.new
|
35
|
+
}
|
36
|
+
|
37
|
+
let(:json) {
|
38
|
+
JSON.parse(last_response.body)
|
39
|
+
}
|
40
|
+
|
41
|
+
def assert_not_found
|
42
|
+
assert_json last_response
|
43
|
+
assert_equal 404, last_response.status
|
44
|
+
assert_equal 'pass', last_response.headers['X-Cascade']
|
45
|
+
refute json['success']
|
46
|
+
assert_equal 'Not Found', json['message']
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_successful_creation
|
50
|
+
assert_json last_response
|
51
|
+
assert_equal 201, last_response.status
|
52
|
+
assert json['success']
|
53
|
+
assert_equal 'Created Successfully', json['message']
|
54
|
+
end
|
55
|
+
|
56
|
+
def assert_successful_sorting
|
57
|
+
assert_json last_response
|
58
|
+
assert_predicate last_response, :ok?
|
59
|
+
assert json['success']
|
60
|
+
assert_equal 'Sorted Successfully', json['message']
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_invalid_instance
|
64
|
+
assert_json last_response
|
65
|
+
assert_equal 400, last_response.status
|
66
|
+
refute json['success']
|
67
|
+
assert_equal 'Invalid Document', json['message']
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_successful_instance
|
71
|
+
assert_json last_response
|
72
|
+
assert_predicate last_response, :ok?
|
73
|
+
assert json['success']
|
74
|
+
end
|
75
|
+
|
76
|
+
def assert_successful_update
|
77
|
+
assert_json last_response
|
78
|
+
assert_predicate last_response, :ok?
|
79
|
+
assert json['success']
|
80
|
+
assert_equal 'Updated Successfully', json['message']
|
81
|
+
end
|
82
|
+
|
83
|
+
def assert_successful_deletion
|
84
|
+
assert_json last_response
|
85
|
+
assert_predicate last_response, :ok?
|
86
|
+
assert json['success']
|
87
|
+
assert_equal 'Deleted Successfully', json['message']
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'GET /version' do
|
91
|
+
it 'Returns the PopulateMe version' do
|
92
|
+
get('/version')
|
93
|
+
assert_json last_response
|
94
|
+
assert_predicate last_response, :ok?
|
95
|
+
assert json['success']
|
96
|
+
assert_equal PopulateMe::VERSION, json['version']
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'POST /:model' do
|
101
|
+
|
102
|
+
it 'Creates successfully' do
|
103
|
+
post('/band', {data: {id: 'neurosis', name: 'Neurosis'}})
|
104
|
+
assert_successful_creation
|
105
|
+
assert_equal 'Neurosis', json['data']['name']
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'Typecasts before creating' do
|
109
|
+
post('/band', {data: {name: 'Arcade Fire', awsome: 'true'}})
|
110
|
+
assert_successful_creation
|
111
|
+
assert json['data']['awsome']
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'Can create a doc even if no data is sent' do
|
115
|
+
post '/band'
|
116
|
+
assert_successful_creation
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'Fails if the doc is invalid' do
|
120
|
+
post('/band', {data: {id: 'invalid_doc_post', name: 'ZZ Top'}})
|
121
|
+
assert_invalid_instance
|
122
|
+
assert_equal({'name'=>['WTF']}, json['data'])
|
123
|
+
assert_nil Band.admin_get('invalid_doc_post')
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'Redirects if destination is given' do
|
127
|
+
post '/band', {'_destination'=>'http://example.org/anywhere'}
|
128
|
+
assert_equal 302, last_response.status
|
129
|
+
assert_equal 'http://example.org/anywhere', last_response.header['Location']
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'PUT /:model' do
|
135
|
+
|
136
|
+
it 'Can set indexes for sorting' do
|
137
|
+
post('/band', {data: {id: 'sortable1', name: 'Sortable 1'}})
|
138
|
+
post('/band', {data: {id: 'sortable2', name: 'Sortable 2'}})
|
139
|
+
post('/band', {data: {id: 'sortable3', name: 'Sortable 3'}})
|
140
|
+
put '/band', {
|
141
|
+
'action'=>'sort',
|
142
|
+
'field'=>'position',
|
143
|
+
'ids'=> ['sortable2','sortable3','sortable1']
|
144
|
+
}
|
145
|
+
assert_successful_sorting
|
146
|
+
assert_equal 0, Band.admin_get('sortable2').position
|
147
|
+
assert_equal 1, Band.admin_get('sortable3').position
|
148
|
+
assert_equal 2, Band.admin_get('sortable1').position
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'Redirects after sorting if destination is given' do
|
152
|
+
post('/band', {data: {id: 'redirectsortable1', name: 'Redirect Sortable 1'}})
|
153
|
+
post('/band', {data: {id: 'redirectsortable2', name: 'Redirect Sortable 2'}})
|
154
|
+
post('/band', {data: {id: 'redirectsortable3', name: 'Redirect Sortable 3'}})
|
155
|
+
put '/band', {
|
156
|
+
'action'=>'sort',
|
157
|
+
'field'=>'position',
|
158
|
+
'ids'=> ['redirectsortable2','redirectsortable3','redirectsortable1'],
|
159
|
+
'_destination'=>'http://example.org/anywhere'
|
160
|
+
}
|
161
|
+
assert_equal 302, last_response.status
|
162
|
+
assert_equal 'http://example.org/anywhere', last_response.header['Location']
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'GET /:model/:id' do
|
168
|
+
it 'Sends a not-found when the model is not a class' do
|
169
|
+
get('/wizz/42')
|
170
|
+
assert_not_found
|
171
|
+
end
|
172
|
+
it 'Sends not-found when the model is a class but not a model' do
|
173
|
+
get('/string/42')
|
174
|
+
assert_not_found
|
175
|
+
end
|
176
|
+
it 'Sends not-found when the id is not provided' do
|
177
|
+
get('/band/')
|
178
|
+
assert_not_found
|
179
|
+
end
|
180
|
+
it 'Sends not-found when the instance does not exist' do
|
181
|
+
get('/band/666')
|
182
|
+
assert_not_found
|
183
|
+
end
|
184
|
+
it 'Sends the instance if it exists' do
|
185
|
+
post('/band', {data: {id: 'sendable', name: 'Morphine'}})
|
186
|
+
get('/band/sendable')
|
187
|
+
assert_successful_instance
|
188
|
+
assert_equal Band.admin_get('sendable').to_h, json['data']
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe 'PUT /:model/:id' do
|
193
|
+
it 'Sends not-found if the instance does not exist' do
|
194
|
+
put('/band/666')
|
195
|
+
assert_not_found
|
196
|
+
end
|
197
|
+
it 'Fails if the document is invalid' do
|
198
|
+
post('/band', {data: {id: 'invalid_doc_put', name: 'Valid here'}})
|
199
|
+
put('/band/invalid_doc_put', {data: {name: 'ZZ Top'}})
|
200
|
+
assert_invalid_instance
|
201
|
+
assert_equal({'name'=>['WTF']}, json['data'])
|
202
|
+
refute_equal 'ZZ Top', Band.admin_get('invalid_doc_put').name
|
203
|
+
end
|
204
|
+
it 'Updates documents' do
|
205
|
+
post('/band', {data: {id: 'updatable', name: 'Updatable'}})
|
206
|
+
put('/band/updatable', {data: {awsome: 'yes'}})
|
207
|
+
assert_successful_update
|
208
|
+
obj = Band.admin_get('updatable')
|
209
|
+
assert_equal 'yes', obj.awsome
|
210
|
+
assert_equal 'Updatable', obj.name
|
211
|
+
end
|
212
|
+
# it 'Updates nested documents' do
|
213
|
+
# obj = Band.admin_get('3')
|
214
|
+
# put('/band/3', {data: {members: [
|
215
|
+
# {id: obj.members[0].id, _class: 'Band::Member', name: 'Joey Ramone'},
|
216
|
+
# {id: obj.members[1].id, _class: 'Band::Member'},
|
217
|
+
# ]}})
|
218
|
+
# assert_successful_update
|
219
|
+
# obj = Band.admin_get('3')
|
220
|
+
# assert_equal 'yes', obj.awsome
|
221
|
+
# assert_equal 'The Ramones', obj.name
|
222
|
+
# assert_equal 2, obj.members.size
|
223
|
+
# assert_equal 'Joey Ramone', obj.members[0].name
|
224
|
+
# assert_equal 'Deedee Ramone', obj.members[1].name
|
225
|
+
# end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'DELETE /:model/:id' do
|
229
|
+
it 'Sends not-found if the instance does not exist' do
|
230
|
+
delete('/band/666')
|
231
|
+
assert_not_found
|
232
|
+
end
|
233
|
+
it 'Returns a deletion response when the instance exists' do
|
234
|
+
post('/band', {data: {id: 'deletable', name: '1D'}})
|
235
|
+
delete('/band/deletable')
|
236
|
+
assert_successful_deletion
|
237
|
+
assert_instance_of Hash, json['data']
|
238
|
+
end
|
239
|
+
it 'Redirects if destination is given' do
|
240
|
+
delete('/band/2', {'_destination'=>'http://example.org/anywhere'})
|
241
|
+
assert_equal 302, last_response.status
|
242
|
+
assert_equal 'http://example.org/anywhere', last_response.header['Location']
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|