rack_warden 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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