bruter 0.0.2

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.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Ted Kimble
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # Bruter
2
+
3
+ Bruter is a miniature Ruby library that allows its bearer the ability to craft elegant Rack-based web applications with ease. Bruter stands on the backs of giants:
4
+
5
+ * Servers interface with Bruter -- a [Rack][rack]-compatible application -- with minimal configuration
6
+ * Variably named paths are routed to their proper destination by [HttpRouter][http_router]
7
+ * Dynamic assets -- Sass, LESS, CoffeeScript and more -- are compiled and served by [Sprockets][sprockets]
8
+ * Views and templates are rendered as one by [Mustache][mustache]
9
+
10
+ Surely under 200 lines of code without comments, Bruter is meant to be easy to understand and use. But do not underestimate Bruter's small footprint: Bruter's role lies in its ability to delegate responsibility and provide an intuitive syntax.
11
+
12
+ ## Example
13
+
14
+ Bruter can be found hiding in the usual places.
15
+
16
+ `gem install bruter`
17
+
18
+ You'll need to create a Rackup file.
19
+
20
+ ```ruby
21
+ # config.ru
22
+ require 'bruter'
23
+ $:.unshift File.dirname(__FILE__)
24
+ require 'app/application'
25
+ require 'app/views/hello' # Note that you need to require any files you'll reference, including views
26
+
27
+ run Application.new
28
+ ```
29
+
30
+ Routes and asset paths are defined in a subclass of `Bruter::Application`.
31
+
32
+ ```ruby
33
+ # app/application.rb
34
+
35
+ class Application < Bruter::Application
36
+
37
+ serve_assets 'assets' # Asset paths are relative to where the method is called
38
+ get '/hello/:name', to: 'views/hello' # Bruter will look for a Views::Hello class to render
39
+
40
+ end
41
+ ```
42
+
43
+ View classes should subclass `Bruter::Mustache`.
44
+
45
+ ```ruby
46
+ # app/views/hello.rb
47
+
48
+ module Views
49
+ class Hello < Bruter::Mustache
50
+
51
+ def name
52
+ params[:name]
53
+ end
54
+
55
+ end
56
+ end
57
+ ```
58
+
59
+ Bruter's Mustache will automatically look for an accompanying template in `'../templates'` relative to the view class.
60
+
61
+ ```html
62
+ # app/templates/hello.mustache
63
+
64
+ <!DOCTYPE html>
65
+ <html lang="en">
66
+ <head>
67
+ <meta charset="utf-8">
68
+ <title>Hello {{name}}!</title>
69
+ <link rel="stylesheet" href="/stylesheets/application.css">
70
+ </head>
71
+ <body>
72
+ <h1>Bruter kindly welcomes you, {{name}}!</h1>
73
+ <img src="/images/bruter.jpg">
74
+ </body>
75
+ </html>
76
+ ```
77
+
78
+ Place `'bruter.jpg'` in `'app/assets/images'` and a stylesheet of your preferred format in `'app/assets/stylesheets'` and Sprockets will take care of the rest.
79
+
80
+ ```css
81
+ # app/assets/stylesheets/application.css.scss
82
+
83
+ body {
84
+ text-align: center
85
+ h1 { font-size: 1.25em; }
86
+ }
87
+ ```
88
+
89
+ Bruter is a Rack application; choose any compatible server.
90
+
91
+ `rackup config.ru`
92
+
93
+ ## Routing
94
+
95
+ Routing is handled by HttpRouter. Most applications will subclass `Bruter::Application` and generate routes by calling the class method `:add`. The method accepts a path and a hash of options.
96
+
97
+ ```ruby
98
+ add(path, options = {})
99
+ ```
100
+
101
+ Most routes will want to specify a `:to` option. The `:to` option can be any Rack application.
102
+
103
+ ```ruby
104
+ add '/welcome', to: [200, {'Content-Type' => 'text/html'}, ['Welcome to Bruter.']]
105
+ ```
106
+
107
+ Or more interestingly, it can be a string representing a Mustache view class.
108
+
109
+ ```ruby
110
+ add '/welcome', to: 'welcome' # Looks for Welcome
111
+ add '/hello', to: 'views/hello_there' # Looks for Views::HelloThere
112
+ ```
113
+
114
+ The path can include named variables and globs.
115
+
116
+ ```ruby
117
+ add '/hello/:name', to: 'hello' # Matches '/hello/bruter' and '/hello/ted'
118
+ add '/tagged/*tags', to: 'tagged' # Matches '/tagged/web' and '/tagged/volcano/lava/dinosaurs'
119
+ ```
120
+
121
+ Routes can be restricted to certain HTTP request methods.
122
+
123
+ ```ruby
124
+ add '/post/:id', to: 'posts/show', request_method: ['GET', 'HEAD']
125
+ ```
126
+
127
+ Routes can be named in order to construct their paths at a later time.
128
+
129
+ ```ruby
130
+ add '/post/:id', to: 'posts/show', name: :post # In a view, calling path(:post, 5) returns '/post/5'
131
+ ```
132
+
133
+ Variable paths can have a regex matching condition placed on them.
134
+
135
+ ```ruby
136
+ add '/:a-plus-:b-equals', to: 'add', matching: {a: /\d+/, b: /\d+/}
137
+ ```
138
+
139
+ ### Redirects
140
+
141
+ Redirects can be added by setting a `:redirect` option instead of a `:to` option.
142
+
143
+ ```ruby
144
+ add '/old-path', redirect: '/new-path'
145
+ ```
146
+
147
+ ### Static Files
148
+
149
+ Static files can be served by the router by setting a `:static` option instead of a `:to` option.
150
+
151
+ ```ruby
152
+ add '/images', static: 'app/images' # Will look for '/images/logo/large.png' in 'app/images/logo/large.png'
153
+ ```
154
+
155
+ ### Convenience Methods
156
+
157
+ Bruter also provides a number of convenience methods that pass to `:add` but first set their `:request_method` option to correspond to the name of the method.
158
+
159
+ ```ruby
160
+ get(path, options = {})
161
+ post(path, options = {})
162
+ put(path, options = {})
163
+ delete(path, options = {})
164
+ head(path, options = {})
165
+ options(path, options = {})
166
+ trace(path, options = {})
167
+ get(path, options = {})
168
+ ```
169
+
170
+ ### Under the Hood
171
+
172
+ When a new `Bruter::Application` object is instantiated, an `HttpRouter` object is also instantiated and any routes added to the application class are added to the router object. This object can also be accessed directly with the `:router` getter method.
173
+
174
+ ```ruby
175
+ app = Bruter::Application.new
176
+ app.router.add('/').to('my_view')
177
+ run app
178
+ ```
179
+
180
+ See [HttpRouter][http_router] for more information.
181
+
182
+ ## Assets
183
+
184
+ Asset compilation and serving is handled by Sprockets. Asset paths can be added to the asset environment with the class method `:serve_assets`. The method accepts a path, which is relative to the location the method is called.
185
+
186
+ ```ruby
187
+ # app/application.rb
188
+ class Application < Bruter::Application
189
+
190
+ serve_assets 'assets'
191
+
192
+ end
193
+ ```
194
+
195
+ This application will compile and serve assets located in `'app/assets'`. For example, a route of `/images/logo.png` will have the asset environment look for a file named `'app/assets/images/logo.png'` and compile (if necessary) and serve it. If it cannot be found, action is passed to the router.
196
+
197
+ Like with `HttpRouter`, a new `Sprockets::Environment` object is instantiated and any asset paths appended to it when a new `Bruter::Application` object is instantiated. This environment can be accessed directly with the `:assets` getter method.
198
+
199
+ ```ruby
200
+ app = Bruter::Application.new
201
+ app.assets.append_path('my/assets/path')
202
+ run app
203
+ ```
204
+
205
+ See [Sprockets][sprockets] for more information.
206
+
207
+ ## Rendering
208
+
209
+ Routes can be directed to Rack-compatible applications or to strings that represent Mustache view classes. When the latter occurs the application will instantiate the view class with three objects -- a Rack request object, a Rack response object, and the Router object -- and call render. The view class can simply subclass from `Mustache`, but it will likely want to subclass `Bruter::Mustache`, itself a subclass of `Mustache`.
210
+
211
+ `Bruter::Mustache` provides a few helpers. First, it tells any subclasses to look for their templates in `'../template'` relative to the view file. It provides an `:initialize` method that accepts the three previously named objects and then attempts to invoke `:setup` if the view has defined it. It provides getter methods to access the Rack request object (`:request`), the Rack response object (`:response`), the Router object (`:router`), the Rack env (`:env`) and the params set during routing (`:params`). It also defines a `:path` method, which accepts the name of a named route and an optional list of arguments to construct a path string.
212
+
213
+ For more information, see [Mustache][mustache].
214
+
215
+ ## License
216
+
217
+ Bruter is Copyright &copy; 2011 by Ted Kimble and distributed under the MIT license; see `LICENSE` for more information.
218
+
219
+ [rack]: https://github.com/rack/rack
220
+ [http_router]: https://github.com/joshbuddy/http_router
221
+ [sprockets]: https://github.com/sstephenson/sprockets
222
+ [mustache]: https://github.com/defunkt/mustache
data/lib/bruter.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rack'
2
+ require 'http_router'
3
+ require 'sprockets'
4
+ require 'mustache'
5
+ require 'active_support/core_ext/string/inflections'
6
+ require 'bruter/version'
7
+ require 'bruter/router'
8
+ require 'bruter/mustache'
9
+ require 'bruter/application'
@@ -0,0 +1,158 @@
1
+ module Bruter
2
+ class Application
3
+
4
+ class << self
5
+ attr_accessor :_directory, :routes, :asset_paths
6
+
7
+ # Hack to set :_directory to the file location in which this class is
8
+ # subclassed. Those not subclassing Bruter::Mustache in a file may want
9
+ # to explicitly set :_directory.
10
+ def inherited(klass)
11
+ klass._directory = File.dirname(caller.first[/^[^:]+/])
12
+ end
13
+
14
+ # Retrieves the absolute path of the argument path relative to where
15
+ # Bruter::Application was subclassed (:_directory, see above).
16
+ def absolute_path(path)
17
+ File.expand_path(path, _directory)
18
+ end
19
+
20
+ # Ensure that :routes defaults to an array.
21
+ def routes
22
+ @routes ||= []
23
+ end
24
+
25
+ # Ensure that :asset_paths defaults to an array.
26
+ def asset_paths
27
+ @asset_paths ||= []
28
+ end
29
+
30
+ # Adds a route to the application. Accepts the path of the route and a
31
+ # hash of options. The path is required and can include named variables
32
+ # ('/post/:id') and globs ('/tags/*tags').
33
+ #
34
+ # The options hash must include either a :to, :redirect or :static key;
35
+ # the route will not be recognized if none is present. If more than one
36
+ # is present the preceding order of keys will serve as precedence.
37
+ #
38
+ # The :to key should be either a Rack application or a string
39
+ # representing a Mustache view class. (eg 'my_blog/index' =>
40
+ # MyBlog::Index)
41
+ #
42
+ # The :redirect key should be a string representing the new path or
43
+ # url that the route should redirect to.
44
+ #
45
+ # The :static key should be a string representing a path on the file
46
+ # system (relative to where the method was called).
47
+ #
48
+ # Additional options:
49
+ # Add a name to the route (:name => :posts)
50
+ # Add a regex matching condition (:matching => {:id => /\d+/})
51
+ # Add a HTTP method constraint (:request_method => ['POST', 'HEAD'])
52
+ #
53
+ # Examples:
54
+ # add '/', to: 'views/posts/index', name: :posts
55
+ # add '/posts/:id', to: 'views/posts/show', name: :post
56
+ # add '/posts/:id', to: 'views/posts/update', request_method: ['PUT']
57
+ # add '/tags/*tags', to: 'views/posts/tagged', name: :tagged
58
+ #
59
+ # add '/:i/is-an-integer', to: 'views/integer', matching: {i: /\d+/}
60
+ #
61
+ # add '/old-location', redirect: '/new-location'
62
+ # add '/images/*', static: 'images'
63
+ #
64
+ # For more information see the HttpRouter library
65
+ # (https://github.com/joshbuddy/http_router)
66
+ def add(path, options = {})
67
+ routes << {path: path, options: options}
68
+ end
69
+
70
+ # Convenience method setting the :request_method option to 'GET'.
71
+ def get(path, options = {})
72
+ add(path, options.merge(request_method: 'GET'))
73
+ end
74
+
75
+ # Convenience method setting the :request_method option to 'POST'.
76
+ def post(path, options = {})
77
+ add(path, options.merge(request_method: 'POST'))
78
+ end
79
+
80
+ # Convenience method setting the :request_method option to 'PUT'.
81
+ def put(path, options = {})
82
+ add(path, options.merge(request_method: 'PUT'))
83
+ end
84
+
85
+ # Convenience method setting the :request_method option to 'DELETE'.
86
+ def delete(path, options = {})
87
+ add(path, options.merge(request_method: 'DELETE'))
88
+ end
89
+
90
+ # Convenience method setting the :request_method option to 'HEAD'.
91
+ def head(path, options = {})
92
+ add(path, options.merge(request_method: 'HEAD'))
93
+ end
94
+
95
+ # Convenience method setting the :request_method option to 'OPTIONS'.
96
+ def options(path, options = {})
97
+ add(path, options.merge(request_method: 'OPTIONS'))
98
+ end
99
+
100
+ # Convenience method setting the :request_method option to 'TRACE'.
101
+ def trace(path, options = {})
102
+ add(path, options.merge(request_method: 'TRACE'))
103
+ end
104
+
105
+ # Tells the application's asset environment to serve and compile assets
106
+ # in the path argument without passing to the router. The path is
107
+ # relative to where Bruter::Application is subclassed.
108
+ def serve_assets(path)
109
+ asset_paths << path
110
+ end
111
+
112
+ end
113
+
114
+ attr_accessor :router, :assets
115
+
116
+ # Initializing a new Bruter::Application or a subclass of it will
117
+ # instantiate a new Router and Sprockets::Environment and subsequently add
118
+ # all routes to the router and asset paths to the asset environment.
119
+ def initialize
120
+ @router = Router.new
121
+ @assets = Sprockets::Environment.new
122
+ #
123
+ self.class.routes.each do |r|
124
+ path = r[:path]
125
+ options = r[:options]
126
+ to = options.delete(:to)
127
+ redirect = options.delete(:redirect)
128
+ static = options.delete(:static)
129
+ route = @router.add(path)
130
+ if to
131
+ route.to(to)
132
+ elsif redirect
133
+ route.redirect(redirect)
134
+ elsif static
135
+ route.static(self.class.absolute_path(static))
136
+ end
137
+ options.each { |k, v| route.send(k, v) if route.respond_to?(k) }
138
+ end
139
+ #
140
+ self.class.asset_paths.each do |path|
141
+ @assets.append_path(self.class.absolute_path(path))
142
+ end
143
+ end
144
+
145
+ # A call to this application will first check to see if the path is in any
146
+ # of the asset environment's paths. If so, the asset environment will
147
+ # attempt to serve and compile the asset; if not, action is passed to the
148
+ # router.
149
+ def call(env)
150
+ if @assets.find_asset(env['PATH_INFO'].gsub(/\A\//, ''))
151
+ @assets.call(env)
152
+ else
153
+ @router.call(env)
154
+ end
155
+ end
156
+
157
+ end
158
+ end
@@ -0,0 +1,82 @@
1
+ module Bruter
2
+ class Mustache < Mustache
3
+
4
+ class << self
5
+ attr_accessor :_directory
6
+
7
+ # Hack to set :_directory to the file location in which this class is
8
+ # subclassed. Those not subclassing Bruter::Mustache in a file may want
9
+ # to explicitly set :_directory or the @template_path variable with
10
+ # :template_path=(path).
11
+ def inherited(klass)
12
+ klass._directory = File.dirname(caller.first[/^[^:]+/])
13
+ end
14
+
15
+ # Sets the default template path to '../templates' relative to where the
16
+ # this class is subclassed.
17
+ #
18
+ # Example:
19
+ # # in '/path/to/app/views/tagged.rb'
20
+ # module Views
21
+ # class TaggedPosts < Bruter::Mustache
22
+ # puts self.template_path
23
+ # end
24
+ # end
25
+ #
26
+ # => '/path/to/app/templates'
27
+ def template_path
28
+ if _directory
29
+ @template_path ||= File.expand_path('../templates', _directory)
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ # Sets the default template name to an underscored version of the class
36
+ # name.
37
+ #
38
+ # Example:
39
+ # # in '/path/to/app/views/tagged.rb'
40
+ # module Views
41
+ # class TaggedPosts < Bruter::Mustache
42
+ # puts self.template_name
43
+ # puts self.template_file
44
+ # end
45
+ # end
46
+ #
47
+ # => 'tagged_posts'
48
+ # => '/path/to/app/templates/tagged_posts.mustache'
49
+ def template_name
50
+ @template_name ||= self.to_s.underscore.split('/').last
51
+ end
52
+ end
53
+
54
+ attr_reader :request, :response, :router
55
+
56
+ # Sets the Rack request and response objects and the calling router
57
+ # object. Subclasses can define a :setup method that will be invoked after
58
+ # initialization is complete.
59
+ def initialize(request, response, router)
60
+ @request = request
61
+ @response = response
62
+ @router = router
63
+ send :setup if respond_to?(:setup)
64
+ end
65
+
66
+ # Retrieves the Rack request's env.
67
+ def env
68
+ @request.env
69
+ end
70
+
71
+ # Retrieves the params from a name variable or globbed route.
72
+ def params
73
+ env['router.params']
74
+ end
75
+
76
+ # Retrieves the path for a named route given the name and any arguments.
77
+ def path(name, *args)
78
+ args.any? ? router.url(name, *args) : router.url(name)
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ module Bruter
2
+ class Router < HttpRouter
3
+
4
+ # Catches destinations that do not respond to :call(env) and transforms
5
+ # them into a constant, initializes them with Rack request and response
6
+ # objects and the router itself, and invokes :render. The Rack response's
7
+ # body will be set to the result of :render and the response finished.
8
+ def process_destination_path(path, env)
9
+ destination = path.route.dest
10
+ if destination.respond_to?(:call)
11
+ destination.call(env)
12
+ else
13
+ request = ::Rack::Request.new(env)
14
+ response = ::Rack::Response.new
15
+ view = destination.camelize.constantize
16
+ response.body = [view.new(request, response, self).render]
17
+ response.finish
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Bruter
2
+ VERSION = '0.0.2'
3
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bruter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Ted Kimble
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-28 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 29
29
+ segments:
30
+ - 1
31
+ - 3
32
+ - 3
33
+ version: 1.3.3
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: http_router
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ hash: 51
45
+ segments:
46
+ - 0
47
+ - 10
48
+ - 2
49
+ version: 0.10.2
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: sprockets
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - "="
59
+ - !ruby/object:Gem::Version
60
+ hash: 15
61
+ segments:
62
+ - 2
63
+ - 0
64
+ - 0
65
+ version: 2.0.0
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: activesupport
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - "="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 3
79
+ - 1
80
+ - 0
81
+ version: 3.1.0
82
+ type: :runtime
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ name: i18n
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ type: :runtime
97
+ version_requirements: *id005
98
+ - !ruby/object:Gem::Dependency
99
+ name: mustache
100
+ prerelease: false
101
+ requirement: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ~>
105
+ - !ruby/object:Gem::Version
106
+ hash: 411
107
+ segments:
108
+ - 0
109
+ - 99
110
+ - 4
111
+ version: 0.99.4
112
+ type: :runtime
113
+ version_requirements: *id006
114
+ description: Bruter is a miniature Ruby library that allows its bearer the ability to craft elegant Rack-based web applications with ease.
115
+ email: ted@kimble.co
116
+ executables: []
117
+
118
+ extensions: []
119
+
120
+ extra_rdoc_files: []
121
+
122
+ files:
123
+ - README.md
124
+ - LICENSE
125
+ - lib/bruter/application.rb
126
+ - lib/bruter/mustache.rb
127
+ - lib/bruter/router.rb
128
+ - lib/bruter/version.rb
129
+ - lib/bruter.rb
130
+ homepage: https://github.com/tedkimble/bruter
131
+ licenses: []
132
+
133
+ post_install_message:
134
+ rdoc_options: []
135
+
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 55
144
+ segments:
145
+ - 1
146
+ - 9
147
+ - 2
148
+ version: 1.9.2
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ requirements: []
159
+
160
+ rubyforge_project: bruter
161
+ rubygems_version: 1.8.6
162
+ signing_key:
163
+ specification_version: 3
164
+ summary: Bruter helps you craft elegant Ruby web applications.
165
+ test_files: []
166
+