strelka 0.0.1.pre129 → 0.0.1.pre148

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/IDEAS.rdoc +26 -32
  2. data/Manifest.txt +27 -12
  3. data/README.rdoc +29 -33
  4. data/Rakefile +2 -1
  5. data/bin/leash +2 -1
  6. data/contrib/hoetemplate/README.rdoc.erb +18 -0
  7. data/contrib/hoetemplate/data/{file_name → project}/apps/file_name_app +0 -0
  8. data/contrib/hoetemplate/data/{file_name → project}/templates/layout.tmpl.erb +0 -0
  9. data/contrib/hoetemplate/data/{file_name → project}/templates/top.tmpl.erb +0 -0
  10. data/examples/config.yml +3 -0
  11. data/examples/gen-config.rb +48 -0
  12. data/examples/sessions-demo.rb +43 -0
  13. data/lib/strelka/app/errors.rb +59 -17
  14. data/lib/strelka/app/filters.rb +3 -1
  15. data/lib/strelka/app/negotiation.rb +5 -3
  16. data/lib/strelka/app/parameters.rb +33 -31
  17. data/lib/strelka/app/plugins.rb +14 -8
  18. data/lib/strelka/app/restresources.rb +3 -1
  19. data/lib/strelka/app/routing.rb +9 -7
  20. data/lib/strelka/app/sessions.rb +175 -0
  21. data/lib/strelka/app/templating.rb +6 -3
  22. data/lib/strelka/app.rb +19 -11
  23. data/lib/strelka/behavior/plugin.rb +4 -2
  24. data/lib/strelka/constants.rb +9 -0
  25. data/lib/strelka/cookie.rb +357 -0
  26. data/lib/strelka/cookieset.rb +117 -0
  27. data/lib/strelka/httprequest/acceptparams.rb +12 -10
  28. data/lib/strelka/httprequest/negotiation.rb +4 -2
  29. data/lib/strelka/httprequest/session.rb +71 -0
  30. data/lib/strelka/httprequest.rb +19 -7
  31. data/lib/strelka/httpresponse/negotiation.rb +17 -15
  32. data/lib/strelka/httpresponse/session.rb +101 -0
  33. data/lib/strelka/httpresponse.rb +26 -4
  34. data/lib/strelka/logging.rb +3 -1
  35. data/lib/strelka/mixins.rb +174 -2
  36. data/lib/strelka/{app/paramvalidator.rb → paramvalidator.rb} +25 -22
  37. data/lib/strelka/{app/defaultrouter.rb → router/default.rb} +6 -4
  38. data/lib/strelka/{app/exclusiverouter.rb → router/exclusive.rb} +6 -4
  39. data/lib/strelka/{app/router.rb → router.rb} +9 -7
  40. data/lib/strelka/session/default.rb +209 -0
  41. data/lib/strelka/session.rb +178 -0
  42. data/lib/strelka.rb +6 -4
  43. data/spec/lib/constants.rb +3 -1
  44. data/spec/lib/helpers.rb +7 -0
  45. data/spec/strelka/app/errors_spec.rb +32 -52
  46. data/spec/strelka/app/filters_spec.rb +3 -1
  47. data/spec/strelka/app/negotiation_spec.rb +3 -1
  48. data/spec/strelka/app/parameters_spec.rb +5 -3
  49. data/spec/strelka/app/plugins_spec.rb +5 -3
  50. data/spec/strelka/app/restresources_spec.rb +3 -1
  51. data/spec/strelka/app/routing_spec.rb +6 -4
  52. data/spec/strelka/app/sessions_spec.rb +109 -0
  53. data/spec/strelka/app/templating_spec.rb +3 -1
  54. data/spec/strelka/app_spec.rb +11 -2
  55. data/spec/strelka/cookie_spec.rb +194 -0
  56. data/spec/strelka/cookieset_spec.rb +159 -0
  57. data/spec/strelka/exceptions_spec.rb +3 -1
  58. data/spec/strelka/httprequest/acceptparams_spec.rb +3 -1
  59. data/spec/strelka/httprequest/negotiation_spec.rb +3 -1
  60. data/spec/strelka/httprequest/session_spec.rb +43 -0
  61. data/spec/strelka/httprequest_spec.rb +28 -1
  62. data/spec/strelka/httpresponse/negotiation_spec.rb +3 -3
  63. data/spec/strelka/httpresponse_spec.rb +13 -0
  64. data/spec/strelka/logging_spec.rb +3 -1
  65. data/spec/strelka/mixins_spec.rb +125 -1
  66. data/spec/strelka/{app/paramvalidator_spec.rb → paramvalidator_spec.rb} +4 -4
  67. data/spec/strelka/{app/defaultrouter_spec.rb → router/default_spec.rb} +6 -4
  68. data/spec/strelka/{app/exclusiverouter_spec.rb → router/exclusive_spec.rb} +6 -4
  69. data/spec/strelka/{app/router_spec.rb → router_spec.rb} +9 -7
  70. data/spec/strelka/session/default_spec.rb +210 -0
  71. data/spec/strelka/session_spec.rb +101 -0
  72. data.tar.gz.sig +1 -1
  73. metadata +64 -47
  74. metadata.gz.sig +0 -0
  75. data/contrib/hoetemplate/.autotest.erb +0 -23
