ananke 1.1.3 → 2.0.0

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.
@@ -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