ananke 1.1.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,159 +0,0 @@
1
- require 'ananke/linking'
2
- require 'ananke/helpers'
3
- require 'ananke/serialize'
4
- require 'ananke/validation'
5
-
6
- module Ananke
7
- public
8
- class << self
9
- attr_accessor :routes
10
- end
11
-
12
- private
13
- @routes = {}
14
-
15
- def add_route(name, method)
16
- Ananke.routes[name.to_sym] ||= []
17
- Ananke.routes[name.to_sym] << method.to_sym
18
- end
19
-
20
- def build_route(repository_module, repository_method, verb, route, &block)
21
- if repository_module.respond_to? repository_method
22
- define_repository_call(repository_module, repository_method)
23
- add_route(route.split('/')[1], repository_method)
24
- Sinatra::Base.send verb, "#{route}", do
25
- @params = collect_params(@params)
26
- instance_eval(&block)
27
- end
28
- else
29
- out(:warning, "#{repository_module} does not respond to '#{repository_method.to_s}'")
30
- end
31
- end
32
-
33
- def make_response_item(path, mod, link_list, link_to_list, obj, key)
34
- id = get_id(obj, key)
35
- out :info, "#{path} - Cannot find key(#{key}) on object #{obj}" if !id
36
- #dic = {path.to_sym => obj}
37
- dic = Serialize.to_h(obj)
38
- links = build_links(link_list, link_to_list, path, id, mod) if Ananke.settings[:links]
39
- dic[:links] = links if links && !links.empty?
40
- dic
41
- end
42
-
43
- def make_response(path, mod, link_list, link_to_list, obj, key)
44
- if obj.class == Array
45
- result_list = obj.collect{|item| make_response_item(path, mod, link_list, link_to_list, item, key) if item}.compact
46
-
47
- #dic = result_list.empty? ? {} : {"#{path}_list".to_sym => result_list}
48
- dic = result_list.empty? ? {} : {"items".to_sym => result_list}
49
- #link_self = build_link_self(path, '') if Ananke.settings[:links]
50
- #dic[:links] = link_self if link_self
51
-
52
- Serialize.to_j(dic)
53
- else
54
- Serialize.to_j(make_response_item(path, mod, link_list, link_to_list, obj, key))
55
- end
56
- end
57
-
58
- def build(path)
59
- mod = get_repository_module(path)
60
- if mod.nil?
61
- out(:error, "Repository for #{path} not found")
62
- return
63
- end
64
- out :info, "No Id specified for #{path}" if @id.empty?
65
- key = @id[:key]
66
- fields = @fields
67
- link_list = @link_list
68
- link_to_list = @link_to_list
69
- route_for_list = @route_for_list
70
-
71
- #===========================GET/ID=============================
72
- build_route mod, :one, :get, "/#{path}/:#{key}" do
73
- param_missing!(key) if params[key].nil?
74
-
75
- obj, obj_status = repository_call(mod, :one, params)
76
- error obj_status, obj if obj_status >= 400
77
- not_found if obj && obj.class == Array && obj.empty?
78
-
79
- status obj_status
80
- make_response(path, mod, link_list, link_to_list, obj, key)
81
- end
82
-
83
- #===========================GET================================
84
- build_route mod, :all, :get, "/#{path}/?" do
85
- obj, obj_status = repository_call(mod, :all)
86
- error obj_status, obj if obj_status >= 400
87
- not_found if obj && obj.class == Array && obj.empty?
88
-
89
- status obj_status
90
- make_response(path, mod, link_list, link_to_list, obj, key)
91
- end
92
-
93
- #===========================POST===============================
94
- build_route mod, :add, :post, "/#{path}/?" do
95
- status, message = validate(fields, params)
96
- error status, message unless status.nil?
97
-
98
- obj, obj_status = repository_call(mod, :add, params)
99
- error obj_status, obj if obj_status >= 400
100
- not_found if obj && obj.class == Array && obj.empty?
101
-
102
- status 201
103
- make_response(path, mod, link_list, link_to_list, obj, key)
104
- end
105
-
106
- #===========================PUT================================
107
- build_route mod, :edit, :put, "/#{path}/:#{key}" do
108
- param_missing!(key) if params[key].nil?
109
- status, message = validate(fields, params)
110
- error status, message unless status.nil?
111
-
112
- obj, obj_status = repository_call(mod, :edit, params)
113
- error obj_status, obj if obj_status >= 400
114
- not_found if obj && obj.class == Array && obj.empty?
115
-
116
- status obj_status
117
- make_response(path, mod, link_list, link_to_list, obj, key)
118
- end
119
-
120
- build_route mod, :edit, :put, "/#{path}/?" do
121
- param_missing!(key)
122
- end
123
-
124
- #===========================DELETE=============================
125
- build_route mod, :delete, :delete, "/#{path}/:#{key}" do
126
- param_missing!(key) if params[key].nil?
127
-
128
- obj, obj_status = repository_call(mod, :delete, params)
129
- error obj_status, obj if obj_status >= 400
130
- not_found if obj && obj.class == Array && obj.empty?
131
-
132
- status obj_status
133
- end
134
-
135
- build_route mod, :delete, :delete, "/#{path}/?" do
136
- param_missing!(key)
137
- end
138
-
139
- #===========================ROUTE_FOR==========================
140
- route_for_list.each do |r|
141
- inputs = mod.method(r[:name]).parameters
142
- full_path = "/#{path}/#{r[:name]}"
143
- full_path << "/:key" if inputs.length == 1
144
-
145
- build_route mod, r[:name], r[:verb], full_path do
146
- param_missing!(:key) if inputs.length == 1 && params[:key].nil?
147
-
148
- obj, obj_status = repository_call(mod, r[:name], params)
149
- error obj_status, obj if obj_status >= 400
150
- not_found if obj && obj.class == Array && obj.empty?
151
-
152
- obj_list = obj.class == Array ? obj : [obj]
153
-
154
- status obj_status
155
- make_response(path, mod, link_list, link_to_list, obj_list, key).gsub("\"/#{path}/\"", "\"#{request.path}\"")
156
- end
157
- end
158
- end
159
- end
@@ -1,53 +0,0 @@
1
- require 'json'
2
- module Serialize
3
-
4
- def self.can_serialize?(obj)
5
- obj.class != Array and obj.instance_variables.empty?#!obj.to_json.start_with?('"#<')
6
- end
7
-
8
- def self.unaccent(obj)
9
- #obj.class == String ? obj.force_encoding(Encoding::UTF_8) : obj
10
- #puts obj.encoding.name if obj.class == String
11
- obj
12
- end
13
-
14
- def self.to_h(obj)
15
- ret = {}
16
-
17
- if obj.class == Hash
18
- obj.each do |k, v|
19
- ret[k.to_sym] = (can_serialize?(v) ? unaccent(v) : Serialize.to_h(v))
20
- end
21
- elsif obj.class == Array
22
- ret = []
23
- obj.each do |i|
24
- #ret << (can_serialize?(i) ? i : Serialize.to_hash(i))
25
- ret << Serialize.to_h(i)
26
- end
27
- elsif obj.instance_variables.empty?
28
- ret = unaccent(obj)
29
- else
30
- obj.instance_variables.each do |e|
31
- value = obj.instance_variable_get e.to_sym
32
- ret[e[1..-1]] = (can_serialize?(value) ? unaccent(value) : Serialize.to_h(value))
33
- end
34
- end
35
- if ret.class == Hash and Ananke.settings[:remove_empty]
36
- ret.delete_if {|k,v| v.nil? || v == ''}
37
- ret = nil if ret.empty?
38
- elsif ret.class == Array and Ananke.settings[:remove_empty]
39
- ret.delete_if {|i| i.nil? || i == ''}
40
- ret = nil if ret.empty?
41
- end
42
- ret
43
- end
44
-
45
- def self.to_j(obj)
46
- Serialize.to_h(obj).to_json
47
- end
48
-
49
- def self.to_j_pretty(obj)
50
- JSON.pretty_generate(Serialize.to_h(obj), opts = {:indent => ' '})
51
- end
52
-
53
- end
@@ -1,28 +0,0 @@
1
- module Ananke
2
- class << self
3
- attr_accessor :settings
4
- end
5
-
6
- private
7
-
8
- @settings = {
9
- :output => true,
10
- :info => true,
11
- :warning => true,
12
- :error => true,
13
-
14
- :links => true,
15
- :remove_empty => false,
16
- :repository => 'Repository'
17
- }
18
-
19
- public
20
-
21
- def repository
22
- @settings[:repository].to_sym
23
- end
24
-
25
- def set(name, val)
26
- @settings[name] = val
27
- end
28
- end
@@ -1,37 +0,0 @@
1
- module Ananke
2
- class << self
3
- attr_accessor :rules
4
- end
5
-
6
- private
7
-
8
- @rules = [:length]
9
-
10
- def validate(fields, params)
11
- errors = []
12
- fields.each do |field|
13
- value = params[field[:key]]
14
- errors << "Missing Required Parameter: #{field[:key]}" if field[:type] == :required && value.nil?
15
- Ananke::Rules.value = value
16
- field[:rules].each do |r|
17
- res = r.class == Hash ? Ananke::Rules.send("validate_#{r.first[0]}", r.first[1]) : Ananke::Rules.send("validate_#{r}")
18
- errors << "#{field[:key]}: #{res}" unless res.nil?
19
- end
20
- end
21
- return 400, errors unless errors.empty?
22
- end
23
- def param_missing!(key)
24
- error 400, "Missing Parameter: #{key.to_s}"
25
- end
26
-
27
- #===========================Rules==============================
28
-
29
- module Rules
30
- class << self
31
- attr_accessor :value
32
- end
33
- def self.validate_length(min)
34
- value.length >= min ? nil : "Value must be at least #{min} characters long"
35
- end
36
- end
37
- end
data/lib/ananke.rb DELETED
@@ -1,52 +0,0 @@
1
- libdir = File.dirname(__FILE__)
2
- $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
-
4
- require 'colored'
5
- require 'sinatra/base'
6
-
7
- require 'ananke/helpers'
8
- require 'ananke/linking'
9
- require 'ananke/routing'
10
- require 'ananke/settings'
11
- require 'ananke/serialize'
12
- require 'ananke/validation'
13
-
14
- module Ananke
15
- private
16
-
17
- public
18
- #===========================DSL================================
19
- def route(path, &block)
20
- @id = {}
21
- @fields = []
22
- @link_list = []
23
- @link_to_list = []
24
- @route_for_list = []
25
- yield block
26
- build path
27
- end
28
-
29
- def id(key, *rules)
30
- @id = {:key => key, :type => :id, :rules => rules}
31
- end
32
- def required(key, *rules)
33
- @fields << {:key => key, :type => :required, :rules => rules}
34
- end
35
- def optional(key, *rules)
36
- @fields << {:key => key, :type => :optional, :rules => rules}
37
- end
38
- def linked(rel)
39
- @link_list << {:rel => rel}
40
- end
41
- def link_to(rel)
42
- @link_to_list << {:rel => rel}
43
- end
44
- def route_for(rel, verb = :get)
45
- @route_for_list << {:name => rel, :verb => verb}
46
- end
47
- def rule(name, &block)
48
- Ananke::Rules.send(:define_singleton_method, "validate_#{name}", block)
49
- end
50
- end
51
-
52
- include Ananke
data/lib/version.rb DELETED
@@ -1,3 +0,0 @@
1
- module Ananke
2
- VERSION = "1.1.3"
3
- end
data/spec/call_chain.rb DELETED
@@ -1,23 +0,0 @@
1
- class CallChain
2
- def self.caller_file(depth=1)
3
- parse_caller(caller(depth+1).first)[0]
4
- end
5
- def self.caller_line(depth=1)
6
- parse_caller(caller(depth+1).first)[1]
7
- end
8
- def self.caller_method(depth=1)
9
- parse_caller(caller(depth+1).first)[2]
10
- end
11
-
12
- private
13
-
14
- #Stolen from ActionMailer, where this was used but was not made reusable
15
- def self.parse_caller(at)
16
- if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
17
- file = Regexp.last_match[1]
18
- line = Regexp.last_match[2].to_i
19
- method = Regexp.last_match[3]
20
- [file, line, method]
21
- end
22
- end
23
- end
data/spec/cov_adapter.rb DELETED
@@ -1,9 +0,0 @@
1
- require 'simplecov'
2
-
3
- SimpleCov.adapters.define 'cov' do
4
- coverage_dir 'public/coverage'
5
-
6
- add_filter '/dump/'
7
- add_filter '/public/'
8
- add_filter '/spec/'
9
- end
data/spec/dumping.rb DELETED
@@ -1,15 +0,0 @@
1
- require './spec/call_chain'
2
- require 'fileutils'
3
-
4
- def clear_dump
5
- dirname = File.join(File.dirname(__FILE__), "..", "dump")
6
- FileUtils::rm_rf(dirname)
7
- FileUtils::mkdir(dirname)
8
- end
9
- def dump(content, filename)
10
- File.open(File.expand_path(File.join(File.dirname(__FILE__), "..", "dump", "#{filename}")), 'w') {|f| f.write(content) }
11
- end
12
- def check_status(status)
13
- dump(last_response.body, "#{CallChain::caller_file.split('/').last}_#{CallChain::caller_line}.htm") if last_response.status != status
14
- last_response.status.should == status
15
- end
@@ -1,178 +0,0 @@
1
- require './spec/spec_helper'
2
- require './lib/ananke'
3
-
4
- describe 'Basic Ananke REST' do
5
- include Rack::Test::Methods
6
- include Ananke
7
-
8
- def app
9
- Sinatra::Base
10
- end
11
-
12
- before(:all) do
13
- Ananke.set :links, false
14
- end
15
-
16
- after(:all) do
17
- Ananke.set :links, true
18
- end
19
-
20
- #----------------------------SETUP--------------------------------------
21
- it """
22
- Should be able to describe a Valid REST Resource
23
- """ do
24
- route :user do
25
- id :user_id
26
- end
27
- end
28
-
29
- it """
30
- Should skip creating Routes for Non-Existing Repositories
31
- """ do
32
- route :invalid do
33
- end
34
- end
35
-
36
- it """
37
- Should setup the defaults for REST
38
- """ do
39
- Ananke.repository.should == :Repository
40
- end
41
-
42
- it """
43
- Should setup Routes
44
- """ do
45
- Sinatra::Base.routes["GET"][-1][0].inspect.include?('user').should == true
46
- count = 0
47
- Sinatra::Base.routes["GET"].each_index{|i| count += 1 if Sinatra::Base.routes["GET"][i][0].inspect.include?('user')}
48
- count.should == 2
49
- Sinatra::Base.routes["POST"][-1][0].inspect.include?('user').should == true
50
- Sinatra::Base.routes["PUT"][-1][0].inspect.include?('user').should == true
51
- Sinatra::Base.routes["DELETE"][-1][0].inspect.include?('user').should == true
52
- end
53
-
54
- it """
55
- Should expose routes registered
56
- """ do
57
- Ananke.routes[:user].should include :one
58
- Ananke.routes[:user].should include :all
59
- Ananke.routes[:user].should include :add
60
- Ananke.routes[:user].should include :edit
61
- Ananke.routes[:user].should include :delete
62
- end
63
-
64
- #----------------------------BASIC--------------------------------------
65
- it """
66
- GET /user
67
- - code: 200
68
- - content-type: text/plain
69
- - body: [{:id=>1, :name=>'one'}, {:id => 2, :name => 'two'}]
70
- """ do
71
- get "/user"
72
- check_status(200)
73
- last_response.body.should == '{"items":[{"user_id":1,"username":"one"},{"user_id":2,"username":"two"}]}'
74
- end
75
-
76
- it """
77
- GET /user/1
78
- - code: 200
79
- - content-type: text/plain
80
- - body: {user_id: ,username: }
81
- """ do
82
- get "/user/1"
83
- check_status(200)
84
- last_response.body.should == '{"user_id":1,"username":"one"}'
85
- end
86
-
87
- it """
88
- POST /user
89
- - body: {user_id: ,username: }
90
- RETURN
91
- - code: 201
92
- - content-type: text/json
93
- - body:
94
- """ do
95
- post "/user", body={:user_id => 3, :username => 'three'}
96
- check_status(201)
97
- end
98
-
99
- it """
100
- PUT /user/3
101
- - body: {user_id: ,username: }
102
- RETURN
103
- - code: 200
104
- - content-type: text/json
105
- - body:
106
- """ do
107
- put "/user/3", body={:username => 'four'}
108
- check_status(200)
109
- end
110
-
111
- it """
112
- DELETE /user/3
113
- RETURN
114
- - code: 200
115
- - content-type: text/json
116
- - body:
117
- """ do
118
- delete "/user/3"
119
- check_status(200)
120
- end
121
-
122
- #----------------------------FAILS--------------------------------------
123
- it """
124
- PUT /user
125
- - body: {user_id: ,username: }
126
- RETURN
127
- - code: 400
128
- - content-type: text/json
129
- - body: Missing Parameter: user_id
130
- """ do
131
- put "/user", body={:username => 'four'}
132
- check_status(400)
133
- last_response.body.should == 'Missing Parameter: user_id'
134
- end
135
-
136
- it """
137
- DELETE /user
138
- RETURN
139
- - code: 400
140
- - content-type: text/json
141
- - body: Missing Parameter: user_id
142
- """ do
143
- delete "/user"
144
- check_status(400)
145
- last_response.body.should == 'Missing Parameter: user_id'
146
- end
147
- end
148
-
149
- module Repository
150
- module User
151
- @data = [{:user_id => 1, :username => 'one'},
152
- {:user_id => 2, :username => 'two'}]
153
-
154
- def self.data
155
- @data
156
- end
157
-
158
- def self.one(user_id)
159
- @data[@data.index{ |d| d[:user_id] == user_id.to_i}]
160
- end
161
- def self.all
162
- @data
163
- end
164
- def self.add(user_id, username)
165
- obj = {:user_id => user_id.to_i, :username => username}
166
- @data << obj
167
- obj
168
- end
169
- def self.edit(user_id, username)
170
- obj = {:user_id => user_id.to_i, :username => username}
171
- @data[@data.index{|i| i[:user_id] == user_id.to_i}] = obj
172
- obj
173
- end
174
- def self.delete(user_id)
175
- @data.delete_if { |i| i[:user_id] == user_id.to_i}
176
- end
177
- end
178
- end
@@ -1,73 +0,0 @@
1
- require './spec/spec_helper'
2
- require './lib/ananke'
3
-
4
- describe 'Resource Route-For' do
5
- include Rack::Test::Methods
6
- include Ananke
7
-
8
- def app
9
- Sinatra::Base
10
- end
11
-
12
- it """
13
- should return a code 400 for an error raised in the Repository that has a message that starts with the method name
14
- """ do
15
- module Repository
16
- module Errors
17
- def self.exception400(id)
18
- raise 'exception400 - Some Exception'
19
- end
20
- end
21
- end
22
- route :errors do
23
- route_for :exception400
24
- end
25
-
26
- get "/errors/exception400/1"
27
- check_status(400)
28
- last_response.body.should == 'Some Exception'
29
- end
30
-
31
- it """
32
- should return a code 500 for an error raised in the Repository
33
- """ do
34
- module Repository
35
- module Errors
36
- def self.exception500(id)
37
- raise 'Some Exception'
38
- end
39
- end
40
- end
41
- route :errors do
42
- route_for :exception500
43
- end
44
-
45
- get "/errors/exception500/1"
46
- check_status(500)
47
- end
48
-
49
- it """
50
- should return a code 404 for an empty Array object returned
51
- """ do
52
- module Repository
53
- module Errors
54
- def self.one(id)
55
- []
56
- end
57
- def self.empty(id)
58
- []
59
- end
60
- end
61
- end
62
- route :errors do
63
- id :id
64
- route_for :empty
65
- end
66
-
67
- get "/errors/empty/1"
68
- check_status(404)
69
-
70
- get "/errors/1"
71
- check_status(404)
72
- end
73
- end
@@ -1,51 +0,0 @@
1
- require './spec/spec_helper'
2
-
3
- describe 'Ananke - JSON Body' do
4
- include Rack::Test::Methods
5
- include Ananke
6
-
7
- def app
8
- Sinatra::Base
9
- end
10
-
11
- it "should be able to parse a JSON Body in a basic POST route" do
12
- post "/json", body={:name => 'one', :surname => 'sur_one'}.to_json
13
- check_status(201)
14
- end
15
-
16
- it "should be able to parse a JSON Body in a basic PUT route" do
17
- put "/json/1", body={:name => 'one', :surname => 'sur_one'}.to_json
18
- check_status(200)
19
- end
20
-
21
- it "should be able to parse a JSON Body in a Custom Route" do
22
- get "/json/custom", body={:name => 'one', :surname => 'sur_one'}.to_json
23
- check_status(200)
24
- end
25
-
26
- module Repository
27
- module Json
28
- @data = []
29
- def self.add(name, surname)
30
- id = @data.empty? && 1 || @data.last[:id] + 1
31
- obj = {:id => id, :name => name, :surname => surname}
32
- @data << obj
33
- obj
34
- end
35
- def self.edit(id, name, surname)
36
- obj = {:id => id, :name => name, :surname => surname}
37
- @data[@data.index{|i| i[:id] == id.to_i}] = obj
38
- obj
39
- end
40
- def self.custom(name, surname)
41
- @data[@data.index{|i| i[:name] == name && i[:surname] == surname}]
42
- end
43
- end
44
- end
45
- route :json do
46
- id :id
47
- required :name
48
- required :surname
49
- route_for :custom
50
- end
51
- end