rack_warden 0.0.9 → 0.0.10

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.
Files changed (39) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -0
  3. data/Gemfile +13 -10
  4. data/README.md +115 -53
  5. data/config.ru +1 -0
  6. data/lib/rack_warden.rb +33 -5
  7. data/lib/rack_warden/app.rb +73 -58
  8. data/lib/rack_warden/core_patches.rb +20 -0
  9. data/lib/rack_warden/env.rb +27 -0
  10. data/lib/rack_warden/frameworks.rb +34 -36
  11. data/lib/rack_warden/frameworks/rack.rb +36 -0
  12. data/lib/rack_warden/frameworks/rails.rb +29 -9
  13. data/lib/rack_warden/frameworks/sinatra.rb +15 -11
  14. data/lib/rack_warden/helpers.rb +197 -29
  15. data/lib/rack_warden/mail.rb +26 -0
  16. data/lib/rack_warden/models.rb +79 -40
  17. data/lib/rack_warden/models/user.rb +180 -22
  18. data/lib/rack_warden/routes.rb +159 -83
  19. data/lib/rack_warden/sinatra/decompile.rb +127 -0
  20. data/lib/rack_warden/sinatra/json.rb +131 -0
  21. data/lib/rack_warden/sinatra/namespace.rb +285 -0
  22. data/lib/rack_warden/sinatra/respond_with.rb +277 -0
  23. data/lib/rack_warden/version.rb +1 -1
  24. data/lib/rack_warden/views/rw_account_widget.html.erb +8 -0
  25. data/lib/rack_warden/views/rw_activation.email.erb +3 -0
  26. data/lib/rack_warden/views/rw_admin.html.erb +7 -5
  27. data/lib/rack_warden/views/rw_dbinfo.html.erb +5 -4
  28. data/lib/rack_warden/views/rw_error.html.erb +1 -0
  29. data/lib/rack_warden/views/rw_flash_widget.html.erb +12 -0
  30. data/lib/rack_warden/views/rw_index.html.erb +1 -1
  31. data/lib/rack_warden/views/rw_layout.html.erb +13 -19
  32. data/lib/rack_warden/views/rw_layout_admin.html.erb +6 -6
  33. data/lib/rack_warden/views/rw_login.html.erb +18 -5
  34. data/lib/rack_warden/views/rw_new_user.html.erb +22 -6
  35. data/lib/rack_warden/views/rw_protected.xml.erb +10 -0
  36. data/lib/rack_warden/views/rw_session.html.erb +34 -0
  37. data/lib/rack_warden/warden.rb +161 -30
  38. data/rack_warden.gemspec +16 -13
  39. metadata +84 -29
