pendragon 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ require 'pendragon/padrino/router'
2
+ require 'pendragon/padrino/route'
3
+ require 'pendragon/padrino/ext/instance_methods'
4
+ require 'pendragon/padrino/ext/class_methods'
5
+
6
+ module Pendragon
7
+ module Padrino
8
+ class << self
9
+ def registered(app)
10
+ app.extend(ClassMethods)
11
+ app.send(:include, InstanceMethods)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,306 @@
1
+ module Pendragon
2
+ module Padrino
3
+ module ClassMethods
4
+ CONTENT_TYPE_ALIASES = {:htm => :html} unless defined?(CONTENT_TYPE_ALIASES)
5
+ ROUTE_PRIORITY = {:high => 0, :normal => 1, :low => 2} unless defined?(ROUTE_PRIORITY)
6
+
7
+ def router
8
+ @router ||= ::Pendragon::Padrino::Router.new
9
+ block_given? ? yield(@router) : @router
10
+ end
11
+
12
+ def compiled_router
13
+ if @deferred_routes
14
+ deferred_routes.each do |routes|
15
+ routes.each do |(route, dest)|
16
+ route.to(&dest)
17
+ route.before_filters.flatten!
18
+ route.after_filters.flatten!
19
+ end
20
+ end
21
+ @deferred_routes = nil
22
+ end
23
+ router
24
+ end
25
+
26
+ def deferred_routes
27
+ @deferred_routes ||= ROUTE_PRIORITY.map{[]}
28
+ end
29
+
30
+ def url(*args)
31
+ params = args.extract_options! # parameters is hash at end
32
+ names, params_array = args.partition{|a| a.is_a?(Symbol)}
33
+ name = names[0, 2].join(" ").to_sym # route name is concatenated with underscores
34
+ if params.is_a?(Hash)
35
+ params[:format] = params[:format].to_s unless params[:format].nil?
36
+ params = value_to_param(params)
37
+ end
38
+ url =
39
+ if params_array.empty?
40
+ compiled_router.path(name, params)
41
+ else
42
+ compiled_router.path(name, *(params_array << params))
43
+ end
44
+ rebase_url(url)
45
+ rescue Pendragon::InvalidRouteException
46
+ route_error = "route mapping for url(#{name.inspect}) could not be found!"
47
+ raise ::Padrino::Routing::UnrecognizedException.new(route_error)
48
+ end
49
+ alias :url_for :url
50
+
51
+ def recognize_path(path)
52
+ responses = @router.recognize_path(path)
53
+ [responses[0], responses[1]]
54
+ end
55
+
56
+ def rebase_url(url)
57
+ if url.start_with?('/')
58
+ new_url = ''
59
+ new_url << conform_uri(uri_root) if defined?(uri_root)
60
+ new_url << conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
61
+ new_url << url
62
+ else
63
+ url.blank? ? '/' : url
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def route(verb, path, *args, &block)
70
+ options = case args.size
71
+ when 2
72
+ args.last.merge(:map => args.first)
73
+ when 1
74
+ map = args.shift if args.first.is_a?(String)
75
+ if args.first.is_a?(Hash)
76
+ map ? args.first.merge(:map => map) : args.first
77
+ else
78
+ {:map => map || args.first}
79
+ end
80
+ when 0
81
+ {}
82
+ else raise
83
+ end
84
+
85
+ route_options = options.dup
86
+ route_options[:provides] = @_provides if @_provides
87
+
88
+ if allow_disabled_csrf
89
+ unless route_options[:csrf_protection] == false
90
+ route_options[:csrf_protection] = true
91
+ end
92
+ end
93
+
94
+ path, *route_options[:with] = path if path.is_a?(Array)
95
+ action = path
96
+ path, name, route_parents, options, route_options = *parse_route(path, route_options, verb)
97
+ options.reverse_merge!(@_conditions) if @_conditions
98
+
99
+ method_name = "#{verb} #{path}"
100
+ unbound_method = generate_method(method_name, &block)
101
+
102
+ block = block.arity != 0 ?
103
+ proc {|a,p| unbound_method.bind(a).call(*p) } :
104
+ proc {|a,p| unbound_method.bind(a).call }
105
+
106
+ invoke_hook(:route_added, verb, path, block)
107
+
108
+ # Pendragon route construction
109
+ path[0, 0] = "/" if path == "(.:format)?"
110
+ route_options.merge!(:name => name) if name
111
+ route = router.add(verb.downcase.to_sym, path, route_options)
112
+ route.action = action
113
+ priority_name = options.delete(:priority) || :normal
114
+ priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}")
115
+ route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
116
+ route.parent = route_parents ? (route_parents.count == 1 ? route_parents.first : route_parents) : route_parents
117
+ route.host = options.delete(:host) if options.key?(:host)
118
+ route.user_agent = options.delete(:agent) if options.key?(:agent)
119
+ if options.key?(:default_values)
120
+ defaults = options.delete(:default_values)
121
+ route.options[:default_values] = defaults if defaults
122
+ end
123
+ options.delete_if do |option, captures|
124
+ if route.significant_variable_names.include?(option)
125
+ route.capture[option] = Array(captures).first
126
+ true
127
+ end
128
+ end
129
+
130
+ # Add Sinatra conditions
131
+ options.each {|o, a| route.respond_to?("#{o}=") ? route.send("#{o}=", a) : send(o, *a) }
132
+ conditions, @conditions = @conditions, []
133
+ route.custom_conditions.concat(conditions)
134
+
135
+ invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
136
+
137
+ # Add Application defaults
138
+ route.before_filters << @filters[:before]
139
+ route.after_filters << @filters[:after]
140
+ if @_controller
141
+ route.use_layout = @layout
142
+ route.controller = Array(@_controller)[0].to_s
143
+ end
144
+
145
+ deferred_routes[priority] << [route, block]
146
+
147
+ route
148
+ end
149
+
150
+ def parse_route(path, options, verb)
151
+ route_options = {}
152
+
153
+ # We need check if path is a symbol, if that it's a named route.
154
+ map = options.delete(:map)
155
+
156
+ # path i.e :index or :show
157
+ if path.kind_of?(Symbol)
158
+ name = path
159
+ path = map ? map.dup : (path == :index ? '/' : path.to_s)
160
+ end
161
+
162
+ # Build our controller
163
+ controller = Array(@_controller).map(&:to_s)
164
+
165
+ case path
166
+ when String # path i.e "/index" or "/show"
167
+ # Now we need to parse our 'with' params
168
+ if with_params = options.delete(:with)
169
+ path = process_path_for_with_params(path, with_params)
170
+ end
171
+
172
+ # Now we need to parse our provides
173
+ options.delete(:provides) if options[:provides].nil?
174
+
175
+ if @_use_format or format_params = options[:provides]
176
+ process_path_for_provides(path, format_params)
177
+ # options[:add_match_with] ||= {}
178
+ # options[:add_match_with][:format] = /[^\.]+/
179
+ end
180
+
181
+ absolute_map = map && map[0] == ?/
182
+
183
+ unless controller.empty?
184
+ # Now we need to add our controller path only if not mapped directly
185
+ if map.blank? and !absolute_map
186
+ controller_path = controller.join("/")
187
+ path.gsub!(%r{^\(/\)|/\?}, "")
188
+ path = File.join(controller_path, path) unless @_map
189
+ end
190
+ end
191
+
192
+ # Now we need to parse our 'parent' params and parent scope.
193
+ if !absolute_map and parent_params = options.delete(:parent) || @_parents
194
+ parent_params = (Array(@_parents) + Array(parent_params)).uniq
195
+ path = process_path_for_parent_params(path, parent_params)
196
+ end
197
+
198
+ # Add any controller level map to the front of the path.
199
+ path = "#{@_map}/#{path}".squeeze('/') unless absolute_map or @_map.blank?
200
+
201
+ # Small reformats
202
+ path.gsub!(%r{/\?$}, '(/)') # Remove index path
203
+ path.gsub!(%r{//$}, '/') # Remove index path
204
+ path[0,0] = "/" if path !~ %r{^\(?/} # Paths must start with a /
205
+ path.sub!(%r{/(\))?$}, '\\1') if path != "/" # Remove latest trailing delimiter
206
+ path.gsub!(/\/(\(\.|$)/, '\\1') # Remove trailing slashes
207
+ path.squeeze!('/')
208
+ when Regexp
209
+ route_options[:path_for_generation] = options.delete(:generate_with) if options.key?(:generate_with)
210
+ end
211
+
212
+ name = options.delete(:route_name) if name.nil? && options.key?(:route_name)
213
+ name = options.delete(:name) if name.nil? && options.key?(:name)
214
+ if name
215
+ controller_name = controller.join("_")
216
+ name = "#{controller_name} #{name}".to_sym unless controller_name.blank?
217
+ end
218
+
219
+ # Merge in option defaults.
220
+ options.reverse_merge!(:default_values => @_defaults)
221
+
222
+ [path, name, parent_params, options, route_options]
223
+ end
224
+
225
+ def provides(*types)
226
+ @_use_format = true
227
+ condition do
228
+ mime_types = types.map {|t| mime_type(t) }.compact
229
+ url_format = params[:format].to_sym if params[:format]
230
+ accepts = request.accept.map {|a| a.to_str }
231
+ accepts = [] if accepts == ["*/*"]
232
+
233
+ # per rfc2616-sec14:
234
+ # Assume */* if no ACCEPT header is given.
235
+ catch_all = (accepts.delete "*/*" || accepts.empty?)
236
+ matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
237
+ if matching_types.empty? && types.include?(:any)
238
+ matching_types = accepts
239
+ end
240
+
241
+ if !url_format && matching_types.first
242
+ type = ::Rack::Mime::MIME_TYPES.find {|k, v| v == matching_types.first }[0].sub(/\./,'').to_sym
243
+ accept_format = CONTENT_TYPE_ALIASES[type] || type
244
+ elsif catch_all && !types.include?(:any)
245
+ type = types.first
246
+ accept_format = CONTENT_TYPE_ALIASES[type] || type
247
+ end
248
+
249
+ matched_format = types.include?(:any) ||
250
+ types.include?(accept_format) ||
251
+ types.include?(url_format) ||
252
+ ((!url_format) && request.accept.empty? && types.include?(:html))
253
+ # per rfc2616-sec14:
254
+ # answer with 406 if accept is given but types to not match any
255
+ # provided type
256
+ halt 406 if
257
+ (!url_format && !accepts.empty? && !matched_format) ||
258
+ (settings.respond_to?(:treat_format_as_accept) && settings.treat_format_as_accept && url_format && !matched_format)
259
+
260
+ if matched_format
261
+ @_content_type = url_format || accept_format || :html
262
+ content_type(@_content_type, :charset => 'utf-8')
263
+ end
264
+
265
+ matched_format
266
+ end
267
+ end
268
+
269
+ def process_path_for_parent_params(path, parent_params)
270
+ parent_prefix = parent_params.flatten.compact.uniq.map do |param|
271
+ map = (param.respond_to?(:map) && param.map ? param.map : param.to_s)
272
+ part = "#{map}/:#{param.to_s.singularize}_id/"
273
+ part = "(#{part})?" if param.respond_to?(:optional) && param.optional?
274
+ part
275
+ end
276
+
277
+ [parent_prefix, path].flatten.join("")
278
+ end
279
+
280
+ def process_path_for_provides(path, format_params)
281
+ path << "(.:format)?" unless path[-11, 11] == '(.:format)?'
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ if defined?(Padrino) && Padrino::VERSION < '0.12.0'
288
+ module Padrino
289
+ class Filter
290
+ def apply?(request)
291
+ detect = @args.any? do |arg|
292
+ case arg
293
+ when Symbol then request.route_obj && (request.route_obj.name == arg or request.route_obj.name == [@scoped_controller, arg].flatten.join(" ").to_sym)
294
+ else arg === request.path_info
295
+ end
296
+ end || @options.any? do |name, val|
297
+ case name
298
+ when :agent then val === request.user_agent
299
+ else val === request.send(name)
300
+ end
301
+ end
302
+ detect ^ !@mode
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,63 @@
1
+ module Pendragon
2
+ module Padrino
3
+ module InstanceMethods
4
+ private
5
+
6
+ def invoke_route(route, params, options = {})
7
+ original_params, parent_layout, successful = @params.dup, @layout, false
8
+ captured_params = params[:captures].is_a?(Array) ? params.delete(:captures) :
9
+ params.values_at(*route.matcher.names.dup)
10
+
11
+ @_response_buffer = nil
12
+ @route = request.route_obj = route
13
+ @params.merge!(params) if params.is_a?(Hash)
14
+ @params.merge!(:captures => captured_params) if !captured_params.empty? && route.path.is_a?(Regexp)
15
+ @block_params = params
16
+
17
+ filter! :before if options[:first]
18
+
19
+ catch(:pass) do
20
+ begin
21
+ (route.before_filters - settings.filters[:before]).each{|block| instance_eval(&block) }
22
+ @layout = route.use_layout if route.use_layout
23
+ route.custom_conditions.each {|block| pass if block.bind(self).call == false }
24
+ halt_response = catch(:halt){ route_eval{ route.block[self, captured_params] }}
25
+ @_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response
26
+ successful = true
27
+ halt(halt_response)
28
+ ensure
29
+ (route.after_filters - settings.filters[:after]).each {|block| instance_eval(&block) } if successful
30
+ @layout, @params = parent_layout, original_params
31
+ end
32
+ end
33
+ end
34
+
35
+ def route!(base = settings, pass_block = nil)
36
+ Thread.current['padrino.instance'] = self
37
+ code, headers, routes = base.compiled_router.call(@request.env)
38
+
39
+ status(code)
40
+ if code == 200
41
+ routes.each_with_index do |(route, pendragon_params), index|
42
+ next if route.user_agent && !(route.user_agent =~ @request.user_agent)
43
+ invoke_route(route, pendragon_params, :first => index.zero?)
44
+ end
45
+ else
46
+ route_eval do
47
+ headers.each{|k, v| response[k] = v } unless headers.empty?
48
+ route_missing if code == 404
49
+ route_missing if allow = response['Allow'] and allow.include?(request.env['REQUEST_METHOD'])
50
+ end
51
+ end
52
+
53
+ if base.superclass.respond_to?(:router)
54
+ route!(base.superclass, pass_block)
55
+ return
56
+ end
57
+
58
+ route_eval(&pass_block) if pass_block
59
+ route_missing
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ require 'pendragon/route'
2
+
3
+ module Pendragon
4
+ module Padrino
5
+ class Route < ::Pendragon::Route
6
+ attr_accessor :action, :cache, :cache_key, :cache_expires_in, :parent,
7
+ :use_layout, :controller, :user_agent, :path_for_generation
8
+
9
+ def before_filters(&block)
10
+ @_before_filters ||= []
11
+ @_before_filters << block if block_given?
12
+ @_before_filters
13
+ end
14
+
15
+ def after_filters(&block)
16
+ @_after_filters ||= []
17
+ @_after_filters << block if block_given?
18
+ @_after_filters
19
+ end
20
+
21
+ def custom_conditions(&block)
22
+ @_custom_conditions ||= []
23
+ @_custom_conditions << block if block_given?
24
+ @_custom_conditions
25
+ end
26
+
27
+ def call(app, *args)
28
+ @block.call(app, *args)
29
+ end
30
+
31
+ def request_methods
32
+ [verb.to_s.upcase]
33
+ end
34
+
35
+ def original_path
36
+ @path
37
+ end
38
+
39
+ def significant_variable_names
40
+ @significant_variable_names ||= if @path.is_a?(String)
41
+ @path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
42
+ elsif @path.is_a?(Regexp) and @path.respond_to?(:named_captures)
43
+ @path.named_captures.keys.map(&:to_sym)
44
+ else
45
+ []
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ require 'pendragon' unless defined?(Pendragon::Router)
2
+
3
+ module Pendragon
4
+ module Padrino
5
+ class Router < Pendragon::Router
6
+ def add(verb, path, options = {}, &block)
7
+ route = Route.new(path, verb, options, &block)
8
+ route.path_for_generation = options[:path_for_generation] if options[:path_for_generation]
9
+ route.router = self
10
+ routes << route
11
+ route
12
+ end
13
+
14
+ def call(env)
15
+ request = Rack::Request.new(env)
16
+ raise BadRequest unless valid_verb?(request.request_method)
17
+ prepare! unless prepared?
18
+ [200, {}, recognize(request)]
19
+ rescue BadRequest, NotFound, MethodNotAllowed
20
+ $!.call
21
+ end
22
+
23
+ def path(name, *args)
24
+ params = args.delete_at(args.last.is_a?(Hash) ? -1 : 0) || {}
25
+ saved_args = args.dup
26
+ @routes.each do |route|
27
+ next unless route.options[:name] == name
28
+ matcher = route.matcher
29
+ if !args.empty? and matcher.mustermann?
30
+ matcher_names = matcher.names
31
+ params_for_expand = Hash[matcher_names.map{|matcher_name|
32
+ [matcher_name.to_sym, (params[matcher_name.to_sym] || args.shift)]
33
+ }]
34
+ params_for_expand.merge!(Hash[params.select{|k, v| !matcher_names.include?(name.to_sym) }])
35
+ args = saved_args.dup
36
+ else
37
+ params_for_expand = params.dup
38
+ end
39
+ return matcher.mustermann? ? matcher.expand(params_for_expand) : route.path_for_generation
40
+ end
41
+ raise InvalidRouteException
42
+ end
43
+ end
44
+ end
45
+ end