@@ -1,12 +1,16 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'set'
4
6
  require 'tsort'
5
7
 
6
8
  require 'strelka' unless defined?( Strelka )
7
9
  require 'strelka/app' unless defined?( Strelka::App )
10
+ require 'strelka/mixins'
8
11
 
9
12
  class Strelka::App
13
+ extend Strelka::MethodUtilities
10
14
 
11
15
  # A topologically-sorted hash for plugin management
12
16
  class PluginRegistry < Hash
@@ -22,9 +26,11 @@ class Strelka::App
22
26
  end
23
27
  end
24
28
 
25
- # The Hash of loaded plugin modules, keyed by their downcased and symbolified
29
+
30
+ ##
31
+ # The Hash of loaded plugin modules, keyed by their downcased and symbolified
26
32
  # name (e.g., Strelka::App::Templating => :templating)
27
- class << self; attr_reader :loaded_plugins; end
33
+ singleton_attr_reader :loaded_plugins
28
34
  @loaded_plugins = PluginRegistry.new
29
35
 
30
36
 
@@ -37,7 +43,7 @@ class Strelka::App
37
43
  end
38
44
 
39
45
 
40
- ### Extension hook -- Extend the given object with methods for setting it
46
+ ### Extension hook -- Extend the given object with methods for setting it
41
47
  ### up as a plugin for Strelka::Apps.
42
48
  def self::extended( object )
43
49
  Strelka.log.debug "Extending %p as a Strelka::App::Plugin" % [ object ]
@@ -73,7 +79,7 @@ class Strelka::App
73
79
  end
74
80
 
75
81
 
76
- ### Register the receiver as needing to be run before +other_plugins+ for requests, and
82
+ ### Register the receiver as needing to be run before +other_plugins+ for requests, and
77
83
  ### *after* them for responses.
78
84
  def run_before( *other_plugins )
79
85
  name = self.plugin_name
@@ -91,7 +97,7 @@ class Strelka::App
91
97
  end
92
98
 
93
99
 
94
- ### Register the receiver as needing to be run after +other_plugins+ for requests, and
100
+ ### Register the receiver as needing to be run after +other_plugins+ for requests, and
95
101
  ### *before* them for responses.
96
102
  def run_after( *other_plugins )
97
103
  Strelka.log.debug " %p will run after %p" % [ self, other_plugins ]
@@ -104,7 +110,7 @@ class Strelka::App
104
110
  # Plugin system
105
111
  module Plugins
106
112
 
107
- ### Inclusion callback -- add class methods and instance variables without
113
+ ### Inclusion callback -- add class methods and instance variables without
108
114
  ### needing a separate call to #extend.
109
115
  def self::included( klass )
110
116
  klass.extend( ClassMethods )
@@ -197,7 +203,7 @@ class Strelka::App
197
203
 
