crepe 0.0.1.pre
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/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: []
|