sinatra-rest-api 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.yml +34 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +92 -0
- data/LICENSE +5 -0
- data/README.md +135 -0
- data/examples/active_record/Gemfile +15 -0
- data/examples/active_record/Gemfile.lock +75 -0
- data/examples/active_record/Rakefile +8 -0
- data/examples/active_record/app.rb +117 -0
- data/examples/active_record/config.ru +8 -0
- data/examples/active_record/database.yml +7 -0
- data/examples/active_record/db/migrate/001_create_authors.rb +11 -0
- data/examples/active_record/db/migrate/002_create_categories.rb +10 -0
- data/examples/active_record/db/migrate/003_create_books.rb +16 -0
- data/examples/active_record/db/migrate/004_create_tags.rb +10 -0
- data/examples/active_record/db/migrate/005_create_books_tags.rb +11 -0
- data/examples/active_record/db/migrate/006_create_chapters.rb +12 -0
- data/examples/active_record/db/schema.rb +61 -0
- data/examples/active_record/db/seeds.rb +0 -0
- data/examples/activeresource/Gemfile +10 -0
- data/examples/activeresource/Gemfile.lock +68 -0
- data/examples/activeresource/app.rb +125 -0
- data/examples/activeresource/config.ru +9 -0
- data/examples/activeresource/layout.slim +78 -0
- data/examples/misc/set_verb_options.rb +12 -0
- data/examples/mongoid/Gemfile +11 -0
- data/examples/mongoid/Gemfile.lock +68 -0
- data/examples/mongoid/Rakefile +3 -0
- data/examples/mongoid/app.rb +144 -0
- data/examples/mongoid/config.ru +8 -0
- data/examples/mongoid/database.yml +13 -0
- data/examples/mongoid/lib/tasks/db.rake +79 -0
- data/examples/ng-admin/index.html +168 -0
- data/examples/ng-admin/package.json +14 -0
- data/examples/sequel/Gemfile +14 -0
- data/examples/sequel/Gemfile.lock +54 -0
- data/examples/sequel/Rakefile +3 -0
- data/examples/sequel/app.rb +138 -0
- data/examples/sequel/config.ru +9 -0
- data/examples/sequel/database.yml +7 -0
- data/examples/sequel/db/migrate/001_create_authors.rb +15 -0
- data/examples/sequel/db/migrate/002_create_categories.rb +14 -0
- data/examples/sequel/db/migrate/003_create_books.rb +20 -0
- data/examples/sequel/db/migrate/004_create_tags.rb +14 -0
- data/examples/sequel/db/migrate/005_create_books_tags.rb +15 -0
- data/examples/sequel/db/migrate/006_create_chapters.rb +16 -0
- data/examples/sequel/lib/tasks/db.rake +44 -0
- data/lib/sinatra-rest-api/actions.rb +2 -1
- data/sinatra-rest-api.gemspec +24 -0
- data/spec/action_create_spec.rb +164 -0
- data/spec/action_delete_spec.rb +32 -0
- data/spec/action_list_spec.rb +31 -0
- data/spec/action_read_spec.rb +28 -0
- data/spec/action_update_spec.rb +98 -0
- data/spec/app_active_record_spec.rb +53 -0
- data/spec/app_mongoid_spec.rb +46 -0
- data/spec/app_sequel_spec.rb +45 -0
- data/spec/spec_helper.rb +59 -0
- metadata +70 -3
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"name": "test-ng-admin",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
"main": "index.html",
|
6
|
+
"scripts": {
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
8
|
+
},
|
9
|
+
"author": "Mat",
|
10
|
+
"license": "MIT",
|
11
|
+
"dependencies": {
|
12
|
+
"ng-admin": "^1.0.0-beta3"
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
backports (3.8.0)
|
5
|
+
coderay (1.1.2)
|
6
|
+
daemons (1.2.4)
|
7
|
+
eventmachine (1.2.5)
|
8
|
+
method_source (0.9.0)
|
9
|
+
multi_json (1.12.2)
|
10
|
+
mustermann (1.0.1)
|
11
|
+
pry (0.11.1)
|
12
|
+
coderay (~> 1.1.0)
|
13
|
+
method_source (~> 0.9.0)
|
14
|
+
rack (2.0.3)
|
15
|
+
rack-protection (2.0.0)
|
16
|
+
rack
|
17
|
+
rake (12.1.0)
|
18
|
+
sequel (5.0.0)
|
19
|
+
sinatra (2.0.0)
|
20
|
+
mustermann (~> 1.0)
|
21
|
+
rack (~> 2.0)
|
22
|
+
rack-protection (= 2.0.0)
|
23
|
+
tilt (~> 2.0)
|
24
|
+
sinatra-contrib (2.0.0)
|
25
|
+
backports (>= 2.0)
|
26
|
+
multi_json
|
27
|
+
mustermann (~> 1.0)
|
28
|
+
rack-protection (= 2.0.0)
|
29
|
+
sinatra (= 2.0.0)
|
30
|
+
tilt (>= 1.3, < 3)
|
31
|
+
sinatra-rest-api (0.1.2)
|
32
|
+
sinatra (~> 2.0, > 1.4.0)
|
33
|
+
sqlite3 (1.3.13)
|
34
|
+
thin (1.7.2)
|
35
|
+
daemons (~> 1.0, >= 1.0.9)
|
36
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
37
|
+
rack (>= 1, < 3)
|
38
|
+
tilt (2.0.8)
|
39
|
+
|
40
|
+
PLATFORMS
|
41
|
+
ruby
|
42
|
+
|
43
|
+
DEPENDENCIES
|
44
|
+
pry
|
45
|
+
rake
|
46
|
+
sequel
|
47
|
+
sinatra
|
48
|
+
sinatra-contrib
|
49
|
+
sinatra-rest-api
|
50
|
+
sqlite3
|
51
|
+
thin
|
52
|
+
|
53
|
+
BUNDLED WITH
|
54
|
+
1.15.4
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'sinatra/base'
|
7
|
+
|
8
|
+
require 'sequel'
|
9
|
+
require 'sqlite3'
|
10
|
+
|
11
|
+
require 'sinatra-rest-api'
|
12
|
+
|
13
|
+
# SequelTest App
|
14
|
+
module SequelTest
|
15
|
+
env = ENV['RACK_ENV'] || 'development'
|
16
|
+
CONF = YAML.load( File.read( File.expand_path( '../database.yml', __FILE__ ) ) )
|
17
|
+
CONF[env]['database'] = File.expand_path( '../' + CONF[env]['database'], __FILE__ )
|
18
|
+
DB = Sequel.connect( CONF[env] )
|
19
|
+
|
20
|
+
unless ENV['SEQUEL_RECREATE_DB'].nil?
|
21
|
+
require 'rake'
|
22
|
+
FileUtils.rm CONF[env]['database'], force: true
|
23
|
+
FileUtils.touch CONF[env]['database']
|
24
|
+
load File.join( File.expand_path( '../lib', __FILE__ ), 'tasks', 'db.rake' )
|
25
|
+
Rake::Task['db:migrate'].invoke
|
26
|
+
end
|
27
|
+
|
28
|
+
Sequel::Model.plugin :association_pks
|
29
|
+
Sequel::Model.plugin :json_serializer
|
30
|
+
Sequel::Model.plugin :nested_attributes
|
31
|
+
Sequel::Model.plugin :timestamps
|
32
|
+
Sequel::Model.plugin :validation_helpers
|
33
|
+
|
34
|
+
# An author
|
35
|
+
class Author < Sequel::Model
|
36
|
+
plugin :timestamps, update_on_create: true
|
37
|
+
|
38
|
+
one_to_many :books
|
39
|
+
|
40
|
+
def validate
|
41
|
+
super
|
42
|
+
validates_presence [ :name, :email ]
|
43
|
+
validates_unique :email
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# A book
|
48
|
+
class Book < Sequel::Model
|
49
|
+
plugin :timestamps, update_on_create: true
|
50
|
+
|
51
|
+
many_to_one :author
|
52
|
+
many_to_one :category
|
53
|
+
many_to_many :tags
|
54
|
+
one_to_many :chapters
|
55
|
+
|
56
|
+
nested_attributes :author
|
57
|
+
nested_attributes :category # , unmatched_pk: :create
|
58
|
+
nested_attributes :tags # , unmatched_pk: :create
|
59
|
+
nested_attributes :chapters
|
60
|
+
|
61
|
+
def validate
|
62
|
+
super
|
63
|
+
validates_presence :title
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# A category
|
68
|
+
class Category < Sequel::Model
|
69
|
+
plugin :timestamps, update_on_create: true
|
70
|
+
|
71
|
+
one_to_many :books
|
72
|
+
end
|
73
|
+
|
74
|
+
# A tag
|
75
|
+
class Tag < Sequel::Model
|
76
|
+
plugin :timestamps, update_on_create: true
|
77
|
+
|
78
|
+
many_to_many :books
|
79
|
+
end
|
80
|
+
|
81
|
+
# A chapter
|
82
|
+
class Chapter < Sequel::Model
|
83
|
+
plugin :timestamps, update_on_create: true
|
84
|
+
|
85
|
+
many_to_one :book
|
86
|
+
|
87
|
+
def validate
|
88
|
+
super
|
89
|
+
validates_presence :title
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##############################################################################
|
94
|
+
|
95
|
+
# App entry point
|
96
|
+
class App < Sinatra::Application
|
97
|
+
register Sinatra::RestApi
|
98
|
+
|
99
|
+
# model Author, actions: { list: { verb: :post }, read: { verb: :post } }
|
100
|
+
# model Author, actions: { list: true, read: true }
|
101
|
+
|
102
|
+
set :restapi_request_type, :json
|
103
|
+
|
104
|
+
resource Author, singular: 'writer' # , plural: 'writers'
|
105
|
+
resource Book
|
106
|
+
resource Category do
|
107
|
+
actions [ :list, :read ]
|
108
|
+
end
|
109
|
+
resource Chapter
|
110
|
+
resource Tag # , actions: [ :list, :read ]
|
111
|
+
|
112
|
+
before do
|
113
|
+
content_type :json
|
114
|
+
headers( 'Access-Control-Allow-Origin' => '*',
|
115
|
+
'Access-Control-Allow-Methods' => Sinatra::RestApi::Router::VERBS.join(',').upcase,
|
116
|
+
'Access-Control-Allow-Headers' => 'Origin, Content-Type, Accept, Authorization, Token',
|
117
|
+
'Access-Control-Expose-Headers' => 'X-Total-Count' )
|
118
|
+
end
|
119
|
+
|
120
|
+
options '*' do
|
121
|
+
response.headers['Access-Control-Allow-Origin'] = '*'
|
122
|
+
response.headers['Access-Control-Allow-Methods'] = 'HEAD,GET,PUT,DELETE,OPTIONS'
|
123
|
+
response.headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept'
|
124
|
+
halt 200
|
125
|
+
end
|
126
|
+
|
127
|
+
get '/' do
|
128
|
+
Sinatra::RestApi::Router.list_routes.to_json
|
129
|
+
end
|
130
|
+
|
131
|
+
not_found do
|
132
|
+
return if JSON.parse( body[0] ).include?( 'error' ) rescue nil # Error already defined
|
133
|
+
[ 404, { message: 'This is not the page you are looking for...' }.to_json ]
|
134
|
+
end
|
135
|
+
|
136
|
+
run! if app_file == $PROGRAM_NAME # = $0 ## starts the server if executed directly by ruby
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table( :books ) do
|
4
|
+
primary_key :id
|
5
|
+
String :title, null: false
|
6
|
+
String :description, text: true
|
7
|
+
Integer :pages
|
8
|
+
Float :price
|
9
|
+
DateTime :dt
|
10
|
+
Integer :author_id
|
11
|
+
Integer :category_id
|
12
|
+
Timestamp :created_at
|
13
|
+
Timestamp :updated_at
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
down do
|
18
|
+
drop_table( :books )
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table( :books_tags ) do
|
4
|
+
primary_key :id
|
5
|
+
Integer :book_id, null: false
|
6
|
+
Integer :tag_id, null: false
|
7
|
+
Timestamp :created_at
|
8
|
+
Timestamp :updated_at
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
down do
|
13
|
+
drop_table( :books_tags )
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table( :chapters ) do
|
4
|
+
primary_key :id
|
5
|
+
String :title, null: false
|
6
|
+
Integer :page
|
7
|
+
Integer :book_id, null: false
|
8
|
+
Timestamp :created_at
|
9
|
+
Timestamp :updated_at
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
down do
|
14
|
+
drop_table( :chapters )
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# require File.expand_path( '../../../app.rb', __FILE__ )
|
2
|
+
require 'fileutils'
|
3
|
+
require 'sequel'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
Sequel.extension :migration
|
7
|
+
|
8
|
+
PATH_MIGRATIONS = File.expand_path( '../../../db/migrate', __FILE__ ).freeze unless defined? PATH_MIGRATIONS
|
9
|
+
|
10
|
+
def get_db( connect = true )
|
11
|
+
env = ENV['RACK_ENV'] || 'development'
|
12
|
+
conf = YAML.load( File.read( File.expand_path( '../../../database.yml', __FILE__ ) ) )
|
13
|
+
conf[env]['database'] = File.expand_path( '../../../' + conf[env]['database'], __FILE__ )
|
14
|
+
connect ? Sequel.connect( conf[env] ) : conf
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace :db do
|
18
|
+
desc 'Create database'
|
19
|
+
task :create do |_t, _args|
|
20
|
+
puts 'Create DB' # unless ENV['RACK_ENV'] == 'test'
|
21
|
+
env = ENV['RACK_ENV'] || 'development'
|
22
|
+
conf = get_db( false )
|
23
|
+
FileUtils.touch conf[env]['database']
|
24
|
+
end
|
25
|
+
|
26
|
+
# Ex. rake db:migrate[5]
|
27
|
+
desc 'Run migrations'
|
28
|
+
task :migrate, [ :version ] do |_t, args|
|
29
|
+
# db = Sequel.connect(ENV.fetch('DATABASE_URL'))
|
30
|
+
if args[:version]
|
31
|
+
puts "Migrating to version #{args[:version]}" # unless ENV['RACK_ENV'] == 'test'
|
32
|
+
Sequel::Migrator.run( get_db, PATH_MIGRATIONS, target: args[:version].to_i)
|
33
|
+
else
|
34
|
+
puts 'Migrating to latest' # unless ENV['RACK_ENV'] == 'test'
|
35
|
+
Sequel::Migrator.run( get_db, PATH_MIGRATIONS )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Reset database'
|
40
|
+
task :reset do |_t, _args|
|
41
|
+
puts 'Reset DB' # unless ENV['RACK_ENV'] == 'test'
|
42
|
+
Sequel::Migrator.run( get_db, PATH_MIGRATIONS, target: 0 )
|
43
|
+
end
|
44
|
+
end
|
@@ -46,10 +46,11 @@ module Sinatra
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def self.list( route_args, params, mapping )
|
49
|
-
# TODO: option to enable X-Total-Count ?
|
49
|
+
# TODO: option to enable/disable X-Total-Count ?
|
50
50
|
params[:_where] = params[:_where].nil? ? {} : JSON.parse( params[:_where] )
|
51
51
|
route_args[:response].headers['X-Total-Count'] = mapping[:count].call( params ).to_s
|
52
52
|
result = mapping[:list].call( params, route_args[:fields] )
|
53
|
+
result = result.limit( route_args[:options][:limit].to_i ) if route_args[:options].include?( :limit )
|
53
54
|
[ 200, result.to_json( include: route_args[:options].include?( :include ) ? route_args[:options][:include] : mapping[:relations].call( nil ) ) ]
|
54
55
|
end
|
55
56
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path( '../lib', __FILE__ )
|
3
|
+
$LOAD_PATH.unshift( lib ) unless $LOAD_PATH.include?( lib )
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'sinatra-rest-api'
|
7
|
+
spec.version = '0.1.4'
|
8
|
+
spec.authors = [ 'Mattia Roccoberton' ]
|
9
|
+
spec.email = 'mat@blocknot.es'
|
10
|
+
spec.homepage = 'https://github.com/blocknotes/sinatra-rest-api'
|
11
|
+
spec.summary = 'Sinatra REST API generator'
|
12
|
+
spec.description = 'Sinatra REST API generator: CRUD actions, nested resources, supports ActiveRecord, Sequel and Mongoid'
|
13
|
+
spec.platform = Gem::Platform::RUBY
|
14
|
+
spec.license = 'ISC'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'sinatra', '> 1.4'
|
24
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
$VERBOSE = false
|
2
|
+
|
3
|
+
# require 'pry'
|
4
|
+
|
5
|
+
# Tests actions
|
6
|
+
module ActionTests
|
7
|
+
RSpec.shared_examples 'Action: create' do |models|
|
8
|
+
##############################################################################
|
9
|
+
|
10
|
+
context 'Creating authors...' do
|
11
|
+
# path = '/authors'
|
12
|
+
path = '/writers' # renamed resource
|
13
|
+
random_authors = []
|
14
|
+
(1..( rand( 15 ) + 5 )).each do |_i|
|
15
|
+
random_authors.push( name: Faker::Book.author, email: Faker::Internet.email )
|
16
|
+
end
|
17
|
+
random_authors.uniq! { |a| a[:email] }
|
18
|
+
|
19
|
+
# action: create
|
20
|
+
it "should create #{random_authors.length} authors" do
|
21
|
+
random_authors.each do |author|
|
22
|
+
post path, writer: author
|
23
|
+
expect( last_response.status ).to eq( 201 ) # response = created ?
|
24
|
+
expect( JSON.parse( last_response.body ).include?( 'id' ) ).to be true # id is returned ?
|
25
|
+
expect( models[:author].where( author ).count ).to eq( 1 ) # record exists ?
|
26
|
+
end
|
27
|
+
expect( models[:author].count ).to eq( random_authors.length ) # check number of authors created
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should not create authors with invalid data' do
|
31
|
+
authors = [
|
32
|
+
{},
|
33
|
+
{ name: '', email: ' ' },
|
34
|
+
{ email: 'aaa@bbb.ccc' },
|
35
|
+
{ name: '', email: 'aaa@bbb.ccc' },
|
36
|
+
{ name: ' ', email: 'aaa@bbb.ccc' },
|
37
|
+
{ name: 'Dick' },
|
38
|
+
{ name: 'Dick', email: '' },
|
39
|
+
{ name: 'Dick', email: ' ' }
|
40
|
+
]
|
41
|
+
authors.each do |author|
|
42
|
+
post path, writer: author
|
43
|
+
expect( last_response.status ).to eq( 400 )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# action: create
|
48
|
+
it 'no authors with same email' do
|
49
|
+
post path, writer: { name: 'Jane Doe', email: random_authors.sample[:email] }
|
50
|
+
expect( last_response.status ).to eq( 400 )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##############################################################################
|
55
|
+
|
56
|
+
context 'Creating tags...' do
|
57
|
+
random_tags = []
|
58
|
+
(1..( rand( 6 ) + 3 ) ).each { |_i| random_tags.push( name: Faker::Hipster.word ) }
|
59
|
+
random_tags.uniq! { |t| t[:name] }
|
60
|
+
|
61
|
+
# action: create
|
62
|
+
it "should create #{random_tags.length} tags" do
|
63
|
+
random_tags.each do |tag|
|
64
|
+
post '/tags', tag: tag
|
65
|
+
expect( last_response.status ).to eq( 201 )
|
66
|
+
expect( JSON.parse( last_response.body ).include?( 'id' ) ).to be true
|
67
|
+
expect( models[:tag].where( tag ).count ).to eq( 1 )
|
68
|
+
end
|
69
|
+
expect( models[:tag].count ).to eq( random_tags.length )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
##############################################################################
|
74
|
+
|
75
|
+
context 'Creating books...' do
|
76
|
+
random_books = []
|
77
|
+
(1..( rand( 4 ) + 2 )).each do |_i|
|
78
|
+
random_books.push( title: Faker::Book.title )
|
79
|
+
end
|
80
|
+
random_books.uniq! { |b| b[:title] }
|
81
|
+
(1..( rand( 4 ) + 2 )).each do |_i|
|
82
|
+
random_books.push( title: Faker::Hipster.sentence, pages: rand( 1000 ), price: rand * 20 )
|
83
|
+
end
|
84
|
+
new_cats = rand( 4 ) + 2
|
85
|
+
(1..new_cats).each do |_i|
|
86
|
+
random_books.push( title: Faker::Superhero.name, description: Faker::StarWars.quote, pages: rand( 1000 ), price: rand * 20, category_attributes: { name: Faker::Book.genre } )
|
87
|
+
end
|
88
|
+
new_tags = 2
|
89
|
+
(1..new_tags).each do |_i|
|
90
|
+
random_books.push( title: Faker::Superhero.name, description: Faker::StarWars.quote, pages: rand( 1000 ), price: rand * 20, tags_attributes: [ { name: Faker::Hipster.word } ] )
|
91
|
+
end
|
92
|
+
|
93
|
+
# action: create
|
94
|
+
it "should create #{random_books.length} books, #{new_cats} categories and #{new_tags} tags" do
|
95
|
+
tags_ids = models[:tag].all.map( &:id )
|
96
|
+
(1..( rand( 4 ) + 2 )).each do |_i|
|
97
|
+
random_books.push( title: Faker::University.name, pages: rand( 1000 ), price: rand * 20, tag_ids: tags_ids.sample( rand( 3 ) ) )
|
98
|
+
end
|
99
|
+
random_books.each do |book|
|
100
|
+
book[:author_id] = models[:author].all.sample.id if rand > 0.5
|
101
|
+
post '/books', book: book
|
102
|
+
expect( last_response.status ).to eq( 201 ) # check if the status is equal to created
|
103
|
+
expect( JSON.parse( last_response.body ).include?( 'id' ) ).to be true # check if the id is returned
|
104
|
+
category_attributes = book.delete :category_attributes
|
105
|
+
tags_attributes = book.delete :tags_attributes
|
106
|
+
tag_ids = book.delete :tag_ids
|
107
|
+
expect( models[:book].where( book ).count ).to eq( 1 ) # check if book is created
|
108
|
+
# check the relations
|
109
|
+
row = models[:book].where( book ).first
|
110
|
+
expect( row.tags.map( &:id ).sort ).to eq( tag_ids.sort ) unless tag_ids.nil?
|
111
|
+
expect( row.tags.map( &:name ) ).to eq( tags_attributes.map { |a| a[:name] } ) unless tags_attributes.nil?
|
112
|
+
expect( row.category.name ).to eq( category_attributes[:name] ) unless category_attributes.nil?
|
113
|
+
end
|
114
|
+
expect( models[:book].count ).to eq( random_books.length )
|
115
|
+
expect( models[:category].count ).to eq( new_cats )
|
116
|
+
expect( models[:tag].count ).to eq( tags_ids.length + new_tags )
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##############################################################################
|
121
|
+
|
122
|
+
context 'Creating chapters...' do
|
123
|
+
random_chapters = []
|
124
|
+
(1..( rand( 10 ) + 2 )).each do |_i|
|
125
|
+
random_chapters.push( title: Faker::Superhero.name )
|
126
|
+
end
|
127
|
+
random_chapters.uniq! { |c| c[:title] }
|
128
|
+
|
129
|
+
# action: create
|
130
|
+
it "should create #{random_chapters.length} chapters" do
|
131
|
+
ids = models[:book].all.map( &:id )
|
132
|
+
random_chapters.each do |chapter|
|
133
|
+
chapter[:book_id] = ids.sample
|
134
|
+
post '/chapters', chapter: chapter
|
135
|
+
expect( last_response.status ).to eq( 201 )
|
136
|
+
expect( JSON.parse( last_response.body ).include?( 'id' ) ).to be true
|
137
|
+
expect( models[:chapter].where( chapter ).count ).to eq( 1 )
|
138
|
+
end
|
139
|
+
expect( models[:chapter].count ).to eq( random_chapters.length )
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should not create a chapter without data' do
|
143
|
+
chapters = [
|
144
|
+
{},
|
145
|
+
{ book_id: models[:book].last.id.to_s }
|
146
|
+
]
|
147
|
+
chapters.each do |chapter|
|
148
|
+
post '/chapters', chapter: chapter
|
149
|
+
expect( last_response.status ).to eq( 400 )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
##############################################################################
|
155
|
+
|
156
|
+
context 'Creating categories...' do
|
157
|
+
# action: create
|
158
|
+
it 'should not create categories' do
|
159
|
+
post '/categories', category: { name: 'A cat' }
|
160
|
+
expect( last_response.status ).to eq( 404 )
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|