198
204
  ### The main extension-point for the plugin system. Strelka::App supers to this method
199
205
  ### with a block that processes the actual request, and the plugins implement this
200
- ### method to add their own functionality.
206
+ ### method to add their own functionality.
201
207
  def handle_request( request, &block )
202
208
  raise LocalJumpError,
203
209
  "no block given; plugin supering without preserving arguments?" unless block
@@ -1,4 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'set'
4
6
  require 'sequel'
@@ -1,16 +1,18 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'strelka' unless defined?( Strelka )
4
6
  require 'strelka/app' unless defined?( Strelka::App )
5
7
 
6
- require 'strelka/app/router'
8
+ require 'strelka/router'
7
9
  require 'strelka/exceptions'
8
10
  require 'strelka/app/plugins'
9
11
 
10
12
  # Sinatra-ish routing logic for Strelka::Apps
11
13
  #
12
14
  # This plugin adds the ability to declare hooks for requests based on their
13
- # attributes. The default router (Strelka::App::DefaultRouter) uses only the
15
+ # attributes. The default router (Strelka::Router::Default) uses only the
14
16
  # HTTP verb and the path, but you can also define your own router class if
15
17
  # you want to include other attributes.
16
18
  #
@@ -74,7 +76,7 @@ require 'strelka/app/plugins'
74
76
  #
75
77
  # == Custom Routers
76
78
  #
77
- # See the Strelka::App::Router for information on how to define your own
79
+ # See the Strelka::Router for information on how to define your own
78
80
  # routing strategies.
79
81
  #
80
82
  module Strelka::App::Routing
@@ -175,7 +177,7 @@ module Strelka::App::Routing
175
177
  patternparts = self.split_route_pattern( pattern )
176
178
  Strelka.log.debug "Split pattern %p into parts: %p" % [ pattern, patternparts ]
177
179
 
178
- # Make a method name from the directories and the named captures of the patterns
180
+ # Make a method name from the directories and the named captures of the patterns
179
181
  # in the route
180
182
  patternparts.each do |part|
181
183
  if part.is_a?( Regexp )
@@ -207,7 +209,7 @@ module Strelka::App::Routing
207
209
  end
208
210
 
209
211
 
210
- ### Split the given +pattern+ into its path components and
212
+ ### Split the given +pattern+ into its path components and
211
213
  def split_route_pattern( pattern )
212
214
  pattern.slice!( 0, 1 ) if pattern.start_with?( '/' )
213
215
 
@@ -237,7 +239,7 @@ module Strelka::App::Routing
237
239
  ### Create a new router object for each class with Routing.
238
240
  def initialize( * )
239
241
  super
240
- @router ||= Strelka::App::Router.create( self.class.routerclass, self.class.routes )
242
+ @router ||= Strelka::Router.create( self.class.routerclass, self.class.routes )
241
243
  end
242
244
 
