darkhelmet-sinatra_more 0.3.33
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/IDEAS.md +54 -0
- data/LICENSE +20 -0
- data/README.rdoc +756 -0
- data/ROADMAP +88 -0
- data/Rakefile +63 -0
- data/TODO +60 -0
- data/VERSION +1 -0
- data/bin/sinatra_gen +6 -0
- data/generators/base_app/.gitignore +7 -0
- data/generators/base_app/Gemfile +13 -0
- data/generators/base_app/app/.empty_directory +0 -0
- data/generators/base_app/app/helpers/.empty_directory +0 -0
- data/generators/base_app/app/helpers/view_helpers.rb +3 -0
- data/generators/base_app/app/mailers/.empty_directory +0 -0
- data/generators/base_app/app/models/.empty_directory +0 -0
- data/generators/base_app/app/routes/.empty_directory +0 -0
- data/generators/base_app/app/views/.empty_directory +0 -0
- data/generators/base_app/config.ru.tt +10 -0
- data/generators/base_app/config/boot.rb.tt +42 -0
- data/generators/base_app/config/dependencies.rb.tt +41 -0
- data/generators/base_app/lib/.empty_directory +0 -0
- data/generators/base_app/public/images/.empty_directory +0 -0
- data/generators/base_app/public/images/.gitignore +0 -0
- data/generators/base_app/public/javascripts/.empty_directory +0 -0
- data/generators/base_app/public/stylesheets/.empty_directory +0 -0
- data/generators/base_app/test/models/.empty_directory +0 -0
- data/generators/base_app/test/routes/.empty_directory +0 -0
- data/generators/base_app/test/test_config.rb.tt +5 -0
- data/generators/base_app/vendor/gems/.empty_directory +0 -0
- data/generators/components/component_actions.rb +48 -0
- data/generators/components/mocks/mocha_mock_gen.rb +8 -0
- data/generators/components/mocks/rr_mock_gen.rb +8 -0
- data/generators/components/orms/activerecord_orm_gen.rb +99 -0
- data/generators/components/orms/couchrest_orm_gen.rb +64 -0
- data/generators/components/orms/datamapper_orm_gen.rb +52 -0
- data/generators/components/orms/mongomapper_orm_gen.rb +101 -0
- data/generators/components/orms/sequel_orm_gen.rb +61 -0
- data/generators/components/renderers/erb_renderer_gen.rb +7 -0
- data/generators/components/renderers/haml_renderer_gen.rb +22 -0
- data/generators/components/scripts/jquery_script_gen.rb +9 -0
- data/generators/components/scripts/prototype_script_gen.rb +10 -0
- data/generators/components/scripts/rightjs_script_gen.rb +10 -0
- data/generators/components/tests/bacon_test_gen.rb +21 -0
- data/generators/components/tests/riot_test_gen.rb +19 -0
- data/generators/components/tests/rspec_test_gen.rb +19 -0
- data/generators/components/tests/shoulda_test_gen.rb +19 -0
- data/generators/components/tests/testspec_test_gen.rb +19 -0
- data/generators/generator_actions.rb +79 -0
- data/generators/skeleton_generator.rb +53 -0
- data/lib/sinatra/mailer_plugin.rb +14 -0
- data/lib/sinatra/mailer_plugin/mail_object.rb +39 -0
- data/lib/sinatra/mailer_plugin/mailer_base.rb +75 -0
- data/lib/sinatra/markup_plugin.rb +19 -0
- data/lib/sinatra/markup_plugin/asset_tag_helpers.rb +109 -0
- data/lib/sinatra/markup_plugin/form_builder/abstract_form_builder.rb +133 -0
- data/lib/sinatra/markup_plugin/form_builder/standard_form_builder.rb +31 -0
- data/lib/sinatra/markup_plugin/form_helpers.rb +194 -0
- data/lib/sinatra/markup_plugin/format_helpers.rb +74 -0
- data/lib/sinatra/markup_plugin/output_helpers.rb +98 -0
- data/lib/sinatra/markup_plugin/tag_helpers.rb +42 -0
- data/lib/sinatra/render_plugin.rb +12 -0
- data/lib/sinatra/render_plugin/render_helpers.rb +63 -0
- data/lib/sinatra/routing_plugin.rb +50 -0
- data/lib/sinatra/routing_plugin/named_route.rb +27 -0
- data/lib/sinatra/routing_plugin/routing_helpers.rb +22 -0
- data/lib/sinatra/support_lite.rb +19 -0
- data/lib/sinatra/warden_plugin.rb +51 -0
- data/lib/sinatra/warden_plugin/warden_helpers.rb +60 -0
- data/lib/sinatra_more.rb +7 -0
- data/sinatra_more.gemspec +215 -0
- data/test/active_support_helpers.rb +7 -0
- data/test/fixtures/mailer_app/app.rb +51 -0
- data/test/fixtures/mailer_app/views/demo_mailer/sample_mail.erb +1 -0
- data/test/fixtures/mailer_app/views/sample_mailer/anniversary_message.erb +2 -0
- data/test/fixtures/mailer_app/views/sample_mailer/birthday_message.erb +2 -0
- data/test/fixtures/markup_app/app.rb +66 -0
- data/test/fixtures/markup_app/views/capture_concat.erb +14 -0
- data/test/fixtures/markup_app/views/capture_concat.haml +13 -0
- data/test/fixtures/markup_app/views/content_for.erb +11 -0
- data/test/fixtures/markup_app/views/content_for.haml +9 -0
- data/test/fixtures/markup_app/views/content_tag.erb +11 -0
- data/test/fixtures/markup_app/views/content_tag.haml +9 -0
- data/test/fixtures/markup_app/views/fields_for.erb +8 -0
- data/test/fixtures/markup_app/views/fields_for.haml +6 -0
- data/test/fixtures/markup_app/views/form_for.erb +56 -0
- data/test/fixtures/markup_app/views/form_for.haml +47 -0
- data/test/fixtures/markup_app/views/form_tag.erb +57 -0
- data/test/fixtures/markup_app/views/form_tag.haml +45 -0
- data/test/fixtures/markup_app/views/link_to.erb +5 -0
- data/test/fixtures/markup_app/views/link_to.haml +4 -0
- data/test/fixtures/markup_app/views/mail_to.erb +3 -0
- data/test/fixtures/markup_app/views/mail_to.haml +3 -0
- data/test/fixtures/markup_app/views/meta_tag.erb +3 -0
- data/test/fixtures/markup_app/views/meta_tag.haml +3 -0
- data/test/fixtures/render_app/app.rb +54 -0
- data/test/fixtures/render_app/views/erb/test.erb +1 -0
- data/test/fixtures/render_app/views/haml/test.haml +1 -0
- data/test/fixtures/render_app/views/template/_user.haml +7 -0
- data/test/fixtures/render_app/views/template/haml_template.haml +1 -0
- data/test/fixtures/render_app/views/template/some_template.haml +2 -0
- data/test/fixtures/routing_app/app.rb +48 -0
- data/test/fixtures/routing_app/views/index.haml +7 -0
- data/test/fixtures/warden_app/app.rb +75 -0
- data/test/fixtures/warden_app/views/dashboard.haml +6 -0
- data/test/generators/test_skeleton_generator.rb +190 -0
- data/test/helper.rb +73 -0
- data/test/mailer_plugin/test_mail_object.rb +24 -0
- data/test/mailer_plugin/test_mailer_base.rb +81 -0
- data/test/markup_plugin/test_asset_tag_helpers.rb +171 -0
- data/test/markup_plugin/test_form_builder.rb +627 -0
- data/test/markup_plugin/test_form_helpers.rb +417 -0
- data/test/markup_plugin/test_format_helpers.rb +96 -0
- data/test/markup_plugin/test_output_helpers.rb +63 -0
- data/test/markup_plugin/test_tag_helpers.rb +73 -0
- data/test/test_mailer_plugin.rb +33 -0
- data/test/test_render_plugin.rb +78 -0
- data/test/test_routing_plugin.rb +94 -0
- data/test/test_warden_plugin.rb +105 -0
- metadata +307 -0
data/.document
ADDED
data/.gitignore
ADDED
data/IDEAS.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# sinatra_more Ideas
|
2
|
+
|
3
|
+
before/after filters only for certain routes:
|
4
|
+
|
5
|
+
before :only => [:show, "/account"] do
|
6
|
+
# ... actions ...
|
7
|
+
end
|
8
|
+
|
9
|
+
after :only => [:show, "/account"] do
|
10
|
+
# ... actions ...
|
11
|
+
end
|
12
|
+
|
13
|
+
resource routing (defines mapped routes)
|
14
|
+
|
15
|
+
map(:user).resource
|
16
|
+
# => EQUIVALENT TO:
|
17
|
+
map(:new_user).to('/users/new') # NEW
|
18
|
+
map(:edit_user).to('/user/:id/edit') # EDIT
|
19
|
+
map(:user).to('/user/:id') # SHOW, UPDATE, DESTROY
|
20
|
+
map(:users).to('/users') # INDEX, CREATE
|
21
|
+
|
22
|
+
benchmarking and logging
|
23
|
+
|
24
|
+
create logger and Sinatra.logger methods
|
25
|
+
to make logging with severity easy
|
26
|
+
|
27
|
+
Sinatra.logger.info "Print something to log"
|
28
|
+
|
29
|
+
model generators (creates model, test, migrate)
|
30
|
+
|
31
|
+
sinatra_gen model Article
|
32
|
+
sinatra_gen --destroy model Article
|
33
|
+
|
34
|
+
migration generator (creates migration file)
|
35
|
+
|
36
|
+
sinatra_gen migration CreateArticleIndices
|
37
|
+
|
38
|
+
controller generators (routes, test, helpers)
|
39
|
+
|
40
|
+
sinatra_gen controller articles
|
41
|
+
sinatra_gen --destroy controller articles
|
42
|
+
|
43
|
+
mailer generators (mailer file, view path, test)
|
44
|
+
|
45
|
+
sinatra_gen mailer UserNotifier confirmation
|
46
|
+
|
47
|
+
task support (in generated app)
|
48
|
+
|
49
|
+
padrino console
|
50
|
+
padrino test
|
51
|
+
padrino db:create
|
52
|
+
padrino db:migrate
|
53
|
+
|
54
|
+
(Note in mailers, check out adv\_attr\_accessor)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Nathan Esquenazi
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,756 @@
|
|
1
|
+
= sinatra_more
|
2
|
+
|
3
|
+
== Preface
|
4
|
+
|
5
|
+
This gem has been designed to work with Sinatra (http://www.sinatrarb.com).
|
6
|
+
Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.
|
7
|
+
The canonical example of how to create an entire simple web application with Sinatra is something like:
|
8
|
+
|
9
|
+
# myapp.rb
|
10
|
+
require 'rubygems'
|
11
|
+
require 'sinatra'
|
12
|
+
get '/' do
|
13
|
+
'Hello world!'
|
14
|
+
end
|
15
|
+
|
16
|
+
and then to run the application:
|
17
|
+
|
18
|
+
$ ruby myapp.rb
|
19
|
+
|
20
|
+
The extreme simplicity of the framework is quite refreshing. I have been using Sinatra a great deal
|
21
|
+
for recent projects. First for small and simple json and xml web services and then even
|
22
|
+
for more complex full-featured applications. This gem represents my attempt to make it as fun and easy
|
23
|
+
as possible to code increasingly advanced view-heavy web applications in Sinatra.
|
24
|
+
|
25
|
+
== Introduction
|
26
|
+
|
27
|
+
Note: This library is still experimental and may not be ready for production just yet.
|
28
|
+
That being said the gem is being actively used on a number of sinatra projects.
|
29
|
+
In addition, the gem has fairly solid test coverage ensuring that everything works as expected.
|
30
|
+
|
31
|
+
This will be a plugin which expands sinatra's capabilities in a variety of ways.
|
32
|
+
Note that all extensions have been created to work with haml, erb, and erubis.
|
33
|
+
This gem is intended to be template-agnostic in providing helpers wherever possible.
|
34
|
+
|
35
|
+
Let me expand briefly on what I want to accomplish with this gem. I love sinatra but if I want to use it
|
36
|
+
for any non-trivial application I very quickly miss a lot of the extra tools provided by rails.
|
37
|
+
|
38
|
+
Now the obvious question is "Why not just use rails then?" Well, in many cases that might be the right decision.
|
39
|
+
Still, at least until version 3 comes along, Rails is quite a large framework with a 'take it or leave it' attitude.
|
40
|
+
Personally, I have come to love the spirit of sinatra which acts as a thin wrapper on top of rack
|
41
|
+
often allowing middleware to do most of the work and pulling in additional complexity only as required.
|
42
|
+
|
43
|
+
My goal with this extension is to match the spirit of Sinatra and at the same time create a standard library
|
44
|
+
of tools, helpers and components that will make Sinatra suitable for more complex applications.
|
45
|
+
|
46
|
+
Here is a small list of what sinatra_more contains:
|
47
|
+
|
48
|
+
* Code generators for creating new sinatra applications (using <tt>sinatra_gen</tt> on command line)
|
49
|
+
* Generic view and tag helpers (<tt>tag</tt>, <tt>content_tag</tt>, <tt>input_tag</tt>, ...)
|
50
|
+
* Asset tag helpers (<tt>link_to</tt>, <tt>image_tag</tt>, <tt>javascript_include_tag</tt>, ...)
|
51
|
+
* Full form helpers and builders support (<tt>form_tag</tt>, <tt>form_for</tt>, <tt>field_set_tag</tt>, <tt>text_field</tt>, ...)
|
52
|
+
* Full url named route support to avoid hardcoding url paths in sinatra (<tt>map</tt>, <tt>url_for</tt>)
|
53
|
+
* Generally useful formatting extensions (<tt>relative_time_ago</tt>, <tt>js_escape_html</tt>, <tt>sanitize_html</tt>)
|
54
|
+
* Simple 'mailer' support for sinatra (akin to <tt>ActionMailer</tt> but simpler and powered by <tt>pony</tt>)
|
55
|
+
* Plug and play setup for the excellent Warden authentication system
|
56
|
+
|
57
|
+
Keep in mind, the user will be able to pull in these components seperately and leave out those that are not required.
|
58
|
+
|
59
|
+
Please help me brainstorm and fork the project if you have any ideas to contribute.
|
60
|
+
|
61
|
+
== Installation
|
62
|
+
|
63
|
+
If you want to use the WardenPlugin component, then the 'warden' gem would need to be installed.
|
64
|
+
|
65
|
+
To install sinatra_more, simply grab the latest version from gemcutter:
|
66
|
+
|
67
|
+
$ sudo gem install sinatra_more --source http://gemcutter.org
|
68
|
+
|
69
|
+
Now you are ready to use this gem in your sinatra project.
|
70
|
+
|
71
|
+
== Usage
|
72
|
+
|
73
|
+
This extension can be easily registered into any existing sinatra application. You can require
|
74
|
+
different components based on which pieces are useful for your particular application.
|
75
|
+
|
76
|
+
# app.rb
|
77
|
+
require 'sinatra/base'
|
78
|
+
require 'sinatra_more' # or require 'sinatra_more/markup_plugin' for precise inclusion
|
79
|
+
|
80
|
+
class Application < Sinatra::Base
|
81
|
+
register Sinatra::MarkupPlugin
|
82
|
+
register Sinatra::RenderPlugin
|
83
|
+
register Sinatra::WardenPlugin
|
84
|
+
register Sinatra::MailerPlugin
|
85
|
+
register Sinatra::RoutingPlugin
|
86
|
+
end
|
87
|
+
|
88
|
+
This will then allow you to use the components that have been registered. A breakdown of components is below:
|
89
|
+
|
90
|
+
=== MarkupPlugin
|
91
|
+
|
92
|
+
This component provides a great deal of view helpers related to html markup generation.
|
93
|
+
There are helpers for generating tags, forms, links, images, and more. Most of the basic
|
94
|
+
methods should be very familiar to anyone who has used rails view helpers.
|
95
|
+
|
96
|
+
==== Output Helpers
|
97
|
+
|
98
|
+
* <tt>content_for(key, &block)</tt>
|
99
|
+
* Capture a block of content to be rendered at a later time.
|
100
|
+
* <tt>content_for(:head) { ...content... }</tt>
|
101
|
+
* Also supports arguments passed to the content block
|
102
|
+
* <tt>content_for(:head) { |param1, param2| ...content... }</tt>
|
103
|
+
* <tt>yield_content(key, *args)</tt>
|
104
|
+
* Render the captured content blocks for a given key.
|
105
|
+
* <tt>yield_content :head</tt>
|
106
|
+
* Also supports arguments yielded to the content block
|
107
|
+
* <tt>yield_content :head, param1, param2</tt>
|
108
|
+
* <tt>capture_html(*args, &block)</tt>
|
109
|
+
* Captures the html from a block of template code for erb or haml
|
110
|
+
* <tt>capture_html(&block)</tt> => "...html..."
|
111
|
+
* <tt>concat_content(text="")</tt>
|
112
|
+
* Outputs the given text to the templates buffer directly in erb or haml
|
113
|
+
* <tt>concat_content("This will be output to the template buffer in erb or haml")</tt>
|
114
|
+
|
115
|
+
==== Tag Helpers
|
116
|
+
|
117
|
+
* <tt>tag(name, options={})</tt>
|
118
|
+
* Creates an html tag with the given name and options
|
119
|
+
* <tt>tag(:br, :style => 'clear:both')</tt> => <br style="clear:both" />
|
120
|
+
* <tt>tag(:p, :content => "demo", :class => 'large')</tt> => <p class="large">demo</p>
|
121
|
+
* <tt>content_tag(name, content, options={})</tt>
|
122
|
+
* Creates an html tag with given name, content and options
|
123
|
+
* <tt>content_tag(:p, "demo", :class => 'light')</tt> => <p class="light">demo</p>
|
124
|
+
* <tt>content_tag(:p, :class => 'dark') { ...content... }</tt> => <p class="dark">...content...</p>
|
125
|
+
* <tt>input_tag(type, options = {})</tt>
|
126
|
+
* Creates an html input field with given type and options
|
127
|
+
* <tt>input_tag :text, :class => "demo"</tt>
|
128
|
+
* <tt>input_tag :password, :value => "secret", :class => "demo"</tt>
|
129
|
+
|
130
|
+
==== Asset Helpers
|
131
|
+
|
132
|
+
* <tt>flash_tag(kind, options={})</tt>
|
133
|
+
* Creates a div to display the flash of given type if it exists
|
134
|
+
* <tt>flash_tag(:notice, :class => 'flash', :id => 'flash-notice')</tt>
|
135
|
+
* <tt>link_to(*args, &block)</tt>
|
136
|
+
* Creates a link element with given name, url and options
|
137
|
+
* <tt>link_to 'click me', '/dashboard', :class => 'linky'</tt>
|
138
|
+
* <tt>link_to('/dashboard', :class => 'blocky') { ...content... }</tt>
|
139
|
+
* <tt>mail_to(email, caption=nil, mail_options={})</tt>
|
140
|
+
* Creates a mailto link tag to the specified email_address
|
141
|
+
* <tt>mail_to "me@demo.com"</tt>
|
142
|
+
* <tt>mail_to "me@demo.com", "My Email", :subject => "Feedback", :cc => 'test@demo.com'</tt>
|
143
|
+
* <tt>image_tag(url, options={})</tt>
|
144
|
+
* Creates an image element with given url and options
|
145
|
+
* <tt>image_tag('icons/avatar.png')</tt>
|
146
|
+
* <tt>stylesheet_link_tag(*sources)</tt>
|
147
|
+
* Returns a stylesheet link tag for the sources specified as arguments
|
148
|
+
* <tt>stylesheet_link_tag 'style', 'application', 'layout'</tt>
|
149
|
+
* <tt>javascript_include_tag(*sources)</tt>
|
150
|
+
* Returns an html script tag for each of the sources provided.
|
151
|
+
* <tt>javascript_include_tag 'application', 'special'</tt>
|
152
|
+
|
153
|
+
==== Form Helpers
|
154
|
+
|
155
|
+
* <tt>form_tag(url, options={}, &block)</tt>
|
156
|
+
* Constructs a form without object based on options
|
157
|
+
* Supports form methods 'put' and 'delete' through hidden field
|
158
|
+
* <tt>form_tag('/register', :class => 'example') { ... }</tt>
|
159
|
+
* <tt>field_set_tag(*args, &block)</tt>
|
160
|
+
* Constructs a field_set to group fields with given options
|
161
|
+
* <tt>field_set_tag(:class => 'office-set') { }</tt>
|
162
|
+
* <tt>field_set_tag("Office", :class => 'office-set') { }</tt>
|
163
|
+
* <tt>error_messages_for(record, options={})</tt>
|
164
|
+
* Constructs list html for the errors for a given object
|
165
|
+
* <tt>error_messages_for @user</tt>
|
166
|
+
* <tt>label_tag(name, options={}, &block)</tt>
|
167
|
+
* Constructs a label tag from the given options
|
168
|
+
* <tt>label_tag :username, :class => 'long-label'</tt>
|
169
|
+
* <tt>label_tag(:username, :class => 'blocked-label') { ... }</tt>
|
170
|
+
* <tt>hidden_field_tag(name, options={})</tt>
|
171
|
+
* Constructs a hidden field input from the given options
|
172
|
+
* <tt>hidden_field_tag :session_key, :value => 'secret'</tt>
|
173
|
+
* <tt>text_field_tag(name, options={})</tt>
|
174
|
+
* Constructs a text field input from the given options
|
175
|
+
* <tt>text_field_tag :username, :class => 'long'</tt>
|
176
|
+
* <tt>text_area_tag(name, options={})</tt>
|
177
|
+
* Constructs a text area input from the given options
|
178
|
+
* <tt>text_area_tag :username, :class => 'long'</tt>
|
179
|
+
* <tt>password_field_tag(name, options={})</tt>
|
180
|
+
* Constructs a password field input from the given options
|
181
|
+
* <tt>password_field_tag :password, :class => 'long'</tt>
|
182
|
+
* <tt>check_box_tag(name, options={})</tt>
|
183
|
+
* Constructs a checkbox input from the given options
|
184
|
+
* <tt>check_box_tag :remember_me, :checked => true</tt>
|
185
|
+
* <tt>radio_button_tag(name, options={})</tt>
|
186
|
+
* Constructs a radio button input from the given options
|
187
|
+
* <tt>radio_button_tag :gender, :value => 'male'</tt>
|
188
|
+
* <tt>select_tag(name, settings={})</tt>
|
189
|
+
* Constructs a select tag with options from the given settings
|
190
|
+
* <tt>select_tag(:favorite_color, :options => ['1', '2', '3'], :selected => '1')</tt>
|
191
|
+
* <tt>select_tag(:more_color, :options => [['label', '1'], ['label2', '2']])</tt>
|
192
|
+
* <tt>select_tag(:multiple_color, :options => [...], :multiple => true)</tt>
|
193
|
+
* <tt>file_field_tag(name, options={})</tt>
|
194
|
+
* Constructs a file field input from the given options
|
195
|
+
* <tt>file_field_tag :photo, :class => 'long'</tt>
|
196
|
+
* <tt>submit_tag(caption, options={})</tt>
|
197
|
+
* Constructs a submit button from the given options
|
198
|
+
* <tt>submit_tag "Create", :class => 'success'</tt>
|
199
|
+
* <tt>button_tag(caption, options={})</tt>
|
200
|
+
* Constructs an input (type => 'button') from the given options
|
201
|
+
* <tt>button_tag "Cancel", :class => 'clear'</tt>
|
202
|
+
* <tt>image_submit_tag(source, options={})</tt>
|
203
|
+
* Constructs an image submit button from the given options
|
204
|
+
* <tt>image_submit_tag "submit.png", :class => 'success'</tt>
|
205
|
+
|
206
|
+
A form_tag might look like:
|
207
|
+
|
208
|
+
- form_tag '/destroy', :class => 'destroy-form', :method => 'delete' do
|
209
|
+
= flash_tag(:notice)
|
210
|
+
- field_set_tag do
|
211
|
+
%p
|
212
|
+
= label_tag :username, :class => 'first'
|
213
|
+
= text_field_tag :username, :value => params[:username]
|
214
|
+
%p
|
215
|
+
= label_tag :password, :class => 'first'
|
216
|
+
= password_field_tag :password, :value => params[:password]
|
217
|
+
%p
|
218
|
+
= label_tag :strategy
|
219
|
+
= select_tag :strategy, :options => ['delete', 'destroy'], :selected => 'delete'
|
220
|
+
%p
|
221
|
+
= check_box_tag :confirm_delete
|
222
|
+
- field_set_tag(:class => 'buttons') do
|
223
|
+
= submit_tag "Remove"
|
224
|
+
|
225
|
+
==== FormBuilders
|
226
|
+
|
227
|
+
* <tt>form_for(object, url, settings={}, &block)</tt>
|
228
|
+
* Constructs a form using given or default form_builder
|
229
|
+
* Supports form methods 'put' and 'delete' through hidden field
|
230
|
+
* Defaults to StandardFormBuilder but you can easily create your own!
|
231
|
+
* <tt>form_for(@user, '/register', :id => 'register') { |f| ...field-elements... }</tt>
|
232
|
+
* <tt>form_for(:user, '/register', :id => 'register') { |f| ...field-elements... }</tt>
|
233
|
+
* <tt>fields_for(object, settings={}, &block)</tt>
|
234
|
+
* Constructs fields for a given object for use in an existing form
|
235
|
+
* Defaults to StandardFormBuilder but you can easily create your own!
|
236
|
+
* <tt>fields_for @user.assignment do |assignment| ... end</tt>
|
237
|
+
* <tt>fields_for :assignment do |assigment| ... end</tt>
|
238
|
+
|
239
|
+
The following are fields provided by AbstractFormBuilder that can be used within a form_for or fields_for:
|
240
|
+
|
241
|
+
* <tt>error_messages(options={})</tt>
|
242
|
+
* Displays list html for the errors on form object
|
243
|
+
* <tt>f.errors_messages</tt>
|
244
|
+
* <tt>label(field, options={})</tt>
|
245
|
+
* <tt>f.label :name, :class => 'long'</tt>
|
246
|
+
* <tt>text_field(field, options={})</tt>
|
247
|
+
* <tt>f.text_field :username, :class => 'long'</tt>
|
248
|
+
* <tt>check_box(field, options={})</tt>
|
249
|
+
* Uses hidden field to provide a 'unchecked' value for field
|
250
|
+
* <tt>f.check_box :remember_me, :uncheck_value => 'false'</tt>
|
251
|
+
* <tt>radio_button(field, options={})</tt>
|
252
|
+
* <tt>f.radio_button :gender, :value => 'male'</tt>
|
253
|
+
* <tt>hidden_field(field, options={})</tt>
|
254
|
+
* <tt>f.hidden_field :session_id, :class => 'hidden'</tt>
|
255
|
+
* <tt>text_area(field, options={})</tt>
|
256
|
+
* <tt>f.text_area :summary, :class => 'long'</tt>
|
257
|
+
* <tt>password_field(field, options={})</tt>
|
258
|
+
* <tt>f.password_field :secret, :class => 'long'</tt>
|
259
|
+
* <tt>file_field(field, options={})</tt>
|
260
|
+
* <tt>f.file_field :photo, :class => 'long'</tt>
|
261
|
+
* <tt>select(field, options={})</tt>
|
262
|
+
* <tt>f.select(:state, :options => ['California', 'Texas', 'Wyoming'])</tt>
|
263
|
+
* <tt>f.select(:state, :collection => @states, :fields => [:name, :id])</tt>
|
264
|
+
* <tt>f.select(:state, :options => [...], :include_blank => true)</tt>
|
265
|
+
* <tt>submit(caption, options={})</tt>
|
266
|
+
* <tt>f.submit "Update", :class => 'long'</tt>
|
267
|
+
* <tt>image_submit(source, options={})</tt>
|
268
|
+
* <tt>f.image_submit "submit.png", :class => 'long'</tt>
|
269
|
+
|
270
|
+
A form_for using these basic fields might look like:
|
271
|
+
|
272
|
+
- form_for @user, '/register', :id => 'register' do |f|
|
273
|
+
= f.error_messages
|
274
|
+
%p
|
275
|
+
= f.label :username, :caption => "Nickname"
|
276
|
+
= f.text_field :username
|
277
|
+
%p
|
278
|
+
= f.label :email
|
279
|
+
= f.text_field :email
|
280
|
+
%p
|
281
|
+
= f.label :password
|
282
|
+
= f.password_field :password
|
283
|
+
%p
|
284
|
+
= f.label :is_admin, :caption => "Admin User?"
|
285
|
+
= f.check_box :is_admin
|
286
|
+
%p
|
287
|
+
= f.label :color, :caption => "Favorite Color?"
|
288
|
+
= f.select :color, :options => ['red', 'black']
|
289
|
+
%p
|
290
|
+
- fields_for @user.location do |location|
|
291
|
+
= location.text_field :street
|
292
|
+
= location.text_field :city
|
293
|
+
%p
|
294
|
+
= f.submit "Create", :class => 'button'
|
295
|
+
|
296
|
+
There is also a StandardFormBuilder which builds on the abstract fields that can be used within a form_for:
|
297
|
+
|
298
|
+
* <tt>text_field_block(field, options={}, label_options={})</tt>
|
299
|
+
* <tt>text_field_block(:nickname, :class => 'big', :caption => "Username")</tt>
|
300
|
+
* <tt>text_area_block(field, options={}, label_options={})</tt>
|
301
|
+
* <tt>text_area_block(:about, :class => 'big')</tt>
|
302
|
+
* <tt>password_field_block(field, options={}, label_options={})</tt>
|
303
|
+
* <tt>password_field_block(:code, :class => 'big')</tt>
|
304
|
+
* <tt>file_field_block(field, options={}, label_options={})</tt>
|
305
|
+
* <tt>file_field_block(:photo, :class => 'big')</tt>
|
306
|
+
* <tt>check_box_block(field, options={}, label_options={})</tt>
|
307
|
+
* <tt>check_box_block(:remember_me, :class => 'big')</tt>
|
308
|
+
* <tt>select_block(field, options={}, label_options={})</tt>
|
309
|
+
* <tt>select_block(:country, :option => ['USA', 'Canada'])</tt>
|
310
|
+
* <tt>submit_block(caption, options={})</tt>
|
311
|
+
* <tt>submit_block(:username, :class => 'big')</tt>
|
312
|
+
* <tt>image_submit_block(source, options={})</tt>
|
313
|
+
* <tt>image_submit_block('submit.png', :class => 'big')</tt>
|
314
|
+
|
315
|
+
A form_for using these standard fields might look like:
|
316
|
+
|
317
|
+
- form_for @user, '/register', :id => 'register' do |f|
|
318
|
+
= f.error_messages
|
319
|
+
= f.text_field_block :name, :caption => "Full name"
|
320
|
+
= f.text_field_block :email
|
321
|
+
= f.check_box_block :remember_me
|
322
|
+
= f.select_block :fav_color, :options => ['red', 'blue']
|
323
|
+
= f.password_field_block :password
|
324
|
+
= f.submit_block "Create", :class => 'button'
|
325
|
+
|
326
|
+
and would generate this html:
|
327
|
+
|
328
|
+
<form id="register" action="/register" method="post">
|
329
|
+
<p><label for="user_name">Full name: </label><input type="text" id="user_name" name="user[name]"></p>
|
330
|
+
...omitted...
|
331
|
+
<p><input type="submit" value="Create" class="button"></p>
|
332
|
+
</form>
|
333
|
+
|
334
|
+
You can also easily build your own FormBuilder which allows for customized fields:
|
335
|
+
|
336
|
+
class MyCustomFormBuilder < AbstractFormBuilder
|
337
|
+
# Here we have access to a number of useful variables
|
338
|
+
#
|
339
|
+
# * template (use this to invoke any helpers)(ex. template.hidden_field_tag(...))
|
340
|
+
# * object (the record for this form) (ex. object.valid?)
|
341
|
+
# * object_name (object's underscored type) (ex. object_name => 'admin_user')
|
342
|
+
#
|
343
|
+
# We also have access to self.field_types => [:text_field, :text_area, ...]
|
344
|
+
# In addition, we have access to all the existing field tag helpers (text_field, hidden_field, file_field, ...)
|
345
|
+
end
|
346
|
+
|
347
|
+
Once a custom builder is defined, any call to form_for can use the new builder:
|
348
|
+
|
349
|
+
- form_for @user, '/register', :builder => 'MyCustomFormBuilder', :id => 'register' do |f|
|
350
|
+
...fields here...
|
351
|
+
|
352
|
+
The form builder can even be made into the default builder when form_for is invoked:
|
353
|
+
|
354
|
+
# anywhere in the sinatra application
|
355
|
+
set :default_builder, 'MyCustomFormBuilder'
|
356
|
+
|
357
|
+
And there you have it, a fairly complete form builder solution for sinatra.
|
358
|
+
I hope to create or merge in an even better 'default' form_builder in the near future.
|
359
|
+
|
360
|
+
==== Format Helpers
|
361
|
+
|
362
|
+
* <tt>escape_html</tt> (alias <tt>h</tt> and <tt>h!</tt>)
|
363
|
+
* (from RackUtils) Escape ampersands, brackets and quotes to their HTML/XML entities.
|
364
|
+
* <tt>relative_time_ago(date)</tt>
|
365
|
+
* Returns relative time in words referencing the given date
|
366
|
+
* <tt>relative_time_ago(2.days.ago)</tt> => "2 days"
|
367
|
+
* <tt>relative_time_ago(5.minutes.ago)</tt> => "5 minutes"
|
368
|
+
* <tt>relative_time_ago(2800.days.ago)</tt> => "over 7 years"
|
369
|
+
* <tt>time_in_words(date)</tt>
|
370
|
+
* Returns relative time in the past or future using appropriate date format
|
371
|
+
* <tt>time_in_words(2.days.ago)</tt> => "2 days ago"
|
372
|
+
* <tt>time_in_words(100.days.ago)</tt> => "Tuesday, July 21"
|
373
|
+
* <tt>time_in_words(1.day.from_now)</tt> => "tomorrow"
|
374
|
+
* <tt>escape_javascript(html_content)</tt>
|
375
|
+
* Escapes html to allow passing information to javascript. Used for passing data inside an ajax .js.erb template
|
376
|
+
* <tt>escape_javascript("<h1>Hey</h1>")</tt>
|
377
|
+
|
378
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/markupplugin>
|
379
|
+
|
380
|
+
=== RenderPlugin
|
381
|
+
|
382
|
+
This component provides a number of rendering helpers for sinatra, making the process
|
383
|
+
of displaying templates far smoother. This plugin also has support for useful additions
|
384
|
+
such as partials (with support for :collection) into the templating system.
|
385
|
+
|
386
|
+
* <tt>erb_template(template_path, options={})</tt>
|
387
|
+
* Renders a erb template based on the given path
|
388
|
+
* <tt>erb_template 'users/new'</tt>
|
389
|
+
* <tt>haml_template(template_path, options={})</tt>
|
390
|
+
* Renders a haml template based on the given path
|
391
|
+
* <tt>haml_template 'users/new'</tt>
|
392
|
+
* <tt>render_template(template_path, options={})</tt>
|
393
|
+
* Renders the first detected template based on the given path
|
394
|
+
* <tt>render_template 'users/new'</tt>
|
395
|
+
* <tt>render_template 'users/index', :template_engine => 'haml'</tt>
|
396
|
+
* <tt>partial(template, *args)</tt>
|
397
|
+
* Renders the html related to the partial template for object or collection
|
398
|
+
* <tt>partial 'photo/_item', :object => @photo, :locals => { :foo => 'bar' }</tt>
|
399
|
+
* <tt>partial 'photo/_item', :collection => @photos</tt>
|
400
|
+
|
401
|
+
Using render plugin helpers is extremely simple. If you want to render an erb template in your view path:
|
402
|
+
|
403
|
+
erb_template 'path/to/my/template'
|
404
|
+
|
405
|
+
or using haml templates works just as well:
|
406
|
+
|
407
|
+
haml_template 'path/to/haml/template'
|
408
|
+
|
409
|
+
There is also a method which renders the first view matching the path and removes the need to define an engine:
|
410
|
+
|
411
|
+
render_template 'path/to/any/template'
|
412
|
+
|
413
|
+
It is worth noting these are mostly for convenience. When I had more complex view files in sinatra, I got tired of writing:
|
414
|
+
|
415
|
+
haml :"the/path/to/file"
|
416
|
+
erb "/path/to/file".to_sym
|
417
|
+
|
418
|
+
Finally, we have the all-important partials support for rendering mini-templates onto a page:
|
419
|
+
|
420
|
+
partial 'photo/_item', :object => @photo, :locals => { :foo => 'bar' }
|
421
|
+
partial 'photo/_item', :collection => @photos
|
422
|
+
|
423
|
+
This works as you would expect and also supports the collection counter inside the partial <tt>item_counter</tt>
|
424
|
+
|
425
|
+
# /views/photo/_item.haml
|
426
|
+
# Access to collection counter with <partial_name>_counter i.e item_counter
|
427
|
+
# Access the object with the partial_name i.e item
|
428
|
+
|
429
|
+
And that concludes the render plugin for now but I am open to adding more methods as time progresses.
|
430
|
+
|
431
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/renderplugin>
|
432
|
+
|
433
|
+
=== WardenPlugin
|
434
|
+
|
435
|
+
This component provides out-of-the-box support for Warden authentication. With this
|
436
|
+
plugin registered, warden will be automatically required, configured and helpers will be
|
437
|
+
provided to make interacting with warden dead simple.
|
438
|
+
|
439
|
+
* <tt>current_user</tt>
|
440
|
+
* Returns the current authenticated user
|
441
|
+
* <tt>authenticate_user!</tt>
|
442
|
+
* Login the user through the default warden strategies
|
443
|
+
* <tt>logout_user!</tt>
|
444
|
+
* Signs out the user from the session
|
445
|
+
* <tt>logged_in?</tt>
|
446
|
+
* Returns true if the user has been authenticated
|
447
|
+
* <tt>authenticated?(&block)</tt>
|
448
|
+
* If a block is given, only yields the content if the user is authenticated
|
449
|
+
* <tt>authenticated? { puts "I am authenticated!" }</tt>
|
450
|
+
* <tt>must_be_authorized!(failure_path=nil)</tt>
|
451
|
+
* Forces a user to return to a fail path unless they are authorized
|
452
|
+
* Used to require a user be authenticated before routing to an action
|
453
|
+
* <tt>must_be_authorized!('/login')</tt>
|
454
|
+
|
455
|
+
There are a few configuration options and details you need to be aware of. By default, the WardenPlugin
|
456
|
+
assumes you have a User class which represents the authenticating class type. If your user class has a different
|
457
|
+
name then you need to specify that as follows:
|
458
|
+
|
459
|
+
Sinatra::WardenPlugin::PasswordStrategy.user_class = CustomUser
|
460
|
+
|
461
|
+
In addition, the strategy used expects that you have an <tt>authenticate</tt> method with the specific signature below:
|
462
|
+
|
463
|
+
class CustomUser
|
464
|
+
# ...
|
465
|
+
# Returns user record if user and password match; otherwise return false
|
466
|
+
def authenticate(username, password)
|
467
|
+
user = User.find(username)
|
468
|
+
user.has_password?(password) ? user : false
|
469
|
+
end
|
470
|
+
# ...
|
471
|
+
end
|
472
|
+
|
473
|
+
Using this plugin you also do need to define your own routes for managing warden sessions. An example is below:
|
474
|
+
|
475
|
+
get '/login/?' do
|
476
|
+
haml_template 'session/login'
|
477
|
+
end
|
478
|
+
|
479
|
+
post '/login/?' do
|
480
|
+
authenticate_user!
|
481
|
+
redirect "/dashboard"
|
482
|
+
end
|
483
|
+
|
484
|
+
post '/unauthenticated/?' do
|
485
|
+
flash[:notice] = "That username and password are not correct!"
|
486
|
+
status 401
|
487
|
+
haml_template 'session/login'
|
488
|
+
end
|
489
|
+
|
490
|
+
get '/logout/?' do
|
491
|
+
logout_user!
|
492
|
+
redirect '/session/login'
|
493
|
+
end
|
494
|
+
|
495
|
+
I was made aware of other sinatra/warden plugins which work very similarly to the system I outline for this plugin.
|
496
|
+
Most notably is the sinatra_warden plugin by Justin Smestad (http://github.com/jsmestad/sinatra_warden)
|
497
|
+
Eventually I plan to vendor that gem or just remove support for this piece of my plugin completely.
|
498
|
+
|
499
|
+
If anybody has any thoughts on this or the warden integration in general, please let me know.
|
500
|
+
Nonetheless, there is a certain convenience for me having access to a plug-and-play warden solution directly from this gem.
|
501
|
+
|
502
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/wardenplugin>
|
503
|
+
|
504
|
+
=== MailerPlugin
|
505
|
+
|
506
|
+
This component uses an enhanced version of the excellent <tt>pony</tt> library (vendored) for a powerful but simple
|
507
|
+
mailer system within Sinatra. There is full support for using an html content type as well as for file attachments.
|
508
|
+
The MailerPlugin has many similarities to ActionMailer but is much lighterweight and (arguably) easier to use.
|
509
|
+
|
510
|
+
Let's take a look at using the MailerPlugin in an application. By default, MailerPlugin uses the built-in sendmail
|
511
|
+
functionality on the server. However, smtp is also supported using the following configuration:
|
512
|
+
|
513
|
+
Sinatra::MailerBase.smtp_settings = {
|
514
|
+
:host => 'smtp.gmail.com',
|
515
|
+
:port => '587',
|
516
|
+
:tls => true,
|
517
|
+
:user => 'user',
|
518
|
+
:pass => 'pass',
|
519
|
+
:auth => :plain
|
520
|
+
}
|
521
|
+
|
522
|
+
Once those have been defined, the default will become smtp delivery unless overwritten in an individual mail definition.
|
523
|
+
Next, we should define a custom mailer extended from <tt>Sinatra::MailerBase</tt>.
|
524
|
+
|
525
|
+
# app/mailers/sample_mailer.rb
|
526
|
+
class SampleMailer < Sinatra::MailerBase
|
527
|
+
def registration_email(name, user_email_address)
|
528
|
+
from 'admin@site.com'
|
529
|
+
to user_email_address
|
530
|
+
subject 'Welcome to the site!'
|
531
|
+
body :name => name
|
532
|
+
type 'html' # optional, defaults to plain/text
|
533
|
+
charset 'windows-1252' # optional, defaults to utf-8
|
534
|
+
via :sendmail # optional, to smtp if defined otherwise sendmail
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
This defines a mail called '<tt>registration_mail</tt>' with the specified attributes for delivery. The <tt>body</tt> method
|
539
|
+
is passing the <tt>name</tt> attribute to the body message template which should be defined in
|
540
|
+
<tt>[views_path]/sample_mailer/registration_email.erb</tt> as shown below:
|
541
|
+
|
542
|
+
# ./views/sample_mailer/registration_email.erb
|
543
|
+
This is the body of the email and can access the <%= name %> that was passed in from the mailer definition
|
544
|
+
That's all there is to defining the body of the email which can be plain text or html
|
545
|
+
|
546
|
+
Once the mailer definition has been completed and the template has been defined, the email can be sent using:
|
547
|
+
|
548
|
+
SampleMailer.deliver(:registration_email, "Bob", "bob@bobby.com")
|
549
|
+
|
550
|
+
or if you like the method_missing approach:
|
551
|
+
|
552
|
+
SampleMailer.deliver_registration_email "Bob", 'bob@bobby.com'
|
553
|
+
|
554
|
+
And that will then deliver the email according the the configured options. This is really all you need to send emails.
|
555
|
+
A few variations are shown below for completeness.
|
556
|
+
|
557
|
+
If we want to attach files to our email:
|
558
|
+
|
559
|
+
# app/mailers/sample_mailer.rb
|
560
|
+
class SampleMailer < Sinatra::MailerBase
|
561
|
+
def attachment_email(name, user_email_address)
|
562
|
+
from 'admin@site.com'
|
563
|
+
to user_email_address
|
564
|
+
# ...
|
565
|
+
attachments { "foo.zip" => File.read("path/to/foo.zip"), "file.txt" => "this is a text file!" }
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
or perhaps we want to have a short body without the need for a template file:
|
570
|
+
|
571
|
+
# app/mailers/sample_mailer.rb
|
572
|
+
class SampleMailer < Sinatra::MailerBase
|
573
|
+
def short_email(name, user_email_address)
|
574
|
+
from 'admin@site.com'
|
575
|
+
to user_email_address
|
576
|
+
subject 'Welcome to the site!'
|
577
|
+
body "This is a short body defined right in the mailer itself"
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/mailerplugin>
|
582
|
+
|
583
|
+
=== RoutingPlugin
|
584
|
+
|
585
|
+
This component provides Sinatra with an enhanced url routing system which enables named route aliases to be defined
|
586
|
+
and used throughout your application to refer to urls. The benefits of this is that instead of having to hard-code route urls
|
587
|
+
into every area of your application, now we can just define the urls in a single spot and then attach an alias
|
588
|
+
which can be used to refer to the url throughout the rest.
|
589
|
+
|
590
|
+
Let's take a look at how to define named route mappings:
|
591
|
+
|
592
|
+
# /app/routes/example.rb
|
593
|
+
require 'sinatra_more'
|
594
|
+
|
595
|
+
class RoutingDemo < Sinatra::Application
|
596
|
+
register Sinatra::RoutingPlugin
|
597
|
+
|
598
|
+
# Define the named route mappings
|
599
|
+
map(:account).to("/the/accounts/:name/and/:id")
|
600
|
+
map(:accounts).to("/the/accounts/index")
|
601
|
+
|
602
|
+
# Configure the routes using the named alias
|
603
|
+
get(:account) { "name: params[:name] - id: params[:id]" }
|
604
|
+
get(:accounts) { "I am the body for the url /the/accounts/index" }
|
605
|
+
end
|
606
|
+
|
607
|
+
Notice we simply create a route alias using the <tt>map</tt> function and then pass in the corresponding url into the <tt>to</tt> method.
|
608
|
+
You can then define the routes using the named symbol representing the url. The route aliases can be accessed using <tt>url_for</tt>
|
609
|
+
|
610
|
+
url_for(:accounts)
|
611
|
+
url_for(:account, :id => 1, :name => 'first')
|
612
|
+
|
613
|
+
You can also refer to the url in views using <tt>url_for</tt>
|
614
|
+
|
615
|
+
# /app/views/index.erb
|
616
|
+
<p>Go to the <%= link_to 'accounts dashboard', url_for(:accounts) %> to view your accounts</p>
|
617
|
+
<p>Go to account for <%= link_to 'first account', url_for(:account, :id => 1, :name => 'first') %>
|
618
|
+
|
619
|
+
Simply invoking <tt>url_for(name, *parameters)</tt> will return the full mapped url for use in links or anywhere else
|
620
|
+
that the url might be required.
|
621
|
+
|
622
|
+
The routing system also supports url route configuration namespaces:
|
623
|
+
|
624
|
+
# /app/routes/example.rb
|
625
|
+
map(:admin, :show).to("/admin/:id/show")
|
626
|
+
|
627
|
+
namespace :admin do
|
628
|
+
get :show do
|
629
|
+
"admin show for #{params[:id]}"
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
You could also define the route aliases themselves using a namespace for convenience:
|
634
|
+
|
635
|
+
# /app/routes/example.rb
|
636
|
+
map :admin do |namespace|
|
637
|
+
namespace.map(:show).to("/admin/:id/show")
|
638
|
+
namespace.map(:destroy).to("/admin/:id/destroy")
|
639
|
+
end
|
640
|
+
|
641
|
+
namespace :admin do
|
642
|
+
get :show do
|
643
|
+
"admin show for #{params[:id]}"
|
644
|
+
end
|
645
|
+
|
646
|
+
get :destroy do
|
647
|
+
"admin destroy for #{params[:id]}"
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
You can then reference the urls using the same <tt>url_for</tt> method:
|
652
|
+
|
653
|
+
<%= link_to 'admin page', url_for(:admin, :show, :id => 25) %>
|
654
|
+
<%= link_to 'admin page', url_for(:admin, :update, :id => 25) %>
|
655
|
+
<%= link_to 'admin page', url_for(:admin, :show, :id => 25) %>
|
656
|
+
|
657
|
+
You can freely use both named route aliases and traditional Sinatra routes in the same application without conflict.
|
658
|
+
|
659
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/routingplugin>
|
660
|
+
|
661
|
+
== Sinatra Generators
|
662
|
+
|
663
|
+
In addition to the support provided above, sinatra_more also comes preloaded with flexible code generators for Sinatra
|
664
|
+
powered in part by the excellent Thor gem (incidentally also used in the Rails 3 generators). These generators are intended
|
665
|
+
to allow for easy code generation both in creating new applications and building on existing ones. The generators have been
|
666
|
+
built to be as library-agnostic as possible, supporting a myriad of test frameworks, js libraries, mocking libraries, etc.
|
667
|
+
|
668
|
+
The usage for the generator is quite simple:
|
669
|
+
|
670
|
+
$ sinatra_gen <the_app_name> </path/to/create/app> --<component-name> <value>
|
671
|
+
|
672
|
+
The simplest possible command to generate a base application would be:
|
673
|
+
|
674
|
+
$ sinatra_gen demo_app .
|
675
|
+
|
676
|
+
This would construct a Sinatra application DemoApp (which extends from Sinatra::Application)
|
677
|
+
inside the folder 'demo_app' at our current path. Inside the application there would be configuration and
|
678
|
+
setup performed for the default components.
|
679
|
+
|
680
|
+
You can also define specific components to be used:
|
681
|
+
|
682
|
+
$ sinatra_gen demo_app . --test=rspec --renderer=haml --mock=rr --script=jquery --orm datamapper
|
683
|
+
|
684
|
+
There is also support for aliases for each component within the command:
|
685
|
+
|
686
|
+
$ sinatra_gen demo_app . -t=rspec -r=haml -m=rr -s=jquery -d=datamapper
|
687
|
+
|
688
|
+
The available components and their default options are listed below:
|
689
|
+
|
690
|
+
* test: <tt>bacon</tt> (default), <tt>shoulda</tt>, <tt>rspec</tt>, <tt>testspec</tt>, <tt>riot</tt>
|
691
|
+
* renderer: <tt>erb</tt> (default), <tt>haml</tt>
|
692
|
+
* mock: <tt>mocha</tt> (default), <tt>rr</tt>
|
693
|
+
* script: <tt>jquery</tt> (default), <tt>prototype</tt>, <tt>rightjs</tt>
|
694
|
+
* orm: <tt>datamapper</tt> (default), <tt>mongomapper</tt>, <tt>activerecord</tt>, <tt>sequel</tt>, <tt>couchrest</tt>
|
695
|
+
|
696
|
+
The generator uses the <tt>bundler</tt> gem to resolve any application dependencies when the application is newly created.
|
697
|
+
The necessary bundler command can be executed automatically through the generator with
|
698
|
+
|
699
|
+
$ sinatra_gen demo_app . --run_bundler # alias -b
|
700
|
+
|
701
|
+
or this can be done manually through executing command <tt>gem bundle</tt> in the terminal at the root of the generated application.
|
702
|
+
If not executed manually, the bundling will be performed automatically the first time the application attempts to boot.
|
703
|
+
Note that this command only has to be performed when the application is first generated or when the Gemfile is modified.
|
704
|
+
|
705
|
+
The generator framework within sinatra_more is extensible and additional components and tools can be added easily.
|
706
|
+
This would be achieved through forking our project and reading through the code in <tt>/generators/sinatra_generator.rb</tt> and
|
707
|
+
the setup instructions inside the relevant files within <tt>/generators/components/</tt>. We are happy to accept pull requests
|
708
|
+
for additional component types not originally included (although helping us maintain them would also be appreciated).
|
709
|
+
|
710
|
+
We are also planning to add generator actions such as:
|
711
|
+
|
712
|
+
* Model generation (working for any of the available orms listed)
|
713
|
+
* Routes generation
|
714
|
+
* Mailer generation
|
715
|
+
* Migrations generation
|
716
|
+
|
717
|
+
See the wiki article for additional information: <http://wiki.github.com/nesquena/sinatra_more/generator>
|
718
|
+
|
719
|
+
== Acknowledgements
|
720
|
+
|
721
|
+
* Thanks to kelredd (http://github.com/kelredd) for the <tt>sinatra_helpers</tt> code that helped me to create erb capture and concat methods.
|
722
|
+
* Thanks to sbfaulkner for the <tt>sinatra-helpers</tt> code that I browsed through many times while starting this library.
|
723
|
+
* Thanks to vestel for the excellent modified <tt>pony</tt> fork which in part powers the mailer_plugin (http://github.com/vestel/pony)
|
724
|
+
* Thanks to focat and sinatra-content-for library (http://github.com/focat/sinatra-content-for) for a good content_for starting point
|
725
|
+
* Thanks to bcarlso for the snap sinatra library (http://github.com/bcarlso/snap) which was a starting point for named routes
|
726
|
+
* Thanks to wycats and others for the awesome Thor gem which made creating the sinatra generator relatively painless
|
727
|
+
* Thanks to wycats and others for the bundler gem which made bundling the required gems for an application easy
|
728
|
+
|
729
|
+
== Contributors
|
730
|
+
|
731
|
+
* Nathan Esquenazi - Project creator and code maintainer
|
732
|
+
* Arthur Chiu - Forming the idea and various code contributions
|
733
|
+
* Rob Holland - Added couchrest ORM component to generator
|
734
|
+
* Bill Turner - Added several form builder and helper fixes
|
735
|
+
|
736
|
+
== Known Issues
|
737
|
+
|
738
|
+
* No serious issues known
|
739
|
+
|
740
|
+
== Links and Resources
|
741
|
+
|
742
|
+
* EnvyCasts - <http://railsenvy.com/2009/10/28/episode-098>
|
743
|
+
* Ruby5 - <http://ruby5.envylabs.com/episodes/24-episode-23-october-30-2009/stories/183-sinatra-more>
|
744
|
+
|
745
|
+
== Note on Patches/Pull Requests
|
746
|
+
|
747
|
+
* Fork the project.
|
748
|
+
* Make your feature addition or bug fix.
|
749
|
+
* Add tests for it. This is important so I don't break it in a
|
750
|
+
future version unintentionally.
|
751
|
+
* Commit, do not mess with rakefile, version, or history.
|
752
|
+
* Send me a pull request. Bonus points for topic branches.
|
753
|
+
|
754
|
+
== Copyright
|
755
|
+
|
756
|
+
Copyright (c) 2009 Nathan Esquenazi. See LICENSE for details.
|