strelka 0.6.0 → 0.7.0
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.
- 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?
|