243
245
 
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'strelka' unless defined?( Strelka )
4
+ require 'strelka/app' unless defined?( Strelka::App )
5
+
6
+ require 'strelka/mixins'
7
+ require 'strelka/exceptions'
8
+ require 'strelka/app/plugins'
9
+
10
+ require 'strelka/httprequest/session'
11
+ require 'strelka/httpresponse/session'
12
+
13
+ # Sessions for Strelka apps
14
+ #
15
+ # This plugin adds a persistant storage mechanism to Strelka applications
16
+ # that can be used to store data about a user's session.
17
+ #
18
+ # == Examples
19
+ #
20
+ # class HelloWorld < Strelka::App
21
+ #
22
+ # # Use the default session store and id-generator
23
+ # plugins :routing, :sessions
24
+ #
25
+ # session_namespace :foo # defaults to the app's ID
26
+ #
27
+ # get '' do
28
+ # finish_with HTTP::FORBIDDEN, "no session!" unless req.session?
29
+ #
30
+ # username = req.session[:username]
31
+ # end
32
+ #
33
+ # end # class HelloWorld
34
+ #
35
+ # == Components
36
+ #
37
+ # This plugin is split up into four parts:
38
+ #
39
+ # [Strelka::Session]
40
+ # The abstract base class for the session object;
41
+ # provides the interface for getting and setting session data,
42
+ # writing the resulting data structure to permanent storage (if
43
+ # necessary), and generating the token that associates the request
44
+ # with the session.
45
+ # [Strelka::HTTPRequest::Session]
46
+ # A mixin module that's added to HTTPRequest when the :sessions plugin
47
+ # is installed. Provides the API on HTTPRequest for fetching and
48
+ # interacting with the Session object.
49
+ # [Strelka::HTTPResponse::Session]
50
+ # A mixin module that's added to HTTPResponse when the :sessions plugin
51
+ # is installed. Provides the API on HTTPResponse for fetching and
52
+ # interacting with the Session object.
53
+ # [Strelka::App::Sessions]
54
+ # This module; stitches the whole system together in your application.
55
+ #
56
+ # == Configuration
57
+ #
58
+ # To specify which Session class to use with your application, add a
59
+ # ':sessions' section with at least the 'type' key to your config.yml:
60
+ #
61
+ # # Use the default session class, but change the name of the cookie
62
+ # # it uses
63
+ # sessions:
64
+ # session_class: default
65
+ # options:
66
+ # cookie_name: acme-session
67
+ #
68
+ # # Load a (hypothetical) Sequel-backed session type and point it
69
+ # # at a database
70
+ # sessions:
71
+ # session_class: sequel
72
+ # options:
73
+ # db: "postgres://pg.example.com/db01"
74
+ # table: sessions
75
+ #
76
+ # The +type+ value will be used to look up the class (see Strelka::Session
77
+ # for more information about how this works), and the +options+ section
78
+ # is passed to the session class's ::configure method (if it has one).
79
+ #
80
+ module Strelka::App::Sessions
81
+ extend Strelka::App::Plugin,
82
+ Strelka::MethodUtilities,
83
+ Configurability
84
+ include Strelka::Loggable,
85
+ Strelka::Constants
86
+
87
+ # Default options to pass to the session object
88
+ DEFAULT_OPTIONS = {
89
+ :cookie_name => 'strelka-session',
90
+ }
91
+
92
+ # Configurability API -- specify which section of the config this class gets
93
+ config_key :sessions
94
+
95
+ # Specify load order; run as late as possible so other plugins can use the session
96
+ run_after :templating, :filters, :parameters
97
+
98
+
99
+ ##
100
+ # What session class to use (Class object)
101
+ singleton_attr_writer :session_class
102
+
103
+
104
+ # Class methods and instance variables to add to classes with routing.
105
+ module ClassMethods # :nodoc:
106
+
107
+ # The namespace of the session that will be exposed to instances of this
108
+ # application
109
+ @session_namespace = nil
110
+
111
+ ### Get/set the key that will determine which namespace in the session object
112
+ ### the application will see.
113
+ def session_namespace( new_namespace=nil )
114
+ @session_namespace = new_namespace if new_namespace
115
+ @session_namespace ||= self.default_appid
116
+ return @session_namespace
117
+ end
118
+
119
+ end # module ClassMethods
120
+
121
+
122
+
123
+ ### Get the configured session class (Strelka::Session subclass)
124
+ def self::session_class
125
+ @session_class ||= Strelka::Session.get_subclass( :default )
126
+ end
127
+
128
+
129
+ ### Configurability API -- set up session type and options with values from
130
+ ### the +config+.
131
+ def self::configure( config )
132
+ options = DEFAULT_OPTIONS.dup
133
+
134
+ # Figure out which session class is going to be used, or choose a default one
135
+ if config
136
+ self.session_class = Strelka::Session.get_subclass( config[:session_class] ) if
137
+ config.key?( :session_class )
138
+ options.merge!( config[:options] ) if config[:options]
139
+ else
140
+ self.session_class = Strelka::Session.get_subclass( :default )
141
+ end
142
+
143
+ # Configure the session class if it can be
144
+ self.session_class.configure( options ) if self.session_class.respond_to?( :configure )
145
+ end
146
+
147
+
148
+ ### Extension callback -- extend the HTTPRequest classes with Session
149
+ ### support when this plugin is loaded.
150
+ def self::included( object )
151
+ Strelka.log.debug "Extending Request with Session mixin"
152
+ Strelka::HTTPRequest.class_eval { include Strelka::HTTPRequest::Session }
153
+ Strelka::HTTPResponse.class_eval { include Strelka::HTTPResponse::Session }
154
+ super
155
+ end
156
+
157
+
158
+ ### Set the session namespace on the HTTPRequest before running the application.
159
+ def fixup_request( request )
160
+ request.session_namespace = self.class.session_namespace
161
+ return super
162
+ end
163
+
164
+
165
+ ### Save the session after the app and plugins are done with the HTTPResponse.
166
+ def fixup_response( response )
167
+ self.log.debug "Saving the session in the response."
168
+ response.save_session
169
+ return super
170
+ end
171
+
172
+
173
+ end # module Strelka::App::Sessions
174
+
175
+
@@ -1,4 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'inversion'
4
6
 