@@ -0,0 +1,127 @@
1
+ require 'sinatra/base'
2
+ require 'backports'
3
+ require 'uri'
4
+
5
+ module Sinatra
6
+
7
+ # = Sinatra::Decompile
8
+ #
9
+ # <tt>Sinatra::Decompile</tt> is an extension that provides a method,
10
+ # conveniently called +decompile+, that will generate a String pattern for a
11
+ # given route.
12
+ #
13
+ # == Usage
14
+ #
15
+ # === Classic Application
16
+ #
17
+ # To use the extension in a classic application all you need to do is require
18
+ # it:
19
+ #
20
+ # require "sinatra"
21
+ # require "sinatra/decompile"
22
+ #
23
+ # # Your classic application code goes here...
24
+ #
25
+ # This will add the +decompile+ method to the application/class scope, but
26
+ # you can also call it as <tt>Sinatra::Decompile.decompile</tt>.
27
+ #
28
+ # === Modular Application
29
+ #
30
+ # To use the extension in a modular application you need to require it, and
31
+ # then, tell the application you will use it:
32
+ #
33
+ # require "sinatra/base"
34
+ # require "sinatra/decompile"
35
+ #
36
+ # class MyApp < Sinatra::Base
37
+ # register Sinatra::Decompile
38
+ #
39
+ # # The rest of your modular application code goes here...
40
+ # end
41
+ #
42
+ # This will add the +decompile+ method to the application/class scope. You
43
+ # can choose not to register the extension, but instead of calling
44
+ # +decompile+, you will need to call <tt>Sinatra::Decompile.decompile</tt>.
45
+ #
46
+ module Decompile
47
+ extend self
48
+
49
+ ##
50
+ # Regenerates a string pattern for a given route
51
+ #
52
+ # Example:
53
+ #
54
+ # class Sinatra::Application
55
+ # routes.each do |verb, list|
56
+ # puts "#{verb}:"
57
+ # list.each do |data|
58
+ # puts "\t" << decompile(data)
59
+ # end
60
+ # end
61
+ # end
62
+ #
63
+ # Will return the internal Regexp if it's unable to reconstruct the pattern,
64
+ # which likely indicates that a Regexp was used in the first place.
65
+ #
66
+ # You can also use this to check whether you could actually use a string
67
+ # pattern instead of your regexp:
68
+ #
69
+ # decompile /^/foo$/ # => '/foo'
70
+ def decompile(pattern, keys = nil, *)
71
+ # Everything in here is basically just the reverse of
72
+ # Sinatra::Base#compile
73
+ #
74
+ # Sinatra 2.0 will come with a mechanism for this, making this obsolete.
75
+ pattern, keys = pattern if pattern.respond_to? :to_ary
76
+ keys, str = keys.try(:dup), pattern.inspect
77
+ return pattern unless str.start_with? '/' and str.end_with? '/'
78
+ str.gsub! /^\/(\^|\\A)?|(\$|\\z)?\/$/, ''
79
+ str.gsub! encoded(' '), ' '
80
+ return pattern if str =~ /^[\.\+]/
81
+ str.gsub! '((?:[^\.\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)'
82
+ str.gsub! '((?:[^\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)'
83
+ str.gsub! /\([^\(\)]*\)|\([^\(\)]*\([^\(\)]*\)[^\(\)]*\)/ do |part|
84
+ case part
85
+ when '(.*?)'
86
+ return pattern if keys.shift != 'splat'
87
+ '*'
88
+ when /^\(\?\:(\\*.)\|%[\w\[\]]+\)$/
89
+ $1
90
+ when /^\(\?\:(%\d+)\|([^\)]+|\([^\)]+\))\)$/
91
+ URI.unescape($1)
92
+ when '([^\/?#]+)'
93
+ return pattern if keys.empty?
94
+ ":" << keys.shift
95
+ when /^\(\?\:\\?(.)\|/
96
+ char = $1
97
+ return pattern unless encoded(char) == part
98
+ Regexp.escape(char)
99
+ else
100
+ return pattern
101
+ end
102
+ end
103
+ str.gsub /(.)([\.\+\(\)\/])/ do
104
+ return pattern if $1 != "\\"
105
+ $2
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def encoded(char)
112
+ return super if defined? super
113
+ enc = uri_parser.escape(char)
114
+ enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
115
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
116
+ enc
117
+ end
118
+
119
+ def uri_parser
120
+ #TODO: Remove check after dropping support for 1.8.7
121
+ @_uri_parser ||= defined?(URI::Parser) ? URI::Parser.new : URI
122
+ end
123
+
124
+ end
125
+
126
+ #register Decompile
127
+ end
@@ -0,0 +1,131 @@
1
+ require 'sinatra/base'
2
+ require 'multi_json'
3
+ module Sinatra
4
+
5
+ # = Sinatra::JSON
6
+ #
7
+ # <tt>Sinatra::JSON</tt> adds a helper method, called +json+, for (obviously)
8
+ # json generation.
9
+ #
10
+ # == Usage
11
+ #
12
+ # === Classic Application
13
+ #
14
+ # In a classic application simply require the helper, and start using it:
15
+ #
16
+ # require "sinatra"
17
+ # require "sinatra/json"
18
+ #
19
+ # # define a route that uses the helper
20
+ # get '/' do
21
+ # json :foo => 'bar'
22
+ # end
23
+ #
24
+ # # The rest of your classic application code goes here...
25
+ #
26
+ # === Modular Application
27
+ #
28
+ # In a modular application you need to require the helper, and then tell the
29
+ # application you will use it:
30
+ #
31
+ # require "sinatra/base"
32
+ # require "sinatra/json"
33
+ #
34
+ # class MyApp < Sinatra::Base
35
+ #
36
+ # # define a route that uses the helper
37
+ # get '/' do
38
+ # json :foo => 'bar'
39
+ # end
40
+ #
41
+ # # The rest of your modular application code goes here...
42
+ # end
43
+ #
44
+ # === Encoders
45
+ #
46
+ # By default it will try to call +to_json+ on the object, but if it doesn't
47
+ # respond to that message, it will use its own rather simple encoder. You can
48
+ # easily change that anyways. To use +JSON+, simply require it:
49
+ #
50
+ # require 'json'
51
+ #
52
+ # The same goes for <tt>Yajl::Encoder</tt>:
53
+ #
54
+ # require 'yajl'
55
+ #
56
+ # For other encoders, besides requiring them, you need to define the
57
+ # <tt>:json_encoder</tt> setting. For instance, for the +Whatever+ encoder:
58
+ #
59
+ # require 'whatever'
60
+ # set :json_encoder, Whatever
61
+ #
62
+ # To force +json+ to simply call +to_json+ on the object:
63
+ #
64
+ # set :json_encoder, :to_json
65
+ #
66
+ # Actually, it can call any method:
67
+ #
68
+ # set :json_encoder, :my_fancy_json_method
69
+ #
70
+ # === Content-Type
71
+ #
72
+ # It will automatically set the content type to "application/json". As
73
+ # usual, you can easily change that, with the <tt>:json_content_type</tt>
74
+ # setting:
75
+ #
76
+ # set :json_content_type, :js
77
+ #
78
+ # === Overriding the Encoder and the Content-Type
79
+ #
80
+ # The +json+ helper will also take two options <tt>:encoder</tt> and
81
+ # <tt>:content_type</tt>. The values of this options are the same as the
82
+ # <tt>:json_encoder</tt> and <tt>:json_content_type</tt> settings,
83
+ # respectively. You can also pass those to the json method:
84
+ #
85
+ # get '/' do
86
+ # json({:foo => 'bar'}, :encoder => :to_json, :content_type => :js)
87
+ # end
88
+ #
89
+ module JSON
90
+ class << self
91
+ def encode(object)
92
+ ::MultiJson.dump(object)
93
+ end
94
+ end
95
+
96
+ def json(object, options = {})
97
+ content_type resolve_content_type(options)
98
+ resolve_encoder_action object, resolve_encoder(options)
99
+ end
100
+
101
+ private
102
+
103
+ def resolve_content_type(options = {})
104
+ options[:content_type] || settings.json_content_type
105
+ end
106
+
107
+ def resolve_encoder(options = {})
108
+ options[:json_encoder] || settings.json_encoder
109
+ end
110
+
111
+ def resolve_encoder_action(object, encoder)
112
+ [:encode, :generate].each do |method|
113
+ return encoder.send(method, object) if encoder.respond_to? method
114
+ end
115
+ if encoder.is_a? Symbol
116
+ object.__send__(encoder)
117
+ else
118
+ fail "#{encoder} does not respond to #generate nor #encode"
119
+ end #if
120
+ end #resolve_encoder_action
121
+ end #JSON
122
+
123
+ Base.set :json_encoder do
124
+ ::MultiJson
125
+ end
126
+
127
+ Base.set :json_content_type, :json
128
+
129
+ # Load the JSON helpers in modular style automatically
130
+ Base.helpers JSON
131
+ end
@@ -0,0 +1,285 @@
1
+ require 'backports'
2
+ require 'sinatra/base'
3
+ require 'rack_warden/sinatra/decompile'
4
+
5
+ module RackWarden
6
+ App.logger.debug "RW loading Namespace"
7
+
8
+ # = Sinatra::Namespace
9
+ #
10
+ # <tt>Sinatra::Namespace</tt> is an extension that adds namespaces to an
11
+ # application. This namespaces will allow you to share a path prefix for the
12
+ # routes within the namespace, and define filters, conditions and error
13
+ # handlers exclusively for them. Besides that, you can also register helpers
14
+ # and extensions that will be used only within the namespace.
15
+ #
16
+ # == Usage
17
+ #
18
+ # Once you have loaded the extension (see below), you can use the +namespace+
19
+ # method to define namespaces in your application.
20
+ #
21
+ # You can define a namespace by a path prefix:
22
+ #
23
+ # namespace '/blog' do
24
+ # get { haml :blog }
25
+ # get '/:entry_permalink' do
26
+ # @entry = Entry.find_by_permalink!(params[:entry_permalink])
27
+ # haml :entry
28
+ # end
29
+ #
30
+ # # More blog routes...
31
+ # end
32
+ #
33
+ # by a condition:
34
+ #
35
+ # namespace :host_name => 'localhost' do
36
+ # get('/admin/dashboard') { haml :dashboard }
37
+ # get('/admin/login') { haml :login }
38
+ #
39
+ # # More admin routes...
40
+ # end
41
+ #
42
+ # or both:
43
+ #
44
+ # namespace '/admin', :host_name => 'localhost' do
45
+ # get('/dashboard') { haml :dashboard }
46
+ # get('/login') { haml :login }
47
+ # post('/login') { login_user }
48
+ #
49
+ # # More admin routes...
50
+ # end
51
+ #
52
+ # When you define a filter or an error handler, or register an extension or a
53
+ # set of helpers within a namespace, they only affect the routes defined in
54
+ # it. For instance, lets define a before filter to prevent the access of
55
+ # unauthorized users to the admin section of the application:
56
+ #
57
+ # namespace '/admin' do
58
+ # helpers AdminHelpers
59
+ # before { authenticate unless request.path_info == '/admin/login' }
60
+ #
61
+ # get '/dashboard' do
62
+ # # Only authenticated users can access here...
63
+ # haml :dashboard
64
+ # end
65
+ #
66
+ # # More admin routes...
67
+ # end
68
+ #
69
+ # get '/' do
70
+ # # Any user can access here...
71
+ # haml :index
72
+ # end
73
+ #
74
+ # Well, they actually also affect the nested namespaces:
75
+ #
76
+ # namespace '/admin' do
77
+ # helpers AdminHelpers
78
+ # before { authenticate unless request.path_info == '/admin/login' }
79
+ #
80
+ # namespace '/users' do
81
+ # get do
82
+ # # Only authenticated users can access here...
83
+ # @users = User.all
84
+ # haml :users
85
+ # end
86
+ #
87
+ # # More user admin routes...
88
+ # end
89
+ #
90
+ # # More admin routes...
91
+ # end
92
+ #
93
+ # === Classic Application Setup
94
+ #
95
+ # To be able to use namespaces in a classic application all you need to do is
96
+ # require the extension:
97
+ #
98
+ # require "sinatra"
99
+ # require "sinatra/namespace"
100
+ #
101
+ # # The rest of your classic application code goes here...
102
+ #
103
+ # === Modular Application Setup
104
+ #
105
+ # To be able to use namespaces in a modular application all you need to do is
106
+ # require the extension, and then, register it:
107
+ #
108
+ # require "sinatra/base"
109
+ # require "sinatra/namespace"
110
+ #
111
+ # class MyApp < Sinatra::Base
112
+ # register Sinatra::Namespace
113
+ #
114
+ # # The rest of your modular application code goes here...
115
+ # end
116
+ #
117
+ module Namespace
118
+ def self.new(base, pattern, conditions = {}, &block)
119
+ Module.new do
120
+ extend NamespacedMethods
121
+ include InstanceMethods
122
+ @base, @extensions, @errors = base, [], {}
123
+ @pattern, @conditions = compile(pattern, conditions)
124
+ @templates = Hash.new { |h,k| @base.templates[k] }
125
+ namespace = self
126
+ before { extend(@namespace = namespace) }
127
+ class_eval(&block)
128
+ end
129
+ end
130
+
131
+ module InstanceMethods
132
+ def settings
133
+ @namespace
134
+ end
135
+
136
+ def template_cache
137
+ super.fetch(:nested, @namespace) { Tilt::Cache.new }
138
+ end
139
+ end
140
+
141
+ module SharedMethods
142
+ def namespace(pattern, conditions = {}, &block)
143
+ Namespace.new(self, pattern, conditions, &block)
144
+ end
145
+ end
146
+
147
+ module NamespacedMethods
148
+ include SharedMethods
149
+ include Sinatra::Decompile
150
+ attr_reader :base, :templates
151
+
152
+ def self.prefixed(*names)
153
+ names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
154
+ end
155
+
156
+ prefixed :before, :after, :delete, :get, :head, :options, :patch, :post, :put
157
+
158
+ def helpers(*extensions, &block)
159
+ class_eval(&block) if block_given?
160
+ include(*extensions) if extensions.any?
161
+ end
162
+
163
+ def register(*extensions, &block)
164
+ extensions << Module.new(&block) if block_given?
165
+ @extensions += extensions
166
+ extensions.each do |extension|
167
+ extend extension
168
+ extension.registered(self) if extension.respond_to?(:registered)
169
+ end
170
+ end
171
+
172
+ def invoke_hook(name, *args)
173
+ @extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
174
+ end
175
+
176
+ def not_found(&block)
177
+ error(404, &block)
178
+ end
179
+
180
+ def errors
181
+ base.errors.merge(namespace_errors)
182
+ end
183
+
184
+ def namespace_errors
185
+ @errors
186
+ end
187
+
188
+ def error(*codes, &block)
189
+ args = Sinatra::Base.send(:compile!, "ERROR", /^#{@pattern}/, block)
190
+ codes = codes.map { |c| Array(c) }.flatten
191
+ codes << Exception if codes.empty?
192
+ codes.each do |c|
193
+ errors = @errors[c] ||= []
194
+ errors << args
195
+ end
196
+ end
197
+
198
+ def respond_to(*args)
199
+ return @conditions[:provides] || base.respond_to if args.empty?
200
+ @conditions[:provides] = args
201
+ end
202
+
203
+ def set(key, value = self, &block)
204
+ raise ArgumentError, "may not set #{key}" if key != :views
205
+ return key.each { |k,v| set(k, v) } if block.nil? and value == self
206
+ block ||= proc { value }
207
+ singleton_class.send(:define_method, key, &block)
208
+ end
209
+
210
+ def enable(*opts)
211
+ opts.each { |key| set(key, true) }
212
+ end
213
+
214
+ def disable(*opts)
215
+ opts.each { |key| set(key, false) }
216
+ end
217
+
218
+ def template(name, &block)
219
+ filename, line = caller_locations.first
220
+ templates[name] = [block, filename, line.to_i]
221
+ end
222
+
223
+ def layout(name=:layout, &block)
224
+ template name, &block
225
+ end
226
+
227
+ private
228
+
229
+ def app
230
+ base.respond_to?(:base) ? base.base : base
231
+ end
232
+
233
+ def compile(pattern, conditions, default_pattern = nil)
234
+ if pattern.respond_to? :to_hash
235
+ conditions = conditions.merge pattern.to_hash
236
+ pattern = nil
237
+ end
238
+ base_pattern, base_conditions = @pattern, @conditions
239
+ pattern ||= default_pattern
240
+ base_pattern ||= base.pattern if base.respond_to? :pattern
241
+ base_conditions ||= base.conditions if base.respond_to? :conditions
242
+ [ prefixed_path(base_pattern, pattern),
243
+ (base_conditions || {}).merge(conditions) ]
244
+ end
245
+
246
+ def prefixed_path(a, b)
247
+ return a || b || // unless a and b
248
+ a, b = decompile(a), decompile(b) unless a.class == b.class
249
+ a, b = regexpify(a), regexpify(b) unless a.class == b.class
250
+ path = a.class.new "#{a}#{b}"
251
+ path = /^#{path}$/ if path.is_a? Regexp and base == app
252
+ path
253
+ end
254
+
255
+ def regexpify(pattern)
256
+ pattern = Sinatra::Base.send(:compile, pattern).first.inspect
257
+ pattern.gsub! /^\/(\^|\\A)?|(\$|\\Z)?\/$/, ''
258
+ Regexp.new pattern
259
+ end
260
+
261
+ def prefixed(method, pattern = nil, conditions = {}, &block)
262
+ default = '*' if method == :before or method == :after
263
+ pattern, conditions = compile pattern, conditions, default
264
+ result = base.send(method, pattern, conditions, &block)
265
+ invoke_hook :route_added, method.to_s.upcase, pattern, block
266
+ result
267
+ end
268
+
269
+ def method_missing(method, *args, &block)
270
+ base.send(method, *args, &block)
271
+ end
272
+ end
273
+
274
+ module BaseMethods
275
+ include SharedMethods
276
+ end
277
+
278
+ def self.extend_object(base)
279
+ base.extend BaseMethods
280
+ end
281
+ end
282
+
283
+ #register Sinatra::Namespace
284
+ #Delegator.delegate :namespace
285
+ end