strelka 0.0.1pre4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gemtest +0 -0
  2. data/History.rdoc +4 -0
  3. data/IDEAS.textile +174 -0
  4. data/Manifest.txt +38 -0
  5. data/README.rdoc +66 -0
  6. data/Rakefile +64 -0
  7. data/bin/leash +403 -0
  8. data/data/strelka/apps/strelka-admin +65 -0
  9. data/data/strelka/apps/strelka-setup +26 -0
  10. data/data/strelka/bootstrap-config.rb +34 -0
  11. data/data/strelka/templates/admin/console.tmpl +21 -0
  12. data/data/strelka/templates/layout.tmpl +30 -0
  13. data/lib/strelka/app/defaultrouter.rb +85 -0
  14. data/lib/strelka/app/filters.rb +70 -0
  15. data/lib/strelka/app/parameters.rb +64 -0
  16. data/lib/strelka/app/plugins.rb +205 -0
  17. data/lib/strelka/app/routing.rb +140 -0
  18. data/lib/strelka/app/templating.rb +157 -0
  19. data/lib/strelka/app.rb +175 -0
  20. data/lib/strelka/behavior/plugin.rb +36 -0
  21. data/lib/strelka/constants.rb +53 -0
  22. data/lib/strelka/httprequest.rb +52 -0
  23. data/lib/strelka/logging.rb +241 -0
  24. data/lib/strelka/mixins.rb +143 -0
  25. data/lib/strelka/process.rb +19 -0
  26. data/lib/strelka.rb +40 -0
  27. data/spec/data/layout.tmpl +3 -0
  28. data/spec/data/main.tmpl +1 -0
  29. data/spec/lib/constants.rb +32 -0
  30. data/spec/lib/helpers.rb +134 -0
  31. data/spec/strelka/app/defaultrouter_spec.rb +215 -0
  32. data/spec/strelka/app/parameters_spec.rb +74 -0
  33. data/spec/strelka/app/plugins_spec.rb +167 -0
  34. data/spec/strelka/app/routing_spec.rb +139 -0
  35. data/spec/strelka/app/templating_spec.rb +169 -0
  36. data/spec/strelka/app_spec.rb +160 -0
  37. data/spec/strelka/httprequest_spec.rb +54 -0
  38. data/spec/strelka/logging_spec.rb +72 -0
  39. data/spec/strelka_spec.rb +27 -0
  40. metadata +226 -0
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
6
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
7
+ }
8
+
9
+ require 'rspec'
10
+
11
+ require 'spec/lib/helpers'
12
+
13
+ require 'strelka'
14
+ require 'strelka/app/plugins'
15
+ require 'strelka/app/routing'
16
+
17
+ require 'strelka/behavior/plugin'
18
+
19
+
20
+ #####################################################################
21
+ ### C O N T E X T S
22
+ #####################################################################
23
+
24
+ describe Strelka::App::Routing do
25
+
26
+ before( :all ) do
27
+ setup_logging( :fatal )
28
+ end
29
+
30
+ after( :all ) do
31
+ reset_logging()
32
+ end
33
+
34
+
35
+ it_should_behave_like( "A Strelka::App Plugin" )
36
+
37
+
38
+ describe "an including App" do
39
+
40
+ before( :each ) do
41
+ Strelka.log.debug "Creating a new Strelka::App"
42
+ @app = Class.new( Strelka::App ) do
43
+ plugin :routing
44
+ end
45
+ Strelka.log.debug " new instance is: %p, routes array: 0x%016x" %
46
+ [ @app, @app.routes.object_id * 2 ]
47
+ end
48
+
49
+
50
+ it "has an Array of raw routes" do
51
+ @app.routes.should be_a( Array )
52
+ end
53
+
54
+ it "can declare a GET route" do
55
+ @app.routes.should be_empty()
56
+
57
+ @app.class_eval do
58
+ get do |req|
59
+ end
60
+ end
61
+
62
+ @app.routes.should == [[ :GET, [], @app.instance_method(:GET), {} ]]
63
+ end
64
+
65
+ it "allows a route to specify a path" do
66
+ @app.routes.should be_empty()
67
+
68
+ @app.class_eval do
69
+ get '/info' do |req|
70
+ end
71
+ end
72
+
73
+ @app.routes.should == [[ :GET, ['info'], @app.instance_method(:GET_info), {} ]]
74
+ end
75
+
76
+ it "allows a route to omit the leading '/' when specifying a path" do
77
+ @app.class_eval do
78
+ get 'info' do |req|
79
+ end
80
+ end
81
+
82
+ @app.routes.should == [[ :GET, ['info'], @app.instance_method(:GET_info), {} ]]
83
+ end
84
+
85
+
86
+ it "uses the Strelka::App::DefaultRouter as it's router by default" do
87
+ @app.routerclass.should equal( Strelka::App::DefaultRouter )
88
+ end
89
+
90
+ it "can specify a different Router class than the default" do
91
+ class MyRouter < Strelka::App::DefaultRouter; end
92
+ @app.class_eval do
93
+ router MyRouter
94
+ end
95
+ @app.routerclass.should equal( MyRouter )
96
+ end
97
+
98
+
99
+ describe "that also uses the :parameters plugin" do
100
+
101
+ before( :each ) do
102
+ @app.plugin( :parameters )
103
+ end
104
+
105
+ it "allows a route to have parameters in it" do
106
+ @app.class_eval do
107
+ param :username, /[a-z]\w+/i
108
+ post '/userinfo/:username' do |req|
109
+ end
110
+ end
111
+
112
+ @app.routes.should ==
113
+ [[ :POST, ['userinfo', /(?<username>(?i-mx:[a-z]\w+))/],
114
+ @app.instance_method(:POST_userinfo__username), {} ]]
115
+ end
116
+
117
+
118
+ it "raises a ScriptError if a route is defined with a param without it having first " +
119
+ "been set up" do
120
+ # RSpec's expect {},to only rescues RuntimeErrors, so we have to do this ourselves.
121
+ begin
122
+ @app.get( '/userinfo/:username' ) {}
123
+ rescue ScriptError => err
124
+ Strelka.log.error "%p: %s" % [ err.class, err.message ]
125
+ :pass
126
+ rescue ::Exception => err
127
+ fail "Expected to raise a ScriptError, but raised a %p instead" % [ err ]
128
+ else
129
+ fail "Expected to raise a ScriptError, but nothing was raised."
130
+ end
131
+
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+
138
+ end
139
+
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
6
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
7
+ }
8
+
9
+ require 'rspec'
10
+ require 'inversion'
11
+
12
+ require 'spec/lib/helpers'
13
+
14
+ require 'strelka'
15
+ require 'strelka/app/plugins'
16
+ require 'strelka/app/templating'
17
+
18
+ require 'strelka/behavior/plugin'
19
+
20
+
21
+ #####################################################################
22
+ ### C O N T E X T S
23
+ #####################################################################
24
+
25
+ describe Strelka::App::Templating do
26
+
27
+ before( :all ) do
28
+ setup_logging( :fatal )
29
+ @request_factory = Mongrel2::RequestFactory.new( route: '/user' )
30
+ end
31
+
32
+ after( :all ) do
33
+ reset_logging()
34
+ end
35
+
36
+
37
+ it_should_behave_like( "A Strelka::App Plugin" )
38
+
39
+
40
+ describe "an including App" do
41
+
42
+ before( :each ) do
43
+ @app = Class.new( Strelka::App ) do
44
+ plugin :templating
45
+ templates :main => 'main.tmpl'
46
+
47
+ def initialize( appid=TEST_APPID, sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
48
+ super
49
+ end
50
+ end
51
+ @req = @request_factory.get( '/user/info' )
52
+ end
53
+
54
+
55
+ it "has a Hash of templates" do
56
+ @app.templates.should be_a( Hash )
57
+ end
58
+
59
+ it "can add templates that it wants to use to its templates hash" do
60
+ @app.class_eval do
61
+ templates :main => 'main.tmpl'
62
+ end
63
+
64
+ @app.templates.should == { :main => 'main.tmpl' }
65
+ end
66
+
67
+ it "can declare a layout template" do
68
+ @app.class_eval do
69
+ layout 'layout.tmpl'
70
+ end
71
+
72
+ @app.layout.should == 'layout.tmpl'
73
+ end
74
+
75
+ describe "instance" do
76
+
77
+ before( :all ) do
78
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
79
+ specdir = basedir + 'spec'
80
+ specdata = specdir + 'data'
81
+
82
+ tmpl_paths = [ specdata ]
83
+ Inversion::Template.configure( :template_paths => tmpl_paths )
84
+ end
85
+
86
+ before( :each ) do
87
+ @app.class_eval do
88
+ templates :main => 'main.tmpl'
89
+ end
90
+ @instance = @app.new( 'template-test', 'tcp://127.0.0.1:9999', 'tcp://127.0.0.1:9998' )
91
+ end
92
+
93
+
94
+ it "can load declared templates by mentioning the symbol" do
95
+ @instance.template( :main ).should be_a( Inversion::Template )
96
+ end
97
+
98
+ it "can respond with just a template name" do
99
+ @app.class_eval do
100
+ def handle_request( req )
101
+ super { :main }
102
+ end
103
+ end
104
+
105
+ res = @app.new.handle( @req )
106
+
107
+ res.body.should == "A template for testing the Templating plugin.\n"
108
+ res.status.should == 200
109
+ end
110
+
111
+ it "can respond with just a template instance" do
112
+ @app.class_eval do
113
+ def handle_request( req )
114
+ super { self.template(:main) }
115
+ end
116
+ end
117
+
118
+ res = @app.new.handle( @req )
119
+
120
+ res.body.should == "A template for testing the Templating plugin.\n"
121
+ res.status.should == 200
122
+ end
123
+
124
+ it "can respond with a Mongrel2::HTTPResponse with a template instance as its body" do
125
+ @app.class_eval do
126
+ def handle_request( req )
127
+ super do
128
+ res = req.response
129
+ res.body = self.template( :main )
130
+ res
131
+ end
132
+ end
133
+ end
134
+
135
+ res = @app.new.handle( @req )
136
+
137
+ res.body.should == "A template for testing the Templating plugin.\n"
138
+ res.status.should == 200
139
+ end
140
+
141
+
142
+ it "wraps the layout template around whatever gets returned if one is set" do
143
+ @app.class_eval do
144
+ layout 'layout.tmpl'
145
+
146
+ def handle_request( req )
147
+ # Super through the plugins and then load the template into the response
148
+ super do
149
+ res = req.response
150
+ res.body = self.template( :main )
151
+ res
152
+ end
153
+ end
154
+ end
155
+
156
+ res = @app.new.handle( @req )
157
+
158
+ res.body.should == "A minimal layout template.\n" +
159
+ "A template for testing the Templating plugin.\n\n"
160
+ res.status.should == 200
161
+ end
162
+
163
+ end
164
+
165
+ end
166
+
167
+
168
+ end
169
+
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
7
+ }
8
+
9
+ require 'rspec'
10
+ require 'zmq'
11
+ require 'mongrel2'
12
+
13
+ require 'spec/lib/helpers'
14
+
15
+ require 'strelka'
16
+ require 'strelka/app'
17
+
18
+
19
+ #####################################################################
20
+ ### C O N T E X T S
21
+ #####################################################################
22
+
23
+ describe Strelka::App do
24
+
25
+ before( :all ) do
26
+ setup_logging( :fatal )
27
+ @request_factory = Mongrel2::RequestFactory.new( route: '/mail' )
28
+ end
29
+
30
+ before( :each ) do
31
+ @app = Class.new( Strelka::App ) do
32
+ def initialize( appid=TEST_APPID, sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
33
+ super
34
+ end
35
+ end
36
+ @req = @request_factory.get( '/mail/inbox' )
37
+ end
38
+
39
+ after( :all ) do
40
+ reset_logging()
41
+ end
42
+
43
+
44
+ it "returns a No Content response by default" do
45
+
46
+ res = @app.new.handle( @req )
47
+
48
+ res.should be_a( Mongrel2::HTTPResponse )
49
+ res.status_line.should == 'HTTP/1.1 204 No Content'
50
+ res.body.should == ''
51
+ end
52
+
53
+
54
+ it "provides a mechanism for aborting with a status" do
55
+
56
+ # make a plugin that always 304s and install it
57
+ not_modified_plugin = Module.new do
58
+ extend Strelka::App::Plugin
59
+ def handle_request( r )
60
+ finish_with( HTTP::NOT_MODIFIED, "Unchanged." )
61
+ fail "Shouldn't be reached."
62
+ end
63
+ end
64
+ @app.plugin( not_modified_plugin )
65
+
66
+ res = @app.new.handle( @req )
67
+
68
+ res.should be_a( Mongrel2::HTTPResponse )
69
+ res.status_line.should == 'HTTP/1.1 304 Not Modified'
70
+ res.body.should == ''
71
+ end
72
+
73
+
74
+ it "creates a simple response body for status responses that can have them" do
75
+ # make an auth plugin that always denies requests
76
+ forbidden_plugin = Module.new do
77
+ extend Strelka::App::Plugin
78
+ def handle_request( r )
79
+ finish_with( HTTP::FORBIDDEN, "You aren't allowed to look at that." )
80
+ fail "Shouldn't be reached."
81
+ end
82
+ end
83
+ @app.plugin( forbidden_plugin )
84
+
85
+ res = @app.new.handle( @req )
86
+
87
+ res.should be_a( Mongrel2::HTTPResponse )
88
+ res.status_line.should == 'HTTP/1.1 403 Forbidden'
89
+ res.content_type.should == 'text/plain'
90
+ res.body.should == "You aren't allowed to look at that.\n"
91
+ end
92
+
93
+
94
+ it "provides a declarative for setting the default content type of responses" do
95
+ @app.class_eval do
96
+ default_content_type 'text/css'
97
+ def handle_request( r )
98
+ r.response.puts( "body { font-family: monospace }" )
99
+ r.response
100
+ end
101
+ end
102
+
103
+ res = @app.new.handle( @req )
104
+
105
+ res.should be_a( Mongrel2::HTTPResponse )
106
+ res.content_type.should == 'text/css'
107
+ end
108
+
109
+
110
+ it "provides a plugin hook for plugins to manipulate the request before handling it" do
111
+ # make a fixup plugin that adds a custom x- header to the request
112
+ header_fixup_plugin = Module.new do
113
+ extend Strelka::App::Plugin
114
+ def fixup_request( r )
115
+ r.headers[:x_funted_by] = 'Cragnux/1.1.3'
116
+ super
117
+ end
118
+ def handle_request( r )
119
+ res = r.response
120
+ res.puts( "Request was funted by %s!" % [r.headers.x_funted_by] )
121
+ res.status = HTTP::OK
122
+ return res
123
+ end
124
+ end
125
+ @app.plugin( header_fixup_plugin )
126
+
127
+ res = @app.new.handle( @req )
128
+
129
+ res.should be_a( Mongrel2::HTTPResponse )
130
+ res.status_line.should == 'HTTP/1.1 200 OK'
131
+ res.body.should == "Request was funted by Cragnux/1.1.3!\n"
132
+ end
133
+
134
+
135
+ it "provides a plugin hook for plugins to manipulate the response before it's returned to Mongrel2" do
136
+ # make a fixup plugin that adds a custom x- header to the response
137
+ header_fixup_plugin = Module.new do
138
+ extend Strelka::App::Plugin
139
+ def fixup_response( req, res )
140
+ res.headers.x_funted_by = 'Cragnux/1.1.3'
141
+ super
142
+ end
143
+ def handle_request( r )
144
+ res = r.response
145
+ res.puts( "Funt this" )
146
+ res.status = HTTP::OK
147
+ return res
148
+ end
149
+ end
150
+ @app.plugin( header_fixup_plugin )
151
+
152
+ res = @app.new.handle( @req )
153
+
154
+ res.should be_a( Mongrel2::HTTPResponse )
155
+ res.status_line.should == 'HTTP/1.1 200 OK'
156
+ res.header_data.should =~ %r{X-Funted-By: Cragnux/1.1.3}
157
+ end
158
+
159
+ end
160
+
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
8
+ }
9
+
10
+ require 'rspec'
11
+ require 'spec/lib/helpers'
12
+ require 'strelka/httprequest'
13
+
14
+
15
+ #####################################################################
16
+ ### C O N T E X T S
17
+ #####################################################################
18
+
19
+ describe Strelka::HTTPRequest do
20
+
21
+ before( :all ) do
22
+ setup_logging( :fatal )
23
+ @request_factory = Mongrel2::RequestFactory.new( route: '/directory' )
24
+ end
25
+
26
+ after( :all ) do
27
+ reset_logging()
28
+ end
29
+
30
+
31
+ before( :each ) do
32
+ @req = @request_factory.get( '/directory/userinfo/ged' )
33
+ end
34
+
35
+ it "knows what the request's parsed URI is" do
36
+ @req.uri.should be_a( URI )
37
+ @req.uri.path.should == '/directory/userinfo/ged'
38
+ @req.uri.query.should be_nil()
39
+ end
40
+
41
+ it "knows what Mongrel2 route it followed" do
42
+ @req.pattern.should == '/directory'
43
+ end
44
+
45
+ it "knows what the path of the request past its route is" do
46
+ @req.app_path.should == '/userinfo/ged'
47
+ end
48
+
49
+ it "knows what HTTP verb the request used" do
50
+ @req.verb.should == :GET
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
7
+ }
8
+
9
+ require 'rspec'
10
+ require 'stringio'
11
+
12
+ require 'spec/lib/helpers'
13
+
14
+ require 'strelka'
15
+ require 'strelka/logging'
16
+
17
+
18
+ #####################################################################
19
+ ### C O N T E X T S
20
+ #####################################################################
21
+
22
+ describe Strelka::Logging, "-extended module" do
23
+
24
+ before( :each ) do
25
+ @extended_module = Module.new do
26
+ extend Strelka::Logging
27
+ end
28
+ end
29
+
30
+ it "should have a default Logger" do
31
+ @extended_module.logger.should be_a( Logger )
32
+ @extended_module.default_logger.should equal( @extended_module.logger )
33
+ end
34
+
35
+ it "should know if its default logger is replaced" do
36
+ @extended_module.should be_using_default_logger
37
+ @extended_module.logger = Logger.new( $stderr )
38
+ @extended_module.should_not be_using_default_logger
39
+ end
40
+
41
+ it "has the default logger instance after being reset" do
42
+ @extended_module.reset_logger
43
+ @extended_module.logger.should equal( @extended_module.default_logger )
44
+ end
45
+
46
+ it "has the default log formatter instance after being reset" do
47
+ @extended_module.reset_logger
48
+ @extended_module.logger.formatter.should equal( @extended_module.default_log_formatter )
49
+ end
50
+
51
+
52
+ context "with new defaults" do
53
+
54
+ before( :each ) do
55
+ @sink = StringIO.new
56
+ @logger = Logger.new( @sink )
57
+ @formatter = Strelka::Logging::ColorFormatter.new( @logger )
58
+
59
+ @extended_module.default_logger = @logger
60
+ @extended_module.default_log_formatter = @formatter
61
+ end
62
+
63
+ it "uses the new defaults when the logging subsystem is reset" do
64
+ @extended_module.reset_logger
65
+ @extended_module.logger.should equal( @logger )
66
+ end
67
+
68
+ end
69
+
70
+
71
+ end
72
+
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname( __FILE__ ).dirname.parent
6
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
7
+ }
8
+
9
+ require 'rspec'
10
+ require 'spec/lib/helpers'
11
+ require 'strelka'
12
+
13
+ describe Strelka do
14
+
15
+ describe "version methods" do
16
+ it "returns a version string if asked" do
17
+ described_class.version_string.should =~ /\w+ [\d.]+/
18
+ end
19
+
20
+
21
+ it "returns a version string with a build number if asked" do
22
+ described_class.version_string(true).should =~ /\w+ [\d.]+ \(build [[:xdigit:]]+\)/
23
+ end
24
+ end
25
+
26
+ end
27
+