@@ -28,7 +30,7 @@ module Strelka::App::Templating
28
30
  attr_accessor :layout_template
29
31
 
30
32
 
31
- ### Get/set the templates declared for the App.
33
+ ### Get/set the templates declared for the App.
32
34
  def templates( newhash=nil )
33
35
  if newhash
34
36
  self.template_map.merge!( newhash )
@@ -67,7 +69,7 @@ module Strelka::App::Templating
67
69
 
68
70
 
69
71
  ### Return the template keyed by the given +name+.
70
- ### :TODO: Add auto-reloading,
72
+ ### :TODO: Add auto-reloading,
71
73
  def template( name )
72
74
  template = self.template_map[ name ] or
73
75
  raise ArgumentError, "no %p template registered!" % [ name ]
@@ -141,6 +143,7 @@ module Strelka::App::Templating
141
143
 
142
144
  # Wrap the template in a layout if there is one
143
145
  if self.layout
146
+ self.layout.reload if self.layout.changed?
144
147
  l_template = self.layout.dup
145
148
  self.log.debug " wrapping response in layout %p" % [ l_template ]
146
149
  l_template.body = template
data/lib/strelka/app.rb CHANGED
@@ -1,13 +1,17 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'rubygems' # For the Rubygems API
4
6
 
5
7
  require 'mongrel2/handler'
6
8
  require 'strelka' unless defined?( Strelka )
9
+ require 'strelka/mixins'
7
10
 
8
11
 
9
12
  # The application base class.
10
13
  class Strelka::App < Mongrel2::Handler
14
+ extend Strelka::MethodUtilities
11
15
  include Strelka::Loggable,
12
16
  Strelka::Constants
13
17
 
@@ -26,9 +30,10 @@ class Strelka::App < Mongrel2::Handler
26
30
  @subclasses = Hash.new {|h,k| h[k] = [] }
27
31
 
28
32
 
29
- # The Hash of Strelka::App subclasses, keyed by the Pathname of the file they were
33
+ ##
34
+ # The Hash of Strelka::App subclasses, keyed by the Pathname of the file they were
30
35
  # loaded from, or +nil+ if they weren't loaded via ::load.
31
- class << self; attr_reader :subclasses; end
36
+ singleton_attr_reader :subclasses
32
37
 
33
38
 
34
39
  ### Inheritance callback -- add subclasses to @subclasses so .load can figure out which
@@ -46,13 +51,14 @@ class Strelka::App < Mongrel2::Handler
46
51
  appid ||= self.default_appid
47
52
 
48
53
  Strelka.logger.level = Logger::DEBUG if $VERBOSE
54
+ Strelka.logger.formatter = Strelka::Logging::ColorFormatter.new( Strelka.logger ) if $stderr.tty?
49
55
 
50
56
  super( appid )
51
57
 
52
58
  end
53
59
 
54
60
 
