mappd_rest 0.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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rubocop.yml +15 -0
- data/Dockerfile +31 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +153 -0
- data/Guardfile +10 -0
- data/Makefile +12 -0
- data/README.md +94 -0
- data/db/.gitkeep +0 -0
- data/docker-compose.yml +11 -0
- data/lib/mappd/rest.rb +105 -0
- data/lib/mappd/version.rb +3 -0
- data/lib/mappd_rest.rb +4 -0
- data/mappd_rest.gemspec +25 -0
- data/spec/boot.rb +9 -0
- data/spec/crud_spec.rb +114 -0
- data/spec/data/author.rb +6 -0
- data/spec/data/book.rb +9 -0
- data/spec/data/book_schema.json +18 -0
- data/spec/data/data.rb +16 -0
- data/spec/spec_helper.rb +18 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 54ee288597cfd12f98eedb6fcc20ec30491688e7303f979071c4b57111d3faf6
|
4
|
+
data.tar.gz: fcb1dddabd63d63d890bf52262383ad808d653f2301ce56d7000895ad9343495
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79b40d18e4f6d0a8ed7f31a4956b4d1cfbd662e17df26d274fb94cfd232212fc96d0648103bb1babae367e680a8e3120e9312dc50d2880f00e541cb23fbd3ade
|
7
|
+
data.tar.gz: b88f37604579a9b7eaa5f5c78388113f35946c8c1590a35fb30a64728ffa7e282b80c91c07298f5e456f16fbab688eacd6137771f98822138ff8ed32da567b84
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.5
|
3
|
+
Exclude:
|
4
|
+
- 'Guardfile'
|
5
|
+
Style/Documentation:
|
6
|
+
Enabled: false
|
7
|
+
Style/FrozenStringLiteralComment:
|
8
|
+
Enabled: false
|
9
|
+
Metrics/ModuleLength:
|
10
|
+
Exclude:
|
11
|
+
- "**/*_spec.rb"
|
12
|
+
Metrics/BlockLength:
|
13
|
+
Exclude:
|
14
|
+
- "**/*_spec.rb"
|
15
|
+
- "lib/mappd/rest.rb"
|
data/Dockerfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
FROM ruby:2.5-slim
|
2
|
+
MAINTAINER dan@paz.am
|
3
|
+
ENV REFRESHED_AT 2018-05-14
|
4
|
+
|
5
|
+
# Install packages
|
6
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
7
|
+
build-essential \
|
8
|
+
git \
|
9
|
+
ruby-dev \
|
10
|
+
libgdbm-dev \
|
11
|
+
libncurses5-dev \
|
12
|
+
libffi-dev \
|
13
|
+
libyaml-dev \
|
14
|
+
libreadline-dev \
|
15
|
+
sqlite3 \
|
16
|
+
libsqlite3-dev \
|
17
|
+
curl \
|
18
|
+
libpq-dev \
|
19
|
+
default-libmysqlclient-dev \
|
20
|
+
&& rm -rf /var/lib/apt/lists/*
|
21
|
+
|
22
|
+
# Setup app location
|
23
|
+
RUN mkdir -p /app
|
24
|
+
WORKDIR /app
|
25
|
+
|
26
|
+
# Install gems
|
27
|
+
ADD mappd_rest.gemspec /app/mappd_rest.gemspec
|
28
|
+
ADD Gemfile /app/Gemfile
|
29
|
+
ADD Gemfile.lock /app/Gemfile.lock
|
30
|
+
ADD lib/ /app/lib
|
31
|
+
RUN bundle install --jobs 4
|
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'activerecord', '>=5.2.0'
|
4
|
+
gem 'grape', '>=1.0.3'
|
5
|
+
gem 'mappd'
|
6
|
+
gem 'rake'
|
7
|
+
gem 'sqlite3'
|
8
|
+
|
9
|
+
group :development do
|
10
|
+
gem 'byebug'
|
11
|
+
gem 'guard-rspec', require: false
|
12
|
+
gem 'guard-rubocop', require: false
|
13
|
+
gem 'rubocop', require: false
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'rack-test'
|
18
|
+
gem 'rspec'
|
19
|
+
end
|
20
|
+
|
21
|
+
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mappd_rest (0.0.1)
|
5
|
+
activerecord (>= 5.2.0)
|
6
|
+
grape (>= 1.0.3)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (5.2.0)
|
12
|
+
activesupport (= 5.2.0)
|
13
|
+
activerecord (5.2.0)
|
14
|
+
activemodel (= 5.2.0)
|
15
|
+
activesupport (= 5.2.0)
|
16
|
+
arel (>= 9.0)
|
17
|
+
activesupport (5.2.0)
|
18
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
19
|
+
i18n (>= 0.7, < 2)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
tzinfo (~> 1.1)
|
22
|
+
arel (9.0.0)
|
23
|
+
ast (2.4.0)
|
24
|
+
axiom-types (0.1.1)
|
25
|
+
descendants_tracker (~> 0.0.4)
|
26
|
+
ice_nine (~> 0.11.0)
|
27
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
28
|
+
builder (3.2.3)
|
29
|
+
byebug (10.0.2)
|
30
|
+
coderay (1.1.2)
|
31
|
+
coercible (1.0.0)
|
32
|
+
descendants_tracker (~> 0.0.1)
|
33
|
+
concurrent-ruby (1.0.5)
|
34
|
+
descendants_tracker (0.0.4)
|
35
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
36
|
+
diff-lcs (1.3)
|
37
|
+
equalizer (0.0.11)
|
38
|
+
ffi (1.9.23)
|
39
|
+
formatador (0.2.5)
|
40
|
+
grape (1.0.3)
|
41
|
+
activesupport
|
42
|
+
builder
|
43
|
+
mustermann-grape (~> 1.0.0)
|
44
|
+
rack (>= 1.3.0)
|
45
|
+
rack-accept
|
46
|
+
virtus (>= 1.0.0)
|
47
|
+
guard (2.14.2)
|
48
|
+
formatador (>= 0.2.4)
|
49
|
+
listen (>= 2.7, < 4.0)
|
50
|
+
lumberjack (>= 1.0.12, < 2.0)
|
51
|
+
nenv (~> 0.1)
|
52
|
+
notiffany (~> 0.0)
|
53
|
+
pry (>= 0.9.12)
|
54
|
+
shellany (~> 0.0)
|
55
|
+
thor (>= 0.18.1)
|
56
|
+
guard-compat (1.2.1)
|
57
|
+
guard-rspec (4.7.3)
|
58
|
+
guard (~> 2.1)
|
59
|
+
guard-compat (~> 1.1)
|
60
|
+
rspec (>= 2.99.0, < 4.0)
|
61
|
+
guard-rubocop (1.3.0)
|
62
|
+
guard (~> 2.0)
|
63
|
+
rubocop (~> 0.20)
|
64
|
+
i18n (1.0.1)
|
65
|
+
concurrent-ruby (~> 1.0)
|
66
|
+
ice_nine (0.11.2)
|
67
|
+
listen (3.1.5)
|
68
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
69
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
70
|
+
ruby_dep (~> 1.2)
|
71
|
+
lumberjack (1.0.13)
|
72
|
+
mappd (0.0.3)
|
73
|
+
activerecord (>= 5.2.0)
|
74
|
+
method_source (0.9.0)
|
75
|
+
minitest (5.11.3)
|
76
|
+
mustermann (1.0.2)
|
77
|
+
mustermann-grape (1.0.0)
|
78
|
+
mustermann (~> 1.0.0)
|
79
|
+
nenv (0.3.0)
|
80
|
+
notiffany (0.1.1)
|
81
|
+
nenv (~> 0.1)
|
82
|
+
shellany (~> 0.0)
|
83
|
+
parallel (1.12.1)
|
84
|
+
parser (2.5.1.0)
|
85
|
+
ast (~> 2.4.0)
|
86
|
+
powerpack (0.1.1)
|
87
|
+
pry (0.11.3)
|
88
|
+
coderay (~> 1.1.0)
|
89
|
+
method_source (~> 0.9.0)
|
90
|
+
rack (2.0.5)
|
91
|
+
rack-accept (0.4.5)
|
92
|
+
rack (>= 0.4)
|
93
|
+
rack-test (1.0.0)
|
94
|
+
rack (>= 1.0, < 3)
|
95
|
+
rainbow (3.0.0)
|
96
|
+
rake (12.3.1)
|
97
|
+
rb-fsevent (0.10.3)
|
98
|
+
rb-inotify (0.9.10)
|
99
|
+
ffi (>= 0.5.0, < 2)
|
100
|
+
rspec (3.7.0)
|
101
|
+
rspec-core (~> 3.7.0)
|
102
|
+
rspec-expectations (~> 3.7.0)
|
103
|
+
rspec-mocks (~> 3.7.0)
|
104
|
+
rspec-core (3.7.1)
|
105
|
+
rspec-support (~> 3.7.0)
|
106
|
+
rspec-expectations (3.7.0)
|
107
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
108
|
+
rspec-support (~> 3.7.0)
|
109
|
+
rspec-mocks (3.7.0)
|
110
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
111
|
+
rspec-support (~> 3.7.0)
|
112
|
+
rspec-support (3.7.1)
|
113
|
+
rubocop (0.56.0)
|
114
|
+
parallel (~> 1.10)
|
115
|
+
parser (>= 2.5)
|
116
|
+
powerpack (~> 0.1)
|
117
|
+
rainbow (>= 2.2.2, < 4.0)
|
118
|
+
ruby-progressbar (~> 1.7)
|
119
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
120
|
+
ruby-progressbar (1.9.0)
|
121
|
+
ruby_dep (1.5.0)
|
122
|
+
shellany (0.0.1)
|
123
|
+
sqlite3 (1.3.13)
|
124
|
+
thor (0.20.0)
|
125
|
+
thread_safe (0.3.6)
|
126
|
+
tzinfo (1.2.5)
|
127
|
+
thread_safe (~> 0.1)
|
128
|
+
unicode-display_width (1.3.2)
|
129
|
+
virtus (1.0.5)
|
130
|
+
axiom-types (~> 0.1)
|
131
|
+
coercible (~> 1.0)
|
132
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
133
|
+
equalizer (~> 0.0, >= 0.0.9)
|
134
|
+
|
135
|
+
PLATFORMS
|
136
|
+
ruby
|
137
|
+
|
138
|
+
DEPENDENCIES
|
139
|
+
activerecord (>= 5.2.0)
|
140
|
+
byebug
|
141
|
+
grape (>= 1.0.3)
|
142
|
+
guard-rspec
|
143
|
+
guard-rubocop
|
144
|
+
mappd
|
145
|
+
mappd_rest!
|
146
|
+
rack-test
|
147
|
+
rake
|
148
|
+
rspec
|
149
|
+
rubocop
|
150
|
+
sqlite3
|
151
|
+
|
152
|
+
BUNDLED WITH
|
153
|
+
1.16.1
|
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
guard :rubocop do
|
2
|
+
watch(%r{.+\.rb$})
|
3
|
+
watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
|
4
|
+
end
|
5
|
+
|
6
|
+
guard :rspec, cmd: 'rspec' do
|
7
|
+
watch(%r{^spec/.+_spec\.rb$})
|
8
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
9
|
+
watch('spec/spec_helper.rb') { "spec" }
|
10
|
+
end
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# MappdRest
|
2
|
+
|
3
|
+
Note: Work in progress.
|
4
|
+
|
5
|
+
A gem that dynamically exposes REST endpoints for ActiveRecord models.
|
6
|
+
|
7
|
+
## Development
|
8
|
+
|
9
|
+
### Prerequisites
|
10
|
+
|
11
|
+
* Docker Community Edition - https://www.docker.com/community-edition
|
12
|
+
* Make - https://www.gnu.org/software/make/
|
13
|
+
|
14
|
+
### Setup
|
15
|
+
|
16
|
+
* `make` will build the application ready for use.
|
17
|
+
|
18
|
+
### Guard
|
19
|
+
|
20
|
+
* `make guard` run both linting and testing whilst watching files
|
21
|
+
|
22
|
+
## Setup
|
23
|
+
|
24
|
+
```
|
25
|
+
gem install mappd-rest
|
26
|
+
```
|
27
|
+
|
28
|
+
Or add to your `Gemfile`:
|
29
|
+
|
30
|
+
```
|
31
|
+
gem 'mappd-rest'
|
32
|
+
```
|
33
|
+
|
34
|
+
## How it works
|
35
|
+
|
36
|
+
### Simple Endpoints
|
37
|
+
|
38
|
+
Lets say you have two ActiveRecord models. Person and Post. By either mounting the `Mappd::Rest` class inside rails or running a `config.ru` file like below the following endpoints will be created.
|
39
|
+
|
40
|
+
| METHOD | ENDPOINT |
|
41
|
+
| --- | --- |
|
42
|
+
| GET | /people/schema |
|
43
|
+
| GET | /people/ |
|
44
|
+
| GET | /people/:id |
|
45
|
+
| POST | /people |
|
46
|
+
| DELETE | /people/:id |
|
47
|
+
| PUT | /people/:id |
|
48
|
+
| GET | /post/schema |
|
49
|
+
| GET | /post/ |
|
50
|
+
| GET | /post/:id |
|
51
|
+
| POST | /post |
|
52
|
+
| DELETE | /post/:id |
|
53
|
+
| PUT | /post/:id |
|
54
|
+
|
55
|
+
### Nested Endpoints
|
56
|
+
|
57
|
+
Also you can drive down into nested data. In this example lets say a Post has a Person and a Person has many Posts. It would create the following endpoints.
|
58
|
+
|
59
|
+
|
60
|
+
| METHOD | ENDPOINT |
|
61
|
+
| --- | --- |
|
62
|
+
| GET | /posts/:id/person |
|
63
|
+
| GET | /posts/:id/person/:id |
|
64
|
+
| GET | /posts/:id/person/:id/posts |
|
65
|
+
|
66
|
+
It does this by dynamically interpreting routes passed to the endpoint and calling the appropriate models and methods.
|
67
|
+
|
68
|
+
### Limiting & Offsetting (Pagination)
|
69
|
+
You can also limit and offset any call that returns a collection.
|
70
|
+
|
71
|
+
`/posts?limit=10&offset=10`
|
72
|
+
`/person/1/posts?limit=5&offset=5`
|
73
|
+
|
74
|
+
### Authorisation
|
75
|
+
|
76
|
+
Before every request the API will check for a header `X-Access-Token`
|
77
|
+
it will then use that access token as a parameter to the authorization method you have set.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# config.ru
|
81
|
+
Mappd::Rest.auth_method = lambda { |token|
|
82
|
+
# Will look for a truth - You can use any method you like
|
83
|
+
User.find_by_token(token).active?
|
84
|
+
# For example - if you didnt want to do any kind of auth
|
85
|
+
# just return true
|
86
|
+
}
|
87
|
+
run Mappd::Rest
|
88
|
+
```
|
89
|
+
## Running
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# config.ru
|
93
|
+
run Mappd::Rest
|
94
|
+
```
|
data/db/.gitkeep
ADDED
File without changes
|
data/docker-compose.yml
ADDED
data/lib/mappd/rest.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module Mappd
|
2
|
+
class Rest < Grape::API
|
3
|
+
format :json
|
4
|
+
|
5
|
+
cattr_accessor :auth_method
|
6
|
+
|
7
|
+
before do
|
8
|
+
unless Rest.auth_method.call(request.headers['X-Access-Token'])
|
9
|
+
error!('Access Denied', 401)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
rescue_from ActiveRecord::RecordNotFound do |e|
|
14
|
+
error!(e, 404)
|
15
|
+
end
|
16
|
+
|
17
|
+
rescue_from ActiveRecord::RecordInvalid do |e|
|
18
|
+
error!(e, 422)
|
19
|
+
end
|
20
|
+
|
21
|
+
helpers do
|
22
|
+
def resource
|
23
|
+
params[:resource].singularize.titlecase.constantize
|
24
|
+
end
|
25
|
+
|
26
|
+
def all
|
27
|
+
resource.all
|
28
|
+
end
|
29
|
+
|
30
|
+
def limiter(relation)
|
31
|
+
return relation unless relation.is_a?(ActiveRecord::Relation)
|
32
|
+
relation = relation.offset(params[:offset]) if params[:offset]
|
33
|
+
relation = relation.limit(params[:limit]) if params[:limit]
|
34
|
+
relation
|
35
|
+
end
|
36
|
+
|
37
|
+
def find
|
38
|
+
resource.find(params[:id])
|
39
|
+
end
|
40
|
+
|
41
|
+
def create!(attributes)
|
42
|
+
resource.create!(attributes)
|
43
|
+
end
|
44
|
+
|
45
|
+
def update!(attributes)
|
46
|
+
find.update!(attributes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy!
|
50
|
+
find&.destroy
|
51
|
+
end
|
52
|
+
|
53
|
+
def method?(param)
|
54
|
+
param.to_i.to_s != param
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/:resource/schema' do
|
59
|
+
resource.columns.map do |column|
|
60
|
+
{
|
61
|
+
name: column.name,
|
62
|
+
type: column.type.to_s
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
params do
|
68
|
+
requires :resource, type: String
|
69
|
+
optional :id, type: Integer
|
70
|
+
end
|
71
|
+
|
72
|
+
get '/:resource' do
|
73
|
+
limiter(all)
|
74
|
+
end
|
75
|
+
|
76
|
+
get '/:resource/:id' do
|
77
|
+
find
|
78
|
+
end
|
79
|
+
|
80
|
+
get '/:resource/:id/*' do
|
81
|
+
args = params[:splat][0].split('/')
|
82
|
+
relation = find
|
83
|
+
args.each_with_index do |param, index|
|
84
|
+
relation = if index.even? || method?(param)
|
85
|
+
relation.send(param.to_sym)
|
86
|
+
else
|
87
|
+
relation.find(param.to_i)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
limiter(relation)
|
91
|
+
end
|
92
|
+
|
93
|
+
post '/:resource' do
|
94
|
+
create!(JSON.parse(params.first[0]))
|
95
|
+
end
|
96
|
+
|
97
|
+
put '/:resource/:id' do
|
98
|
+
update!(JSON.parse(params.first[0]))
|
99
|
+
end
|
100
|
+
|
101
|
+
delete '/:resource/:id' do
|
102
|
+
destroy!
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/mappd_rest.rb
ADDED
data/mappd_rest.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
|
+
require 'mappd/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'mappd_rest'
|
6
|
+
s.version = MappdRest::VERSION
|
7
|
+
s.authors = ['Dan Watson']
|
8
|
+
s.email = ['dan@paz.am']
|
9
|
+
s.homepage = 'https://github.com/dan-watson/mappd-rest'
|
10
|
+
s.summary = 'Mappd Rest is a microframework for exposing ActiveRecord
|
11
|
+
models over rest'
|
12
|
+
s.description = 'You can expose ActiveRecord objects over REST.'
|
13
|
+
.gsub(/^ {4}/, '')
|
14
|
+
|
15
|
+
s.rubyforge_project = 'mappd_rest'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`
|
20
|
+
.split("\n").map { |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
|
23
|
+
s.add_dependency 'activerecord', '>=5.2.0'
|
24
|
+
s.add_dependency 'grape', '>=1.0.3'
|
25
|
+
end
|
data/spec/boot.rb
ADDED
data/spec/crud_spec.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
require File.expand_path('spec_helper.rb', __dir__)
|
3
|
+
|
4
|
+
require 'rack/test'
|
5
|
+
require 'byebug'
|
6
|
+
|
7
|
+
describe 'Rack Test' do
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
def app
|
11
|
+
Mappd::Rest.auth_method = lambda { |token|
|
12
|
+
# User.find_by_token(token)
|
13
|
+
token == 'TOKEN'
|
14
|
+
}
|
15
|
+
Mappd::Rest
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'endpoints' do
|
19
|
+
before(:all) do
|
20
|
+
current_session.header 'X-Access-Token', 'TOKEN'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns schema for an entity' do
|
24
|
+
schema = JSON.parse(File.read('spec/data/book_schema.json'))
|
25
|
+
get '/books/schema'
|
26
|
+
expect(last_response).to be_ok
|
27
|
+
expect(JSON.parse(last_response.body)).to eq(schema)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns all for an entity' do
|
31
|
+
get '/books'
|
32
|
+
expect(last_response).to be_ok
|
33
|
+
expect(JSON.parse(last_response.body).length).to eq(3)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns two offset by one for an entity' do
|
37
|
+
get '/books?limit=2&offset=1'
|
38
|
+
expect(last_response).to be_ok
|
39
|
+
result = JSON.parse(last_response.body)
|
40
|
+
expect(result.length).to eq(2)
|
41
|
+
expect(result.map { |i| i['id'] }).to eq([2, 3])
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns all from a relationship' do
|
45
|
+
get '/authors/1/books'
|
46
|
+
expect(last_response).to be_ok
|
47
|
+
expect(JSON.parse(last_response.body).length).to eq(2)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns all from a relationship with limits' do
|
51
|
+
get '/authors/1/books?limit=2&offset=1'
|
52
|
+
expect(last_response).to be_ok
|
53
|
+
expect(JSON.parse(last_response.body).length).to eq(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns one from relationship' do
|
57
|
+
get '/authors/1/books/1'
|
58
|
+
expect(last_response).to be_ok
|
59
|
+
expect(JSON.parse(last_response.body)['title'])
|
60
|
+
.to eq('Wind in the willows')
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns one from a deeply nested relationship' do
|
64
|
+
get '/authors/1/books/1/author'
|
65
|
+
expect(last_response).to be_ok
|
66
|
+
expect(JSON.parse(last_response.body)['name'])
|
67
|
+
.to eq('Dan')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns one' do
|
71
|
+
get '/books/1'
|
72
|
+
expect(JSON.parse(last_response.body)['title'])
|
73
|
+
.to eq('Wind in the willows')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not return one' do
|
77
|
+
get '/books/600'
|
78
|
+
expect(last_response.status).to eq(404)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'creates one' do
|
82
|
+
post '/books', { title: 'New Book', year: 2012 }.to_json
|
83
|
+
expect(last_response.status).to eq(201)
|
84
|
+
expect(Book.find(3)).to be_present
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'does not create one' do
|
88
|
+
post '/books', { title: 'New Book' }.to_json
|
89
|
+
expect(last_response.status).to eq(422)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'updates one' do
|
93
|
+
put '/books/1', { title: 'Updated Book', year: 2012 }.to_json
|
94
|
+
expect(last_response.status).to eq(200)
|
95
|
+
expect(Book.find(1).title).to eq('Updated Book')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not update one' do
|
99
|
+
put '/books/1', { title: 'Updated Book', year: nil }.to_json
|
100
|
+
expect(last_response.status).to eq(422)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'deletes one' do
|
104
|
+
delete '/books/1'
|
105
|
+
expect(last_response).to be_ok
|
106
|
+
expect { Book.find(1) }.to raise_error(ActiveRecord::RecordNotFound)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'does not delete one' do
|
110
|
+
delete '/books/600'
|
111
|
+
expect(last_response.status).to eq(404)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/spec/data/author.rb
ADDED
data/spec/data/book.rb
ADDED
data/spec/data/data.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path('book.rb', __dir__)
|
2
|
+
require File.expand_path('author.rb', __dir__)
|
3
|
+
|
4
|
+
# Book
|
5
|
+
|
6
|
+
Book.migrate!
|
7
|
+
Author.migrate!
|
8
|
+
Book.delete_all
|
9
|
+
Author.delete_all
|
10
|
+
|
11
|
+
author = Author.create!(name: 'Dan')
|
12
|
+
author2 = Author.create!(name: 'James')
|
13
|
+
|
14
|
+
Book.create!(title: 'Wind in the willows', year: 1911, author: author)
|
15
|
+
Book.create!(title: 'Once upon a time', year: 1911, author: author)
|
16
|
+
Book.create!(title: 'Not another one', year: 2012, author: author2)
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('boot.rb', __dir__)
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.full_backtrace = false
|
5
|
+
config.backtrace_exclusion_patterns = [
|
6
|
+
/bundle/
|
7
|
+
]
|
8
|
+
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.mock_with :rspec do |mocks|
|
14
|
+
mocks.verify_partial_doubles = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mappd_rest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Watson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: grape
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.3
|
41
|
+
description: You can expose ActiveRecord objects over REST.
|
42
|
+
email:
|
43
|
+
- dan@paz.am
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- ".rspec"
|
50
|
+
- ".rubocop.yml"
|
51
|
+
- Dockerfile
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- Guardfile
|
55
|
+
- Makefile
|
56
|
+
- README.md
|
57
|
+
- db/.gitkeep
|
58
|
+
- docker-compose.yml
|
59
|
+
- lib/mappd/rest.rb
|
60
|
+
- lib/mappd/version.rb
|
61
|
+
- lib/mappd_rest.rb
|
62
|
+
- mappd_rest.gemspec
|
63
|
+
- spec/boot.rb
|
64
|
+
- spec/crud_spec.rb
|
65
|
+
- spec/data/author.rb
|
66
|
+
- spec/data/book.rb
|
67
|
+
- spec/data/book_schema.json
|
68
|
+
- spec/data/data.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
homepage: https://github.com/dan-watson/mappd-rest
|
71
|
+
licenses: []
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project: mappd_rest
|
89
|
+
rubygems_version: 2.7.6
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Mappd Rest is a microframework for exposing ActiveRecord models over rest
|
93
|
+
test_files: []
|