crepe 0.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/crepe.rb +10 -0
- data/lib/crepe/api.rb +242 -0
- data/lib/crepe/endpoint.rb +173 -0
- data/lib/crepe/endpoint/filter.rb +10 -0
- data/lib/crepe/endpoint/filter/acceptance.rb +34 -0
- data/lib/crepe/endpoint/filter/parser.rb +43 -0
- data/lib/crepe/endpoint/renderer.rb +19 -0
- data/lib/crepe/endpoint/renderer/base.rb +95 -0
- data/lib/crepe/endpoint/renderer/simple.rb +21 -0
- data/lib/crepe/endpoint/renderer/tilt.rb +87 -0
- data/lib/crepe/endpoint/request.rb +59 -0
- data/lib/crepe/middleware.rb +9 -0
- data/lib/crepe/middleware/content_negotiation.rb +110 -0
- data/lib/crepe/middleware/head.rb +30 -0
- data/lib/crepe/middleware/restful_status.rb +33 -0
- data/lib/crepe/params.rb +74 -0
- data/lib/crepe/util.rb +73 -0
- data/lib/crepe/util/chained_include.rb +78 -0
- data/lib/crepe/util/hash_stack.rb +57 -0
- data/lib/crepe/version.rb +10 -0
- metadata +207 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Crepe
|
2
|
+
module Middleware
|
3
|
+
# This middleware wraps boths sides of a request.
|
4
|
+
#
|
5
|
+
# Going in, it munges the Rack environment so that a HEAD request
|
6
|
+
# masquerades as a GET request by the time it hits a Crepe Endpoint. The
|
7
|
+
# Crepe request helper will know it's a HEAD request by referring to the
|
8
|
+
# `crepe.original_request_method` environment value.
|
9
|
+
#
|
10
|
+
# Going out, it ensures an empty response body.
|
11
|
+
class Head
|
12
|
+
|
13
|
+
def initialize app
|
14
|
+
@app = app
|
15
|
+
end
|
16
|
+
|
17
|
+
def call env
|
18
|
+
if env['REQUEST_METHOD'] == 'HEAD'
|
19
|
+
env['crepe.original_request_method'] = 'HEAD'
|
20
|
+
env['REQUEST_METHOD'] = 'GET'
|
21
|
+
status, headers, _ = @app.call env
|
22
|
+
[status, headers, []]
|
23
|
+
else
|
24
|
+
@app.call env
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Crepe
|
2
|
+
module Middleware
|
3
|
+
# This middleware provides intelligent defaults for response status codes
|
4
|
+
# depending on the HTTP verb:
|
5
|
+
#
|
6
|
+
# - POST will provide 201 Created.
|
7
|
+
#
|
8
|
+
# - DELETE will provide 204 No Content (and clear the response body).
|
9
|
+
class RestfulStatus
|
10
|
+
|
11
|
+
def initialize app
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call env
|
16
|
+
status, headers, body = @app.call env
|
17
|
+
|
18
|
+
if status == 200
|
19
|
+
case env['REQUEST_METHOD']
|
20
|
+
when 'POST'
|
21
|
+
status = 201
|
22
|
+
when 'DELETE'
|
23
|
+
status = 204
|
24
|
+
body = []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[status, headers, body]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/crepe/params.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
require 'crepe/util'
|
3
|
+
|
4
|
+
module Crepe
|
5
|
+
#--
|
6
|
+
# Based on https://github.com/rails/strong_parameters, provides a security
|
7
|
+
# proxy object for submitted parameters.
|
8
|
+
#++
|
9
|
+
class Params < BasicObject
|
10
|
+
|
11
|
+
instance_methods.grep(/^[^_]/).each { |m| undef_method m }
|
12
|
+
|
13
|
+
# Raised when a required parameter is missing (see Crepe::Params#require).
|
14
|
+
class Missing < ::IndexError
|
15
|
+
|
16
|
+
attr_reader :key
|
17
|
+
|
18
|
+
def initialize key
|
19
|
+
@key = key
|
20
|
+
super "Missing parameter: #{key}"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Raised when an unpermitted parameter is found (see Crepe::Params#permit).
|
26
|
+
class Invalid < ::StandardError
|
27
|
+
|
28
|
+
attr_reader :keys
|
29
|
+
|
30
|
+
def initialize keys
|
31
|
+
@keys = keys
|
32
|
+
super "Invalid parameter(s): #{keys.join ', '}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize params = {}
|
38
|
+
@params = ::HashWithIndifferentAccess.new params
|
39
|
+
::Crepe::Util.deep_freeze @params
|
40
|
+
@permitted = false
|
41
|
+
end
|
42
|
+
|
43
|
+
def require required_key
|
44
|
+
fetch(required_key) { raise Missing, required_key }
|
45
|
+
end
|
46
|
+
|
47
|
+
def permit *secure_keys
|
48
|
+
insecure_keys = keys - secure_keys.map(&:to_s)
|
49
|
+
raise Invalid, insecure_keys unless insecure_keys.empty?
|
50
|
+
@permitted = true
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def permitted?
|
55
|
+
@permitted
|
56
|
+
end
|
57
|
+
|
58
|
+
def dup
|
59
|
+
@params.dup
|
60
|
+
end
|
61
|
+
|
62
|
+
def respond_to? method_name, include_private = false
|
63
|
+
[:require, :permit, :permitted?].include? method_name or super
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def method_missing method_name, *args, &block
|
69
|
+
value = @params.send method_name, *args, &block
|
70
|
+
value.is_a?(::Hash) ? ::Crepe::Params.new(value) : value
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/crepe/util.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'active_support/core_ext/object/duplicable'
|
2
|
+
|
3
|
+
module Crepe
|
4
|
+
module Util
|
5
|
+
|
6
|
+
autoload :HashStack, 'crepe/util/hash_stack'
|
7
|
+
autoload :ChainedInclude, 'crepe/util/chained_include'
|
8
|
+
|
9
|
+
# Deeply duplicates values in the object passed in. If the object
|
10
|
+
# is a Hash or Array, it recursively dups the object's values.
|
11
|
+
#
|
12
|
+
# Active Support's deep_dup does not dup array contents.
|
13
|
+
def deep_dup object
|
14
|
+
object = object.dup if object.duplicable?
|
15
|
+
|
16
|
+
case object
|
17
|
+
when Hash then object.each { |k, v| object[k] = deep_dup v }
|
18
|
+
when Array then object.map! { |o| deep_dup o }
|
19
|
+
end
|
20
|
+
|
21
|
+
object
|
22
|
+
end
|
23
|
+
|
24
|
+
# See `deeper_merge!`: returns a copy of the original hash, rather
|
25
|
+
# than merging it in place.
|
26
|
+
def deeper_merge hash, other_hash
|
27
|
+
deeper_merge! hash.dup, other_hash
|
28
|
+
end
|
29
|
+
|
30
|
+
# Deeply merges the first hash with the second hash, concatenating
|
31
|
+
# any array values that are shared across keys.
|
32
|
+
def deeper_merge! hash, other_hash
|
33
|
+
other_hash.each do |key, value|
|
34
|
+
if hash[key].is_a?(Hash) && value.is_a?(Hash)
|
35
|
+
hash[key] = deeper_merge hash[key], value
|
36
|
+
elsif hash[key].is_a?(Array) && value.is_a?(Array)
|
37
|
+
hash[key] += value
|
38
|
+
else
|
39
|
+
hash[key] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
|
46
|
+
# Recursively freezes all keys and values.
|
47
|
+
def deep_freeze hash
|
48
|
+
hash.each do |key, value|
|
49
|
+
case value
|
50
|
+
when Hash then deep_freeze value
|
51
|
+
when Array then value.each { |v| deep_freeze v }
|
52
|
+
else value.freeze
|
53
|
+
end
|
54
|
+
end
|
55
|
+
hash.freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
def normalize_path path
|
59
|
+
normalize_path! path.dup
|
60
|
+
end
|
61
|
+
|
62
|
+
def normalize_path! path
|
63
|
+
path.squeeze! '/'
|
64
|
+
path.sub! %r{/+\z}, ''
|
65
|
+
path.sub! %r{/([.?])}, '\1'
|
66
|
+
path.replace '/' if path.empty?
|
67
|
+
path
|
68
|
+
end
|
69
|
+
|
70
|
+
extend self
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Crepe
|
2
|
+
module Util
|
3
|
+
# Forwards all future includes to objects that have already been
|
4
|
+
# extended by (or have included) the parent module.
|
5
|
+
#
|
6
|
+
# Default Ruby behavior:
|
7
|
+
#
|
8
|
+
# # A module is defined...
|
9
|
+
# module A
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# # ...and included in a class.
|
13
|
+
# class B
|
14
|
+
# include Module A
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # Given another module...
|
18
|
+
# module C
|
19
|
+
# def c
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # ...included in the first...
|
24
|
+
# module A
|
25
|
+
# include Module C
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # ...the class will not have access to its methods.
|
29
|
+
# B.new.c # NoMethodError: undefined method `c' for #<B>
|
30
|
+
#
|
31
|
+
# To prevent the above error, the original module could have been
|
32
|
+
# extended with ChainedInclude:
|
33
|
+
#
|
34
|
+
# module A
|
35
|
+
# extend ChainedInclude
|
36
|
+
# end
|
37
|
+
# class B
|
38
|
+
# include Module A
|
39
|
+
# end
|
40
|
+
# module C
|
41
|
+
# def c
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# module A
|
45
|
+
# include Module C
|
46
|
+
# end
|
47
|
+
# B.new.c # => nil
|
48
|
+
module ChainedInclude
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def include mod
|
53
|
+
super
|
54
|
+
extending.each { |object| object.extend mod }
|
55
|
+
included_by.each { |base| base.__send__ :include, mod }
|
56
|
+
end
|
57
|
+
|
58
|
+
def extended object
|
59
|
+
super
|
60
|
+
extending << object
|
61
|
+
end
|
62
|
+
|
63
|
+
def included base
|
64
|
+
super
|
65
|
+
included_by << base
|
66
|
+
end
|
67
|
+
|
68
|
+
def extending
|
69
|
+
@_extending ||= []
|
70
|
+
end
|
71
|
+
|
72
|
+
def included_by
|
73
|
+
@_included_by ||= []
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'active_support/core_ext/module/delegation'
|
3
|
+
|
4
|
+
module Crepe
|
5
|
+
module Util
|
6
|
+
# A {Hash}-like object that scopes state changes using an underlying stack.
|
7
|
+
class HashStack
|
8
|
+
|
9
|
+
def initialize first = {}
|
10
|
+
@stack = Array.wrap first
|
11
|
+
end
|
12
|
+
|
13
|
+
delegate :pop, :push, :<<,
|
14
|
+
to: :stack
|
15
|
+
|
16
|
+
def top
|
17
|
+
stack.last
|
18
|
+
end
|
19
|
+
|
20
|
+
delegate :[]=, :delete,
|
21
|
+
to: :top
|
22
|
+
|
23
|
+
def key? key
|
24
|
+
stack.any? { |frame| frame.key? key }
|
25
|
+
end
|
26
|
+
|
27
|
+
def [] key
|
28
|
+
found_at = stack.reverse.detect { |frame| frame.key? key }
|
29
|
+
found_at && found_at[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
def all key
|
33
|
+
stack.map { |frame| frame[key] }.flatten 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def with frame = {}
|
37
|
+
self << frame
|
38
|
+
yield
|
39
|
+
pop
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
stack.inject({}, &:merge)
|
44
|
+
end
|
45
|
+
alias_method :to_h, :to_hash
|
46
|
+
|
47
|
+
def dup
|
48
|
+
self.class.new Util.deep_dup stack
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :stack
|
52
|
+
private :stack
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
metadata
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crepe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Celis
|
8
|
+
- Evan Owen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.2.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 3.2.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rack
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.5.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.5.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rack-mount
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 0.8.0
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.8.0
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: cane
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.3.0
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.3.0
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: multi_json
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.6.0
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.6.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: multi_xml
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.5.0
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ~>
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.5.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rake
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ~>
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 10.0.0
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ~>
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 10.0.0
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rspec
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 2.13.0
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.13.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rack-test
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ~>
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 0.6.0
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ~>
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 0.6.0
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: tilt
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ~>
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 1.3.0
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ~>
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: 1.3.0
|
154
|
+
description: |
|
155
|
+
Rack-based API framework
|
156
|
+
email:
|
157
|
+
- stephen@stephencelis.com
|
158
|
+
- kainosnoema@gmail.com
|
159
|
+
executables: []
|
160
|
+
extensions: []
|
161
|
+
extra_rdoc_files: []
|
162
|
+
files:
|
163
|
+
- lib/crepe/api.rb
|
164
|
+
- lib/crepe/endpoint/filter/acceptance.rb
|
165
|
+
- lib/crepe/endpoint/filter/parser.rb
|
166
|
+
- lib/crepe/endpoint/filter.rb
|
167
|
+
- lib/crepe/endpoint/renderer/base.rb
|
168
|
+
- lib/crepe/endpoint/renderer/simple.rb
|
169
|
+
- lib/crepe/endpoint/renderer/tilt.rb
|
170
|
+
- lib/crepe/endpoint/renderer.rb
|
171
|
+
- lib/crepe/endpoint/request.rb
|
172
|
+
- lib/crepe/endpoint.rb
|
173
|
+
- lib/crepe/middleware/content_negotiation.rb
|
174
|
+
- lib/crepe/middleware/head.rb
|
175
|
+
- lib/crepe/middleware/restful_status.rb
|
176
|
+
- lib/crepe/middleware.rb
|
177
|
+
- lib/crepe/params.rb
|
178
|
+
- lib/crepe/util/chained_include.rb
|
179
|
+
- lib/crepe/util/hash_stack.rb
|
180
|
+
- lib/crepe/util.rb
|
181
|
+
- lib/crepe/version.rb
|
182
|
+
- lib/crepe.rb
|
183
|
+
homepage: https://github.com/stephencelis/crepe
|
184
|
+
licenses:
|
185
|
+
- MIT
|
186
|
+
metadata: {}
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - '>='
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - '>'
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: 1.3.1
|
201
|
+
requirements: []
|
202
|
+
rubyforge_project:
|
203
|
+
rubygems_version: 2.0.2
|
204
|
+
signing_key:
|
205
|
+
specification_version: 4
|
206
|
+
summary: Rack-based API framework
|
207
|
+
test_files: []
|