55
- ### Calculate a default application ID for the class based on either its ID
61
+ ### Calculate a default application ID for the class based on either its ID
56
62
  ### constant or its name and return it.
57
63
  def self::default_appid
58
64
  Strelka.log.info "Looking up appid for %p" % [ self.class ]
@@ -74,7 +80,9 @@ class Strelka::App < Mongrel2::Handler
74
80
  ### Return a Hash of Strelka app files as Pathname objects from installed gems,
75
81
  ### keyed by gemspec name .
76
82
  def self::discover_paths
77
- appfiles = {}
83
+ appfiles = {
84
+ 'strelka' => Pathname.glob( DATADIR + APP_GLOB_PATTERN )
85
+ }
78
86
 
79
87
  # Find all the gems that depend on Strelka
80
88
  gems = Gem::Specification.find_all do |gemspec|
@@ -110,7 +118,7 @@ class Strelka::App < Mongrel2::Handler
110
118
  Strelka.log.debug "Loading apps from %d discovered paths" % [ app_paths.length ]
111
119
  app_paths.each do |gemname, paths|
112
120
  Strelka.log.debug " loading gem %s" % [ gemname ]
113
- gem( gemname )
121
+ gem( gemname ) unless gemname == 'strelka'
114
122
 
115
123
  Strelka.log.debug " loading apps from %s: %d handlers" % [ gemname, paths.length ]
116
124
  paths.each do |path|
@@ -214,8 +222,8 @@ class Strelka::App < Mongrel2::Handler
214
222
  protected
215
223
  #########
216
224
 
217
- ### Make any changes to the +request+ that are necessary before handling it and
218
- ### return it. This is an alternate extension-point for plugins that
225
+ ### Make any changes to the +request+ that are necessary before handling it and
226
+ ### return it. This is an alternate extension-point for plugins that
219
227
  ### wish to modify or replace the request before the request cycle is
220
228
  ### started.
221
229
  def fixup_request( request )
@@ -240,8 +248,8 @@ class Strelka::App < Mongrel2::Handler
240
248
  end
241
249
 
242
250
 
243
- ### Make any changes to the +response+ that are necessary before handing it to
244
- ### Mongrel and return it. This is an alternate extension-point for plugins that
251
+ ### Make any changes to the +response+ that are necessary before handing it to
252
+ ### Mongrel and return it. This is an alternate extension-point for plugins that
245
253
  ### wish to modify or replace the response after the whole request cycle is
246
254
  ### completed.
247
255
  def fixup_response( response )
@@ -301,7 +309,7 @@ class Strelka::App < Mongrel2::Handler
301
309
  end
302
310
 
303
311
 
304
- ### Create a response to specified +request+ based on the specified +status_code+
312
+ ### Create a response to specified +request+ based on the specified +status_code+
305
313
  ### and +message+.
306
314
  def prepare_status_response( request, status_info )
307
315
  status_code, message = status_info.values_at( :status, :message )
@@ -1,4 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
2
4
 
3
5
  require 'rspec'
4
6
 
@@ -11,7 +13,7 @@ require 'strelka/app/plugins'
11
13
  # plugins share in common. If you're creating a Strelka::App plugin,
12
14
  # you can test its conformity to the expectations placed on them by
13
15
  # adding this to your spec:
14
- #
16
+ #
15
17
  # require 'strelka/behavior/plugin'
16
18
  #
17
19
  # describe YourPlugin do
@@ -11,6 +11,15 @@ module Strelka::Constants
11
11
  # Import Mongrel2's constants, too
12
12
  include Mongrel2::Constants
13
13
 
14
+ # The data directory in the project if that exists, otherwise the gem datadir
15
+ DATADIR = if File.directory?( 'data/strelka' )
16
+ Pathname( 'data/strelka' )
17
+ elsif path = Gem.datadir('strelka')
18
+ Pathname( path )
19
+ else
20
+ raise ScriptError, "can't find the data directory!"
21
+ end
22
+
14
23
  # Extend Mongrel2's HTTP constants collection
15
24
  module HTTP
16
25
  include Mongrel2::Constants::HTTP