strelka 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ChangeLog +156 -9
- data/History.rdoc +15 -0
- data/IDEAS.rdoc +17 -1
- data/MILESTONES.rdoc +1 -1
- data/Manifest.txt +10 -2
- data/Plugins.rdoc +4 -4
- data/README.rdoc +3 -3
- data/Rakefile +5 -4
- data/bin/strelka +19 -10
- data/contrib/hoetemplate/data/project/apps/file_name_app +1 -0
- data/contrib/hoetemplate/lib/file_name.rb.erb +3 -2
- data/examples/apps/hello-world +1 -0
- data/examples/apps/ws-chat +69 -0
- data/examples/apps/ws-echo +61 -0
- data/examples/gen-config.rb +6 -5
- data/lib/strelka/app/auth.rb +2 -2
- data/lib/strelka/app/errors.rb +1 -1
- data/lib/strelka/app/filters.rb +3 -2
- data/lib/strelka/app/negotiation.rb +2 -2
- data/lib/strelka/app/parameters.rb +1 -2
- data/lib/strelka/app/restresources.rb +3 -2
- data/lib/strelka/app/routing.rb +1 -1
- data/lib/strelka/app/sessions.rb +2 -2
- data/lib/strelka/app/templating.rb +7 -3
- data/lib/strelka/app.rb +5 -145
- data/lib/strelka/behavior/plugin.rb +4 -4
- data/lib/strelka/discovery.rb +211 -0
- data/lib/strelka/httprequest.rb +1 -0
- data/lib/strelka/httpresponse/negotiation.rb +7 -1
- data/lib/strelka/mixins.rb +4 -1
- data/lib/strelka/paramvalidator.rb +1 -1
- data/lib/strelka/plugins.rb +8 -6
- data/lib/strelka/websocketserver/routing.rb +116 -0
- data/lib/strelka/websocketserver.rb +147 -0
- data/lib/strelka.rb +5 -4
- data/spec/{lib/constants.rb → constants.rb} +3 -2
- data/spec/{lib/helpers.rb → helpers.rb} +15 -14
- data/spec/strelka/app/auth_spec.rb +145 -142
- data/spec/strelka/app/errors_spec.rb +20 -26
- data/spec/strelka/app/filters_spec.rb +67 -54
- data/spec/strelka/app/negotiation_spec.rb +8 -14
- data/spec/strelka/app/parameters_spec.rb +23 -29
- data/spec/strelka/app/restresources_spec.rb +98 -100
- data/spec/strelka/app/routing_spec.rb +57 -57
- data/spec/strelka/app/sessions_spec.rb +11 -17
- data/spec/strelka/app/templating_spec.rb +36 -40
- data/spec/strelka/app_spec.rb +48 -147
- data/spec/strelka/authprovider/basic_spec.rb +5 -11
- data/spec/strelka/authprovider/hostaccess_spec.rb +9 -15
- data/spec/strelka/authprovider_spec.rb +3 -9
- data/spec/strelka/cookie_spec.rb +32 -38
- data/spec/strelka/cookieset_spec.rb +31 -37
- data/spec/strelka/discovery_spec.rb +144 -0
- data/spec/strelka/exceptions_spec.rb +2 -8
- data/spec/strelka/httprequest/acceptparams_spec.rb +74 -83
- data/spec/strelka/httprequest/auth_spec.rb +5 -15
- data/spec/strelka/httprequest/negotiation_spec.rb +93 -103
- data/spec/strelka/httprequest/session_spec.rb +12 -22
- data/spec/strelka/httprequest_spec.rb +1 -7
- data/spec/strelka/httpresponse/negotiation_spec.rb +84 -76
- data/spec/strelka/httpresponse/session_spec.rb +25 -35
- data/spec/strelka/httpresponse_spec.rb +20 -26
- data/spec/strelka/mixins_spec.rb +66 -61
- data/spec/strelka/multipartparser_spec.rb +31 -37
- data/spec/strelka/paramvalidator_spec.rb +389 -373
- data/spec/strelka/plugins_spec.rb +17 -23
- data/spec/strelka/router/default_spec.rb +32 -38
- data/spec/strelka/router/exclusive_spec.rb +28 -34
- data/spec/strelka/router_spec.rb +2 -8
- data/spec/strelka/session/db_spec.rb +17 -15
- data/spec/strelka/session/default_spec.rb +22 -28
- data/spec/strelka/session_spec.rb +3 -9
- data/spec/strelka/websocketserver/routing_spec.rb +119 -0
- data/spec/strelka/websocketserver_spec.rb +149 -0
- data/spec/strelka_spec.rb +11 -13
- data.tar.gz.sig +3 -3
- metadata +22 -14
- metadata.gz.sig +0 -0
data/lib/strelka/app/errors.rb
CHANGED
data/lib/strelka/app/filters.rb
CHANGED
@@ -10,7 +10,8 @@ require 'strelka/app' unless defined?( Strelka::App )
|
|
10
10
|
module Strelka::App::Filters
|
11
11
|
extend Strelka::Plugin
|
12
12
|
|
13
|
-
|
13
|
+
run_inside :templating
|
14
|
+
run_outside :routing
|
14
15
|
|
15
16
|
|
16
17
|
# Class methods to add to classes with routing.
|
@@ -68,7 +69,7 @@ module Strelka::App::Filters
|
|
68
69
|
|
69
70
|
self.apply_request_filters( request )
|
70
71
|
response = super
|
71
|
-
self.apply_response_filters( response )
|
72
|
+
self.apply_response_filters( request.response )
|
72
73
|
|
73
74
|
return response
|
74
75
|
end
|
@@ -29,8 +29,8 @@ module Strelka::App::Negotiation
|
|
29
29
|
include Strelka::Constants
|
30
30
|
extend Strelka::Plugin
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
run_outside :routing, :filters
|
33
|
+
run_inside :templating, :parameters
|
34
34
|
|
35
35
|
|
36
36
|
# Class methods to add to classes with content-negotiation.
|
@@ -369,7 +369,7 @@ module Strelka::App::RestResources
|
|
369
369
|
end
|
370
370
|
|
371
371
|
|
372
|
-
### Add a handler method for
|
372
|
+
### Add a handler method for replacing all instances of +rsrcobj+ collection.
|
373
373
|
### PUT /resources
|
374
374
|
def add_collection_update_handler( route, rsrcobj, options )
|
375
375
|
pkey = rsrcobj.primary_key
|
@@ -388,7 +388,8 @@ module Strelka::App::RestResources
|
|
388
388
|
# Save it in a transaction, erroring if any of 'em fail validations
|
389
389
|
begin
|
390
390
|
rsrcobj.db.transaction do
|
391
|
-
rsrcobj.
|
391
|
+
rsrcobj.truncate
|
392
|
+
rsrcobj.create( newvals )
|
392
393
|
end
|
393
394
|
rescue Sequel::ValidationFailed => err
|
394
395
|
finish_with( HTTP::BAD_REQUEST, err.message )
|
data/lib/strelka/app/routing.rb
CHANGED
data/lib/strelka/app/sessions.rb
CHANGED
@@ -94,9 +94,9 @@ module Strelka::App::Sessions
|
|
94
94
|
# Configurability API -- specify which section of the config this class gets
|
95
95
|
config_key :sessions
|
96
96
|
|
97
|
-
# Plugins API -- Specify load order; run as
|
97
|
+
# Plugins API -- Specify load order; run as early as possible so other plugins
|
98
98
|
# can use the session
|
99
|
-
|
99
|
+
run_outside :templating, :filters, :parameters
|
100
100
|
|
101
101
|
|
102
102
|
# Class methods and instance variables to add to classes with sessions.
|
@@ -74,6 +74,10 @@ require 'strelka/plugins'
|
|
74
74
|
#
|
75
75
|
# It will be loaded, set as the response body, and the above common objects added to it.
|
76
76
|
#
|
77
|
+
# :TODO: Explain how returning things other than responses doesn't work well with
|
78
|
+
# :filters and maybe other plugins that run inside :templating.
|
79
|
+
#
|
80
|
+
#
|
77
81
|
# === Layouts
|
78
82
|
#
|
79
83
|
# Very often, you'll want all or most of the views in your app to share a common page
|
@@ -116,14 +120,13 @@ module Strelka::App::Templating
|
|
116
120
|
log_to :strelka
|
117
121
|
|
118
122
|
# Run order
|
119
|
-
|
120
|
-
run_after :filters
|
123
|
+
run_outside :routing, :negotiation, :errors, :filters
|
121
124
|
|
122
125
|
|
123
126
|
### Return an Array of Pathnames to all directories named 'templates' under the
|
124
127
|
### data dirctories of loaded gems which have a dependency on Strelka.
|
125
128
|
def self::discover_template_dirs
|
126
|
-
directories = Strelka::
|
129
|
+
directories = Strelka::Discovery.discover_data_dirs.values.flatten
|
127
130
|
|
128
131
|
self.log.debug "Discovered data directories: %p" % [ directories ]
|
129
132
|
|
@@ -282,6 +285,7 @@ module Strelka::App::Templating
|
|
282
285
|
# Not templated; returned as-is
|
283
286
|
else
|
284
287
|
self.log.debug " response isn't templated; returning nil"
|
288
|
+
# :TODO: Return the response instead of nil
|
285
289
|
return nil
|
286
290
|
end
|
287
291
|
end
|
data/lib/strelka/app.rb
CHANGED
@@ -10,12 +10,14 @@ require 'mongrel2/handler'
|
|
10
10
|
require 'strelka' unless defined?( Strelka )
|
11
11
|
require 'strelka/mixins'
|
12
12
|
require 'strelka/plugins'
|
13
|
+
require 'strelka/discovery'
|
13
14
|
|
14
15
|
|
15
16
|
# The Strelka HTTP application base class.
|
16
17
|
class Strelka::App < Mongrel2::Handler
|
17
18
|
extend Loggability,
|
18
19
|
Configurability,
|
20
|
+
Strelka::Discovery,
|
19
21
|
Strelka::MethodUtilities,
|
20
22
|
Strelka::PluginLoader
|
21
23
|
include Strelka::Constants,
|
@@ -85,29 +87,14 @@ class Strelka::App < Mongrel2::Handler
|
|
85
87
|
### to the 'app' section of the config that will be passed to you when the config
|
86
88
|
### is loaded.
|
87
89
|
def self::configure( config=nil )
|
88
|
-
config =
|
89
|
-
|
90
|
+
config = Strelka::App.defaults.merge( config || {} )
|
90
91
|
self.devmode = config[:devmode] || $DEBUG
|
91
|
-
self.app_glob_pattern = config[:app_glob_pattern]
|
92
|
-
self.local_data_dirs = config[:local_data_dirs]
|
93
|
-
|
94
92
|
self.log.info "Enabled developer mode." if self.devmode?
|
95
93
|
end
|
96
94
|
|
97
95
|
|
98
|
-
### Inheritance callback -- add subclasses to @subclasses so .load can figure out which
|
99
|
-
### classes correspond to which files.
|
100
|
-
def self::inherited( subclass )
|
101
|
-
super
|
102
|
-
self.log.debug "Adding %p to the subclasses hash." % [ subclass ]
|
103
|
-
Strelka::App.subclasses[ @loading_file ] << subclass #if self == Strelka::App
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
96
|
### Overridden from Mongrel2::Handler -- use the value returned from .default_appid if
|
109
|
-
### one is not specified
|
110
|
-
### already.
|
97
|
+
### one is not specified.
|
111
98
|
def self::run( appid=nil )
|
112
99
|
appid ||= self.default_appid
|
113
100
|
self.log.info "Starting up with appid %p." % [ appid ]
|
@@ -134,134 +121,6 @@ class Strelka::App < Mongrel2::Handler
|
|
134
121
|
end
|
135
122
|
|
136
123
|
|
137
|
-
### Return a Hash of glob patterns for matching data directories for the latest
|
138
|
-
### versions of all installed gems which have a dependency on Strelka, keyed
|
139
|
-
### by gem name.
|
140
|
-
def self::discover_data_dirs
|
141
|
-
datadirs = {
|
142
|
-
'' => self.local_data_dirs
|
143
|
-
}
|
144
|
-
|
145
|
-
# Find all the gems that depend on Strelka
|
146
|
-
gems = Gem::Specification.find_all do |gemspec|
|
147
|
-
gemspec.dependencies.find {|dep| dep.name == 'strelka'}
|
148
|
-
end
|
149
|
-
|
150
|
-
self.log.debug "Found %d gems with a Strelka dependency" % [ gems.length ]
|
151
|
-
|
152
|
-
# Find all the files under those gems' data directories that match the application
|
153
|
-
# pattern
|
154
|
-
gems.sort.reverse.each do |gemspec|
|
155
|
-
# Only look at the latest version of the gem
|
156
|
-
next if datadirs.key?( gemspec.name )
|
157
|
-
datadirs[ gemspec.name ] = File.join( gemspec.full_gem_path, "data", gemspec.name )
|
158
|
-
end
|
159
|
-
|
160
|
-
self.log.debug " returning data directories: %p" % [ datadirs ]
|
161
|
-
return datadirs
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
### Return a Hash of Strelka app files as Pathname objects from installed gems,
|
166
|
-
### keyed by gemspec name .
|
167
|
-
def self::discover_paths
|
168
|
-
appfiles = {}
|
169
|
-
|
170
|
-
self.discover_data_dirs.each do |gemname, dir|
|
171
|
-
pattern = File.join( dir, self.app_glob_pattern )
|
172
|
-
appfiles[ gemname ] = Pathname.glob( pattern )
|
173
|
-
end
|
174
|
-
|
175
|
-
return appfiles
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
### Return an Array of Strelka::App classes loaded from the installed Strelka gems.
|
180
|
-
def self::discover
|
181
|
-
discovered_apps = []
|
182
|
-
app_paths = self.discover_paths
|
183
|
-
|
184
|
-
self.log.debug "Loading apps from %d discovered paths" % [ app_paths.length ]
|
185
|
-
app_paths.each do |gemname, paths|
|
186
|
-
self.log.debug " loading gem %s" % [ gemname ]
|
187
|
-
gem( gemname ) unless gemname == ''
|
188
|
-
|
189
|
-
self.log.debug " loading apps from %s: %d handlers" % [ gemname, paths.length ]
|
190
|
-
paths.each do |path|
|
191
|
-
classes = begin
|
192
|
-
Strelka::App.load( path )
|
193
|
-
rescue StandardError, ScriptError => err
|
194
|
-
self.log.error "%p while loading Strelka apps from %s: %s" %
|
195
|
-
[ err.class, path, err.message ]
|
196
|
-
self.log.debug "Backtrace: %s" % [ err.backtrace.join("\n\t") ]
|
197
|
-
[]
|
198
|
-
end
|
199
|
-
self.log.debug " loaded app classes: %p" % [ classes ]
|
200
|
-
|
201
|
-
discovered_apps += classes
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
return discovered_apps
|
206
|
-
end
|
207
|
-
|
208
|
-
|
209
|
-
### Find the first app with the given +appname+ and return the path to its file and the name of
|
210
|
-
### the gem it's from. If the optional +gemname+ is given, only consider apps from that gem.
|
211
|
-
### Raises a RuntimeError if no app with the given +appname+ was found.
|
212
|
-
def self::find( appname, gemname=nil )
|
213
|
-
discovered_apps = self.discover_paths
|
214
|
-
|
215
|
-
path = nil
|
216
|
-
if gemname
|
217
|
-
discovered_apps[ gemname ].each do |apppath|
|
218
|
-
self.log.debug " %s (%s)" % [ apppath, apppath.basename('.rb') ]
|
219
|
-
if apppath.basename('.rb').to_s == appname
|
220
|
-
path = apppath
|
221
|
-
break
|
222
|
-
end
|
223
|
-
end
|
224
|
-
else
|
225
|
-
self.log.debug "No gem name; searching them all:"
|
226
|
-
discovered_apps.each do |disc_gemname, paths|
|
227
|
-
self.log.debug " %s: %d paths" % [ disc_gemname, paths.length ]
|
228
|
-
path = paths.find do |apppath|
|
229
|
-
self.log.debug " %s (%s)" % [ apppath, apppath.basename('.rb') ]
|
230
|
-
self.log.debug " %p vs. %p" % [ apppath.basename('.rb').to_s, appname ]
|
231
|
-
apppath.basename('.rb').to_s == appname
|
232
|
-
end or next
|
233
|
-
gemname = disc_gemname
|
234
|
-
break
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
unless path
|
239
|
-
msg = "Couldn't find an app named '#{appname}'"
|
240
|
-
msg << " in the #{gemname} gem" if gemname
|
241
|
-
raise( msg )
|
242
|
-
end
|
243
|
-
self.log.debug " found: %s" % [ path ]
|
244
|
-
|
245
|
-
return path, gemname
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
### Load the specified +file+, and return any Strelka::App subclasses that are loaded
|
250
|
-
### as a result.
|
251
|
-
def self::load( file )
|
252
|
-
self.log.debug "Loading application/s from %p" % [ file ]
|
253
|
-
@loading_file = Pathname( file ).expand_path
|
254
|
-
self.subclasses.delete( @loading_file )
|
255
|
-
Kernel.load( @loading_file.to_s )
|
256
|
-
new_subclasses = self.subclasses[ @loading_file ]
|
257
|
-
self.log.debug " loaded %d new app class/es" % [ new_subclasses.size ]
|
258
|
-
|
259
|
-
return new_subclasses
|
260
|
-
ensure
|
261
|
-
@loading_file = nil
|
262
|
-
end
|
263
|
-
|
264
|
-
|
265
124
|
#
|
266
125
|
# :section: Application declarative methods
|
267
126
|
#
|
@@ -463,6 +322,7 @@ class Strelka::App < Mongrel2::Handler
|
|
463
322
|
# Some status codes allow explanatory text to be returned; some forbid it. Append the
|
464
323
|
# message for those that allow one.
|
465
324
|
unless request.verb == :HEAD || response.bodiless?
|
325
|
+
self.log.debug "Writing plain-text response body: %p" % [ message ]
|
466
326
|
response.content_type = 'text/plain'
|
467
327
|
response.puts( message )
|
468
328
|
end
|
@@ -10,7 +10,7 @@ require 'strelka/plugins'
|
|
10
10
|
|
11
11
|
|
12
12
|
# This is a shared behavior for specs which different Strelka::App
|
13
|
-
# plugins share in common. If you're creating
|
13
|
+
# plugins share in common. If you're creating A Strelka Plugin,
|
14
14
|
# you can test its conformity to the expectations placed on them by
|
15
15
|
# adding this to your spec:
|
16
16
|
#
|
@@ -18,11 +18,11 @@ require 'strelka/plugins'
|
|
18
18
|
#
|
19
19
|
# describe YourPlugin do
|
20
20
|
#
|
21
|
-
# it_should_behave_like "A Strelka
|
21
|
+
# it_should_behave_like "A Strelka Plugin"
|
22
22
|
#
|
23
23
|
# end
|
24
24
|
|
25
|
-
shared_examples_for "A Strelka
|
25
|
+
shared_examples_for "A Strelka Plugin" do
|
26
26
|
|
27
27
|
let( :plugin ) do
|
28
28
|
described_class
|
@@ -30,7 +30,7 @@ shared_examples_for "A Strelka::App Plugin" do
|
|
30
30
|
|
31
31
|
|
32
32
|
it "extends Strelka::Plugin" do
|
33
|
-
plugin.
|
33
|
+
expect( plugin ).to be_a( Strelka::Plugin )
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
# encoding: utf-8
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'strelka' unless defined?( Strelka )
|
7
|
+
|
8
|
+
|
9
|
+
# The Strelka application-discovery system.
|
10
|
+
module Strelka::Discovery
|
11
|
+
extend Loggability,
|
12
|
+
Configurability,
|
13
|
+
Strelka::MethodUtilities
|
14
|
+
|
15
|
+
|
16
|
+
# Loggability API -- log to the Strelka logger
|
17
|
+
log_to :strelka
|
18
|
+
|
19
|
+
# Configurability API -- use the 'discovery' section of the config
|
20
|
+
config_key :discovery
|
21
|
+
|
22
|
+
|
23
|
+
# Default config
|
24
|
+
CONFIG_DEFAULTS = {
|
25
|
+
app_glob_pattern: '{apps,handlers}/**/*',
|
26
|
+
local_data_dirs: 'data/*',
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
# The Hash of Strelka::App subclasses, keyed by the Pathname of the file they were
|
32
|
+
# loaded from, or +nil+ if they weren't loaded via ::load.
|
33
|
+
singleton_attr_reader :subclasses
|
34
|
+
|
35
|
+
##
|
36
|
+
# The glob(3) pattern for matching Apps during discovery
|
37
|
+
singleton_attr_accessor :app_glob_pattern
|
38
|
+
|
39
|
+
##
|
40
|
+
# The glob(3) pattern for matching local data directories during discovery. Local
|
41
|
+
# data directories are evaluated relative to the CWD.
|
42
|
+
singleton_attr_accessor :local_data_dirs
|
43
|
+
|
44
|
+
##
|
45
|
+
# The name of the file that's currently being loaded (if any)
|
46
|
+
singleton_attr_reader :loading_file
|
47
|
+
|
48
|
+
|
49
|
+
# Module instance variables
|
50
|
+
@subclasses = Hash.new {|h,k| h[k] = [] }
|
51
|
+
@loading_file = nil
|
52
|
+
@app_glob_pattern = CONFIG_DEFAULTS[:app_glob_pattern]
|
53
|
+
@local_data_dirs = CONFIG_DEFAULTS[:local_data_dirs]
|
54
|
+
|
55
|
+
|
56
|
+
### Configure the App. Override this if you wish to add additional configuration
|
57
|
+
### to the 'app' section of the config that will be passed to you when the config
|
58
|
+
### is loaded.
|
59
|
+
def self::configure( config=nil )
|
60
|
+
config = self.defaults.merge( config || {} )
|
61
|
+
|
62
|
+
self.app_glob_pattern = config[:app_glob_pattern]
|
63
|
+
self.local_data_dirs = config[:local_data_dirs]
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
### Return a Hash of glob patterns for matching data directories for the latest
|
68
|
+
### versions of all installed gems which have a dependency on Strelka, keyed
|
69
|
+
### by gem name.
|
70
|
+
def self::discover_data_dirs
|
71
|
+
datadirs = {
|
72
|
+
'' => self.local_data_dirs
|
73
|
+
}
|
74
|
+
|
75
|
+
# Find all the gems that depend on Strelka
|
76
|
+
gems = Gem::Specification.find_all do |gemspec|
|
77
|
+
gemspec.dependencies.find {|dep| dep.name == 'strelka'}
|
78
|
+
end
|
79
|
+
|
80
|
+
self.log.debug "Found %d gems with a Strelka dependency" % [ gems.length ]
|
81
|
+
|
82
|
+
# Find all the files under those gems' data directories that match the application
|
83
|
+
# pattern
|
84
|
+
gems.sort.reverse.each do |gemspec|
|
85
|
+
# Only look at the latest version of the gem
|
86
|
+
next if datadirs.key?( gemspec.name )
|
87
|
+
datadirs[ gemspec.name ] = File.join( gemspec.full_gem_path, "data", gemspec.name )
|
88
|
+
end
|
89
|
+
|
90
|
+
self.log.debug " returning data directories: %p" % [ datadirs ]
|
91
|
+
return datadirs
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
### Return a Hash of Strelka app files as Pathname objects from installed gems,
|
96
|
+
### keyed by gemspec name .
|
97
|
+
def self::discover_paths
|
98
|
+
appfiles = {}
|
99
|
+
|
100
|
+
self.discover_data_dirs.each do |gemname, dir|
|
101
|
+
pattern = File.join( dir, self.app_glob_pattern )
|
102
|
+
appfiles[ gemname ] = Pathname.glob( pattern )
|
103
|
+
end
|
104
|
+
|
105
|
+
return appfiles
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
### Return an Array of Strelka::App classes loaded from the installed Strelka gems.
|
110
|
+
def self::discover
|
111
|
+
discovered_apps = []
|
112
|
+
app_paths = self.discover_paths
|
113
|
+
|
114
|
+
self.log.debug "Loading apps from %d discovered paths" % [ app_paths.length ]
|
115
|
+
app_paths.each do |gemname, paths|
|
116
|
+
self.log.debug " loading gem %s" % [ gemname ]
|
117
|
+
gem( gemname ) unless gemname == ''
|
118
|
+
|
119
|
+
self.log.debug " loading apps from %s: %d handlers" % [ gemname, paths.length ]
|
120
|
+
paths.each do |path|
|
121
|
+
classes = begin
|
122
|
+
self.load( path )
|
123
|
+
rescue StandardError, ScriptError => err
|
124
|
+
self.log.error "%p while loading Strelka apps from %s: %s" %
|
125
|
+
[ err.class, path, err.message ]
|
126
|
+
self.log.debug "Backtrace: %s" % [ err.backtrace.join("\n\t") ]
|
127
|
+
[]
|
128
|
+
end
|
129
|
+
self.log.debug " loaded app classes: %p" % [ classes ]
|
130
|
+
|
131
|
+
discovered_apps += classes
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
return discovered_apps
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
### Find the first app with the given +appname+ and return the path to its file and the name of
|
140
|
+
### the gem it's from. If the optional +gemname+ is given, only consider apps from that gem.
|
141
|
+
### Raises a RuntimeError if no app with the given +appname+ was found.
|
142
|
+
def self::find( appname, gemname=nil )
|
143
|
+
discovered_apps = self.discover_paths
|
144
|
+
|
145
|
+
path = nil
|
146
|
+
if gemname
|
147
|
+
discovered_apps[ gemname ].each do |apppath|
|
148
|
+
self.log.debug " %s (%s)" % [ apppath, apppath.basename('.rb') ]
|
149
|
+
if apppath.basename('.rb').to_s == appname
|
150
|
+
path = apppath
|
151
|
+
break
|
152
|
+
end
|
153
|
+
end
|
154
|
+
else
|
155
|
+
self.log.debug "No gem name; searching them all:"
|
156
|
+
discovered_apps.each do |disc_gemname, paths|
|
157
|
+
self.log.debug " %s: %d paths" % [ disc_gemname, paths.length ]
|
158
|
+
path = paths.find do |apppath|
|
159
|
+
self.log.debug " %s (%s)" % [ apppath, apppath.basename('.rb') ]
|
160
|
+
self.log.debug " %p vs. %p" % [ apppath.basename('.rb').to_s, appname ]
|
161
|
+
apppath.basename('.rb').to_s == appname
|
162
|
+
end or next
|
163
|
+
gemname = disc_gemname
|
164
|
+
break
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
unless path
|
169
|
+
msg = "Couldn't find an app named '#{appname}'"
|
170
|
+
msg << " in the #{gemname} gem" if gemname
|
171
|
+
raise( msg )
|
172
|
+
end
|
173
|
+
self.log.debug " found: %s" % [ path ]
|
174
|
+
|
175
|
+
return path, gemname
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
### Load the specified +file+, and return any Strelka::App subclasses that are loaded
|
180
|
+
### as a result.
|
181
|
+
def self::load( file )
|
182
|
+
self.log.debug "Loading application/s from %p" % [ file ]
|
183
|
+
@loading_file = Pathname( file ).expand_path
|
184
|
+
self.subclasses.delete( @loading_file )
|
185
|
+
Kernel.load( @loading_file.to_s )
|
186
|
+
new_subclasses = self.subclasses[ @loading_file ]
|
187
|
+
self.log.debug " loaded %d new app class/es" % [ new_subclasses.size ]
|
188
|
+
|
189
|
+
return new_subclasses
|
190
|
+
ensure
|
191
|
+
@loading_file = nil
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
### Register the given +subclass+ as having inherited a class that has been extended
|
196
|
+
### with Discovery.
|
197
|
+
def self::add_inherited_class( subclass )
|
198
|
+
self.log.debug "Registering discovered subclass %p" % [ subclass ]
|
199
|
+
self.subclasses[ self.loading_file ] << subclass
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
### Inheritance callback -- register the subclass with its parent for discovery.
|
204
|
+
def inherited( subclass )
|
205
|
+
super
|
206
|
+
Strelka::Discovery.log.info "%p inherited by discoverable class %p" % [ self, subclass ]
|
207
|
+
Strelka::Discovery.add_inherited_class( subclass )
|
208
|
+
end
|
209
|
+
|
210
|
+
end # module Strelka::Discovery
|
211
|
+
|
data/lib/strelka/httprequest.rb
CHANGED
@@ -211,6 +211,7 @@ class Strelka::HTTPRequest < Mongrel2::HTTPRequest
|
|
211
211
|
when 'application/json', 'text/javascript'
|
212
212
|
return Yajl.load( self.body )
|
213
213
|
when 'text/x-yaml', 'application/x-yaml'
|
214
|
+
return nil if self.body.eof?
|
214
215
|
return YAML.load( self.body, safe: true )
|
215
216
|
when 'multipart/form-data'
|
216
217
|
boundary = self.content_type[ /\bboundary=(\S+)/, 1 ] or
|
@@ -352,7 +352,13 @@ module Strelka::HTTPResponse::Negotiation
|
|
352
352
|
def try_content_type_callback( mimetype, callback )
|
353
353
|
self.log.debug " trying content-type callback %p (%s)" % [ callback, mimetype ]
|
354
354
|
|
355
|
-
new_body =
|
355
|
+
new_body = begin
|
356
|
+
callback.call( mimetype )
|
357
|
+
rescue => err
|
358
|
+
self.log.error " %p raised from the callback for %s: %s" %
|
359
|
+
[ err.class, mimetype, err.message ]
|
360
|
+
return false
|
361
|
+
end
|
356
362
|
|
357
363
|
self.log.debug " successfully transformed: %p! Setting up response." % [ new_body.class ]
|
358
364
|
stringifiers = Strelka::HTTPResponse::Negotiation.stringifiers
|
data/lib/strelka/mixins.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
# vim: set nosta noet ts=4 sw=4:
|
3
3
|
# encoding: utf-8
|
4
4
|
|
5
|
+
require 'tempfile'
|
6
|
+
|
5
7
|
require 'strelka' unless defined?( Strelka )
|
6
8
|
require 'strelka/constants'
|
7
9
|
|
@@ -192,7 +194,8 @@ module Strelka
|
|
192
194
|
return obj if obj.class.name == 'RSpec::Mocks::Mock'
|
193
195
|
|
194
196
|
return case obj
|
195
|
-
when NilClass, Numeric, TrueClass, FalseClass, Symbol,
|
197
|
+
when NilClass, Numeric, TrueClass, FalseClass, Symbol,
|
198
|
+
Module, Encoding, IO, Tempfile
|
196
199
|
obj
|
197
200
|
|
198
201
|
when Array
|
@@ -753,7 +753,7 @@ class Strelka::ParamValidator
|
|
753
753
|
### the given +value+, and add the field to the appropriate field list based on the
|
754
754
|
### result.
|
755
755
|
def apply_constraint( constraint, value )
|
756
|
-
if !value.nil?
|
756
|
+
if !( value.nil? || value == '' )
|
757
757
|
result = constraint.apply( value, self.untaint_all? )
|
758
758
|
|
759
759
|
if !result.nil?
|
data/lib/strelka/plugins.rb
CHANGED
@@ -57,7 +57,7 @@ module Strelka
|
|
57
57
|
name = object.plugin_name
|
58
58
|
if (( deps = pluggable.loaded_plugins[name] ))
|
59
59
|
self.log.debug " installing deferred deps for %p" % [ name ]
|
60
|
-
object.
|
60
|
+
object.run_inside( *deps )
|
61
61
|
end
|
62
62
|
|
63
63
|
self.log.debug " adding %p (%p) to the plugin registry for %p" %
|
@@ -87,28 +87,30 @@ module Strelka
|
|
87
87
|
|
88
88
|
### Register the receiver as needing to be run before +other_plugins+ for requests, and
|
89
89
|
### *after* them for responses.
|
90
|
-
def
|
90
|
+
def run_outside( *other_plugins )
|
91
91
|
name = self.plugin_name
|
92
92
|
other_plugins.each do |other_name|
|
93
93
|
self.pluggable.loaded_plugins[ other_name ] ||= []
|
94
94
|
mod = self.pluggable.loaded_plugins[ other_name ]
|
95
95
|
|
96
|
-
if mod.respond_to?( :
|
97
|
-
mod.
|
96
|
+
if mod.respond_to?( :run_inside )
|
97
|
+
mod.run_inside( name )
|
98
98
|
else
|
99
99
|
self.log.debug "%p plugin not yet loaded; setting up pending deps" % [ other_name ]
|
100
100
|
mod << name
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
|
+
alias_method :run_before, :run_outside
|
104
105
|
|
105
106
|
|
106
107
|
### Register the receiver as needing to be run after +other_plugins+ for requests, and
|
107
108
|
### *before* them for responses.
|
108
|
-
def
|
109
|
+
def run_inside( *other_plugins )
|
109
110
|
self.log.debug " %p will run after %p" % [ self, other_plugins ]
|
110
111
|
self.successors.merge( other_plugins )
|
111
112
|
end
|
113
|
+
alias_method :run_after, :run_inside
|
112
114
|
|
113
115
|
end # module Plugin
|
114
116
|
|
@@ -244,7 +246,7 @@ module Strelka
|
|
244
246
|
|
245
247
|
|
246
248
|
### Install the mixin part of the plugin, in the order determined by
|
247
|
-
### the plugin registry based on the
|
249
|
+
### the plugin registry based on the run_outside and run_inside specifications
|
248
250
|
### of the plugins themselves.
|
249
251
|
def install_plugins
|
250
252
|
if self.plugins_installed?
|