yodatra 0.1.4 → 0.1.5
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.
- data/Gemfile +7 -1
- data/README.md +6 -0
- data/Rakefile +13 -0
- data/lib/yodatra/api_formatter.rb +1 -1
- data/lib/yodatra/crypto.rb +26 -0
- data/lib/yodatra/model_controller.rb +100 -0
- data/lib/yodatra/version.rb +1 -1
- data/spec/spec_helper.rb +24 -0
- data/spec/unit/model_controller_spec.rb +148 -0
- metadata +11 -5
- data/README +0 -6
data/Gemfile
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
3
|
+
group :test do
|
4
|
+
gem 'rake'
|
5
|
+
gem 'rspec', :require => false
|
6
|
+
gem 'simplecov', :require => false
|
7
|
+
gem 'coveralls', :require => false
|
8
|
+
gem 'mocha', :require => false
|
9
|
+
end
|
4
10
|
|
5
11
|
gemspec
|
data/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Yodatra [](https://travis-ci.org/squareteam/yodatra) [](https://coveralls.io/r/squareteam/yodatra)
|
2
|
+
===
|
3
|
+
|
4
|
+
Backend development you shall do. And yodatra you shall use.
|
5
|
+
|
6
|
+
A minimalistic framework built on top of Sinatra it is.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new do |task|
|
5
|
+
task.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
6
|
+
task.pattern = 'spec/**/*_spec.rb'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'coveralls/rake/task'
|
10
|
+
Coveralls::RakeTask.new
|
11
|
+
task :test_with_coveralls => [:spec, :features, 'coveralls:push']
|
12
|
+
|
13
|
+
task :default => :spec
|
@@ -13,7 +13,7 @@ module Yodatra
|
|
13
13
|
def _call(env)
|
14
14
|
status, headers, response = @app.call(env)
|
15
15
|
|
16
|
-
@block.yield
|
16
|
+
status, headers, response = @block.yield(status, headers, response) unless @block.nil?
|
17
17
|
|
18
18
|
[status, headers, response]
|
19
19
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Yodatra
|
2
|
+
class Crypto
|
3
|
+
|
4
|
+
class << self
|
5
|
+
# Computes the PBKDF2-SHA256 digest of a password using 10000 iterations and
|
6
|
+
# the given salt, and return a 32-byte output. If no salt is given, a random
|
7
|
+
# 8-byte salt is generated. The salt is also returned.
|
8
|
+
def generate_pbkdf(password, salt = nil)
|
9
|
+
new_salt = salt
|
10
|
+
iter = 1000
|
11
|
+
|
12
|
+
if salt.nil?
|
13
|
+
new_salt = SecureRandom.random_bytes(8)
|
14
|
+
end
|
15
|
+
|
16
|
+
digest = OpenSSL::Digest::SHA256.new
|
17
|
+
len = digest.digest_length
|
18
|
+
value = OpenSSL::PKCS5.pbkdf2_hmac(password, new_salt, iter, len, digest)
|
19
|
+
|
20
|
+
[new_salt, value]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Yodatra
|
2
|
+
class ModelController < Sinatra::Base
|
3
|
+
|
4
|
+
before do
|
5
|
+
content_type 'application/json'
|
6
|
+
end
|
7
|
+
|
8
|
+
READ_ALL = :read_all
|
9
|
+
get "/*s" do
|
10
|
+
no_route if disabled? READ_ALL
|
11
|
+
model_name.constantize.all.to_json
|
12
|
+
end
|
13
|
+
|
14
|
+
READ_ONE = :read
|
15
|
+
get "/*s/:id" do
|
16
|
+
no_route if disabled? READ_ONE
|
17
|
+
|
18
|
+
@one = model_name.constantize.find params[:id]
|
19
|
+
@one.to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
CREATE_ONE = :create
|
23
|
+
post "/*s" do
|
24
|
+
no_route if disabled? CREATE_ONE
|
25
|
+
|
26
|
+
@one = model_name.constantize.new params
|
27
|
+
|
28
|
+
if @one.save
|
29
|
+
@one.to_json
|
30
|
+
else
|
31
|
+
status 400
|
32
|
+
@one.errors.full_messages.to_json
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
UPDATE_ONE = :update
|
37
|
+
put "/*s/:id" do
|
38
|
+
no_route if disabled? UPDATE_ONE
|
39
|
+
|
40
|
+
@one = model_name.constantize.find params[:id]
|
41
|
+
|
42
|
+
if !@one.nil? && @one.update_attributes(params)
|
43
|
+
@one.to_json
|
44
|
+
else
|
45
|
+
status 400
|
46
|
+
if !@one.nil?
|
47
|
+
@one.errors.full_messages.to_json
|
48
|
+
else
|
49
|
+
['record not found'].to_json
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
DELETE_ONE = :delete
|
55
|
+
delete "/*s/:id" do
|
56
|
+
no_route if disabled? DELETE_ONE
|
57
|
+
|
58
|
+
@one = model_name.constantize.find params[:id]
|
59
|
+
|
60
|
+
if @one.destroy
|
61
|
+
@one.to_json
|
62
|
+
else
|
63
|
+
status 400
|
64
|
+
@one.errors.full_messages.to_json
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def model_params
|
71
|
+
params
|
72
|
+
end
|
73
|
+
|
74
|
+
def model_name
|
75
|
+
self.class.name.split('::').last.gsub(/sController/, '')
|
76
|
+
end
|
77
|
+
|
78
|
+
def route_name
|
79
|
+
self.send(:model_name).underscore
|
80
|
+
end
|
81
|
+
|
82
|
+
def disabled? key
|
83
|
+
params[:splat].first != route_name || self.class.method_defined?(key) && self.send(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def no_route
|
87
|
+
pass
|
88
|
+
end
|
89
|
+
|
90
|
+
class << self
|
91
|
+
def disable(*opts)
|
92
|
+
opts.each do |key|
|
93
|
+
undef_method(key) if method_defined? key
|
94
|
+
define_method(key, Proc.new {|| true})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
data/lib/yodatra/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
ENV['RACK_ENV'] ||= 'test'
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
require 'coveralls'
|
5
|
+
Coveralls.wear!
|
6
|
+
require 'rack/test'
|
7
|
+
require 'rspec'
|
8
|
+
|
9
|
+
require File.expand_path '../../lib/yodatra.rb', __FILE__
|
10
|
+
require File.expand_path '../../lib/yodatra/model_controller.rb', __FILE__
|
11
|
+
|
12
|
+
module RSpecMixin
|
13
|
+
include Rack::Test::Methods
|
14
|
+
def app
|
15
|
+
Sinatra.new {
|
16
|
+
use Yodatra::Base
|
17
|
+
use Yodatra::ModelController
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
RSpec.configure do |c|
|
23
|
+
c.include RSpecMixin
|
24
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.expand_path '../../spec_helper.rb', __FILE__
|
2
|
+
require 'mocha/setup'
|
3
|
+
require 'mocha/api'
|
4
|
+
|
5
|
+
# Mock model constructed for the tests
|
6
|
+
class TestModel
|
7
|
+
ALL = %w(a b c)
|
8
|
+
class << self
|
9
|
+
def all; ALL.map{ |e| TestModel.new({:data=> e }) }; end
|
10
|
+
def find(id); TestModel.new({:data => ALL[id.to_i]}); end
|
11
|
+
end
|
12
|
+
def initialize(param); @data = param[:data]; end
|
13
|
+
def save
|
14
|
+
if @data.is_a? String
|
15
|
+
ALL.push(@data)
|
16
|
+
true
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def destroy
|
22
|
+
if ALL.include? @data
|
23
|
+
ALL.delete @data
|
24
|
+
true
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def errors; []; end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'Model controller' do
|
33
|
+
before do
|
34
|
+
@any_model = 'TestModel'
|
35
|
+
@errors = ['error']
|
36
|
+
Yodatra::ModelController.any_instance.stubs(:model_name).returns(@any_model)
|
37
|
+
Array.any_instance.stubs(:full_messages).returns(@errors)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'Getting a collection of the Model' do
|
41
|
+
context 'default' do
|
42
|
+
it 'should have a GET all route' do
|
43
|
+
get '/test_models'
|
44
|
+
|
45
|
+
last_response.should be_ok
|
46
|
+
expect(last_response.body).to eq(TestModel::ALL.map{|e| {:data => e} }.to_json)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
context 'forced GET all route disabled' do
|
50
|
+
before do
|
51
|
+
class Yodatra::ModelController
|
52
|
+
disable :read_all
|
53
|
+
end
|
54
|
+
end
|
55
|
+
it 'should fail with no route available' do
|
56
|
+
get '/test_models'
|
57
|
+
|
58
|
+
last_response.should_not be_ok
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
describe 'getting an specific Model instance' do
|
63
|
+
it 'should have a GET one route' do
|
64
|
+
get '/test_models/2'
|
65
|
+
|
66
|
+
last_response.should be_ok
|
67
|
+
expect(last_response.body).to eq({ :data => 'c'}.to_json)
|
68
|
+
end
|
69
|
+
context 'forced GET one route disabled' do
|
70
|
+
before do
|
71
|
+
class Yodatra::ModelController
|
72
|
+
disable :read
|
73
|
+
end
|
74
|
+
end
|
75
|
+
it 'should fail with no route available' do
|
76
|
+
get '/test_models/1'
|
77
|
+
|
78
|
+
last_response.should_not be_ok
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
describe 'creating a Model instance' do
|
83
|
+
context 'with correct model params' do
|
84
|
+
it 'adds creates an instance, saves it and succeed' do
|
85
|
+
expect{
|
86
|
+
post '/test_models', {:data => 'd'}
|
87
|
+
}.to change(TestModel::ALL, :length).by(1)
|
88
|
+
|
89
|
+
last_response.should be_ok
|
90
|
+
end
|
91
|
+
end
|
92
|
+
context 'with incorrect params' do
|
93
|
+
it 'doesn t create an instance and fails' do
|
94
|
+
expect{
|
95
|
+
post '/test_models', {}
|
96
|
+
}.to change(TestModel::ALL, :length).by(0)
|
97
|
+
|
98
|
+
last_response.should_not be_ok
|
99
|
+
expect(last_response.body).to eq(@errors.to_json)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
context 'when the creation route is disabled' do
|
103
|
+
before do
|
104
|
+
class Yodatra::ModelController
|
105
|
+
disable :create
|
106
|
+
end
|
107
|
+
end
|
108
|
+
it 'should fail with no route available' do
|
109
|
+
post '/test_models', {:data => 'd'}
|
110
|
+
|
111
|
+
last_response.should_not be_ok
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
describe 'deleting a Model instance' do
|
116
|
+
context 'targeting an existing instance' do
|
117
|
+
it 'deletes the instance and succeed' do
|
118
|
+
expect{
|
119
|
+
delete '/test_models/1'
|
120
|
+
}.to change(TestModel::ALL, :length).by(-1)
|
121
|
+
|
122
|
+
last_response.should be_ok
|
123
|
+
end
|
124
|
+
end
|
125
|
+
context 'targeting a not existing instance' do
|
126
|
+
it 'does not delete any instance and fails' do
|
127
|
+
expect{
|
128
|
+
delete '/test_models/6'
|
129
|
+
}.to change(TestModel::ALL, :length).by(0)
|
130
|
+
|
131
|
+
last_response.should_not be_ok
|
132
|
+
end
|
133
|
+
end
|
134
|
+
context 'when the deletion route is disabled' do
|
135
|
+
before do
|
136
|
+
class Yodatra::ModelController
|
137
|
+
disable :delete
|
138
|
+
end
|
139
|
+
end
|
140
|
+
it 'should fail with no route available' do
|
141
|
+
delete '/test_models/2'
|
142
|
+
|
143
|
+
last_response.should_not be_ok
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yodatra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-02-
|
12
|
+
date: 2014-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -124,19 +124,24 @@ email: paul+st@bonaud.fr
|
|
124
124
|
executables: []
|
125
125
|
extensions: []
|
126
126
|
extra_rdoc_files:
|
127
|
-
- README
|
127
|
+
- README.md
|
128
128
|
- LICENSE
|
129
129
|
files:
|
130
130
|
- Gemfile
|
131
131
|
- LICENSE
|
132
|
-
- README
|
132
|
+
- README.md
|
133
|
+
- Rakefile
|
133
134
|
- lib/yodatra.rb
|
134
135
|
- lib/yodatra/api_formatter.rb
|
135
136
|
- lib/yodatra/base.rb
|
136
137
|
- lib/yodatra/boot.rb
|
138
|
+
- lib/yodatra/crypto.rb
|
137
139
|
- lib/yodatra/initializers.rb
|
138
140
|
- lib/yodatra/logger.rb
|
141
|
+
- lib/yodatra/model_controller.rb
|
139
142
|
- lib/yodatra/version.rb
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/unit/model_controller_spec.rb
|
140
145
|
- yodatra.gemspec
|
141
146
|
homepage: http://squareteam.github.io/yodatra
|
142
147
|
licenses:
|
@@ -170,4 +175,5 @@ rubygems_version: 1.8.25
|
|
170
175
|
signing_key:
|
171
176
|
specification_version: 3
|
172
177
|
summary: Classy backend development with the speed of Sinatra and the power of ActiveRecord
|
173
|
-
test_files:
|
178
|
+
test_files:
|
179
|
+
- spec/unit/model_controller_spec.rb
|