= sinatra_more
== Preface
This gem has been designed to work with Sinatra (http://www.sinatrarb.com).
Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.
The canonical example of how to create an entire simple web application with Sinatra is something like:
# myapp.rb
require 'rubygems'
require 'sinatra'
get '/' do
'Hello world!'
end
and then to run the application:
$ ruby myapp.rb
The extreme simplicity of the framework is quite refreshing. I have been using Sinatra a great deal
for recent projects. First for small and simple json and xml web services and then even
for more complex full-featured applications. This gem represents my attempt to make it as fun and easy
as possible to code increasingly advanced view-heavy web applications in Sinatra.
== Introduction
Note: This library is still experimental and may not be ready for production just yet.
That being said the gem is being actively used on a number of sinatra projects.
In addition, the gem has fairly solid test coverage ensuring that everything works as expected.
This will be a plugin which expands sinatra's capabilities in a variety of ways.
Note that all extensions have been created to work with haml, erb, and erubis.
This gem is intended to be template-agnostic in providing helpers wherever possible.
Let me expand briefly on what I want to accomplish with this gem. I love sinatra but if I want to use it
for any non-trivial application I very quickly miss a lot of the extra tools provided by rails.
Now the obvious question is "Why not just use rails then?" Well, in many cases that might be the right decision.
Still, at least until version 3 comes along, Rails is quite a large framework with a 'take it or leave it' attitude.
Personally, I have come to love the spirit of sinatra which acts as a thin wrapper on top of rack
often allowing middleware to do most of the work and pulling in additional complexity only as required.
My goal with this extension is to match the spirit of Sinatra and at the same time create a standard library
of tools, helpers and components that will make Sinatra suitable for more complex applications.
Here is a small list of what sinatra_more contains:
* Code generators for creating new sinatra applications (using sinatra_gen on command line)
* Generic view and tag helpers (tag, content_tag, input_tag, ...)
* Asset tag helpers (link_to, image_tag, javascript_include_tag, ...)
* Full form helpers and builders support (form_tag, form_for, field_set_tag, text_field, ...)
* Generally useful formatting extensions (relative_time_ago, js_escape_html, sanitize_html)
* Simple 'mailer' support for sinatra (akin to ActionMailer but simpler and powered by pony)
* Plug and play setup for the excellent Warden authentication system
Keep in mind, the user will be able to pull in these components seperately and leave out those that are not required.
Please help me brainstorm and fork the project if you have any ideas to contribute.
== Installation
If you want to use the WardenPlugin component, then the 'warden' gem would need to be installed.
To install sinatra_more, simply grab the latest version from gemcutter:
$ sudo gem install sinatra_more --source http://gemcutter.org
Now you are ready to use this gem in your sinatra project.
== Usage
This extension can be easily registered into any existing sinatra application. You can require
different components based on which pieces are useful for your particular application.
# app.rb
require 'sinatra/base'
require 'sinatra_more' # or require 'sinatra_more/markup_plugin' for precise inclusion
class Application < Sinatra::Base
register SinatraMore::MarkupPlugin
register SinatraMore::RenderPlugin
register SinatraMore::WardenPlugin
register SinatraMore::MailerPlugin
end
This will then allow you to use the components that have been registered. A breakdown of components is below:
=== MarkupPlugin
This component provides a great deal of view helpers related to html markup generation.
There are helpers for generating tags, forms, links, images, and more. Most of the basic
methods should be very familiar to anyone who has used rails view helpers.
==== Output Helpers
* content_for(key, &block)
* Capture a block of content to be rendered at a later time.
* content_for(:head) { ...content... }
* Also supports arguments passed to the content block
* content_for(:head) { |param1, param2| ...content... }
* yield_content(key, *args)
* Render the captured content blocks for a given key.
* yield_content :head
* Also supports arguments yielded to the content block
* yield_content :head, param1, param2
* capture_html(*args, &block)
* Captures the html from a block of template code for erb or haml
* capture_html(&block) => "...html..."
* concat_content(text="")
* Outputs the given text to the templates buffer directly in erb or haml
* concat_content("This will be output to the template buffer in erb or haml")
==== Tag Helpers
* tag(name, options={})
* Creates an html tag with the given name and options
* tag(:br, :style => 'clear:both') =>
* tag(:p, :content => "demo", :class => 'large') =>
demo
* content_tag(name, content, options={}) * Creates an html tag with given name, content and options * content_tag(:p, "demo", :class => 'light') =>demo
* content_tag(:p, :class => 'dark') { ...content... } =>...content...
* input_tag(type, options = {}) * Creates an html input field with given type and options * input_tag :text, :class => "demo" * input_tag :password, :value => "secret", :class => "demo" ==== Asset Helpers * flash_tag(kind, options={}) * Creates a div to display the flash of given type if it exists * flash_tag(:notice, :class => 'flash', :id => 'flash-notice') * link_to(*args, &block) * Creates a link element with given name, url and options * link_to 'click me', '/dashboard', :class => 'linky' * link_to('/dashboard', :class => 'blocky') { ...content... } * image_tag(url, options={}) * Creates an image element with given url and options * image_tag('icons/avatar.png') * stylesheet_link_tag(*sources) * Returns a stylesheet link tag for the sources specified as arguments * stylesheet_link_tag 'style', 'application', 'layout' * javascript_include_tag(*sources) * Returns an html script tag for each of the sources provided. * javascript_include_tag 'application', 'special' ==== Form Helpers * form_tag(url, options={}, &block) * Constructs a form without object based on options * Supports form methods 'put' and 'delete' through hidden field * form_tag('/register', :class => 'example') { ... } * field_set_tag(*args, &block) * Constructs a field_set to group fields with given options * field_set_tag(:class => 'office-set') { } * field_set_tag("Office", :class => 'office-set') { } * error_messages_for(record, options={}) * Constructs list html for the errors for a given object * error_messages_for @user * label_tag(name, options={}, &block) * Constructs a label tag from the given options * label_tag :username, :class => 'long-label' * label_tag(:username, :class => 'blocked-label') { ... } * hidden_field_tag(name, options={}) * Constructs a hidden field input from the given options * hidden_field_tag :session_key, :value => 'secret' * text_field_tag(name, options={}) * Constructs a text field input from the given options * text_field_tag :username, :class => 'long' * text_area_tag(name, options={}) * Constructs a text area input from the given options * text_area_tag :username, :class => 'long' * password_field_tag(name, options={}) * Constructs a password field input from the given options * password_field_tag :password, :class => 'long' * check_box_tag(name, options={}) * Constructs a checkbox input from the given options * check_box_tag :remember_me, :checked => true * radio_button_tag(name, options={}) * Constructs a radio button input from the given options * radio_button_tag :gender, :value => 'male' * select_tag(name, settings={}) * Constructs a select tag with options from the given settings * select_tag(:favorite_color, :options => ['1', '2', '3'], :selected => '1') * select_tag(:more_color, :options => [['label', '1'], ['label2', '2']]) * select_tag(:multiple_color, :options => [...], :multiple => true) * file_field_tag(name, options={}) * Constructs a file field input from the given options * file_field_tag :photo, :class => 'long' * submit_tag(caption, options={}) * Constructs a submit button from the given options * submit_tag "Create", :class => 'success' * image_submit_tag(source, options={}) * Constructs an image submit button from the given options * image_submit_tag "submit.png", :class => 'success' A form_tag might look like: - form_tag '/destroy', :class => 'destroy-form', :method => 'delete' do = flash_tag(:notice) - field_set_tag do %p = label_tag :username, :class => 'first' = text_field_tag :username, :value => params[:username] %p = label_tag :password, :class => 'first' = password_field_tag :password, :value => params[:password] %p = label_tag :strategy = select_tag :strategy, :options => ['delete', 'destroy'], :selected => 'delete' %p = check_box_tag :confirm_delete - field_set_tag(:class => 'buttons') do = submit_tag "Remove" ==== FormBuilders * form_for(object, url, settings={}, &block) * Constructs a form using given or default form_builder * Supports form methods 'put' and 'delete' through hidden field * Defaults to StandardFormBuilder but you can easily create your own! * form_for(@user, '/register', :id => 'register') { |f| ...field-elements... } * form_for(:user, '/register', :id => 'register') { |f| ...field-elements... } The following are fields provided by AbstractFormBuilder that can be used within a form_for: * error_messages(options={}) * Displays list html for the errors on form object * f.errors_messages * label(field, options={}) * f.label :name, :class => 'long' * text_field(field, options={}) * f.text_field :username, :class => 'long' * check_box(field, options={}) * Uses hidden field to provide a 'unchecked' value for field * f.check_box :remember_me, :uncheck_value => 'false' * radio_button(field, options={}) * f.radio_button :gender, :value => 'male' * hidden_field(field, options={}) * f.hidden_field :session_id, :class => 'hidden' * text_area(field, options={}) * f.text_area :summary, :class => 'long' * password_field(field, options={}) * f.password_field :secret, :class => 'long' * file_field(field, options={}) * f.file_field :photo, :class => 'long' * select(field, options={}) * f.select(:state, :options => ['California', 'Texas', 'Wyoming']) * f.select(:state, :collection => @states, :fields => [:name, :id]) * f.select(:state, :options => [...], :include_blank => true) * submit(caption, options={}) * f.submit "Update", :class => 'long' * image_submit(source, options={}) * f.submit "submit.png", :class => 'long' A form_for using these basic fields might look like: - form_for @user, '/register', :id => 'register' do |f| = f.error_messages %p = f.label :username, :caption => "Nickname" = f.text_field :username %p = f.label :email = f.text_field :email %p = f.label :password = f.password_field :password %p = f.label :is_admin, :caption => "Admin User?" = f.check_box :is_admin $p = f.label :color, :caption => "Favorite Color?" = f.select :color, :options => ['red', 'black'] %p = f.submit "Create", :class => 'button' There is also a StandardFormBuilder which builds on the abstract fields that can be used within a form_for: * text_field_block(field, options={}, label_options={}) * text_field_block(:nickname, :class => 'big', :caption => "Username") * text_area_block(field, options={}, label_options={}) * text_area_block(:about, :class => 'big') * password_field_block(field, options={}, label_options={}) * password_field_block(:code, :class => 'big') * file_field_block(field, options={}, label_options={}) * file_field_block(:photo, :class => 'big') * check_box_block(field, options={}, label_options={}) * check_box_block(:remember_me, :class => 'big') * select_block(field, options={}, label_options={}) * select_block(:country, :option => ['USA', 'Canada']) * submit_block(caption, options={}) * submit_block(:username, :class => 'big') * image_submit_block(source, options={}) * image_submit_block('submit.png', :class => 'big') A form_for using these standard fields might look like: - form_for @user, '/register', :id => 'register' do |f| = f.error_messages = f.text_field_block :name, :caption => "Full name" = f.text_field_block :email = f.check_box_block :remember_me = f.select_block :fav_color, :options => ['red', 'blue'] = f.password_field_block :password = f.submit_block "Create", :class => 'button' and would generate this html: You can also easily build your own FormBuilder which allows for customized fields: class MyCustomFormBuilder < AbstractFormBuilder # Here we have access to a number of useful variables # # * template (use this to invoke any helpers)(ex. template.hidden_field_tag(...)) # * object (the record for this form) (ex. object.valid?) # * object_name (object's underscored type) (ex. object_name => 'admin_user') # # We also have access to self.field_types => [:text_field, :text_area, ...] # In addition, we have access to all the existing field tag helpers (text_field, hidden_field, file_field, ...) end Once a custom builder is defined, any call to form_for can use the new builder: - form_for @user, '/register', :builder => 'MyCustomFormBuilder', :id => 'register' do |f| ...fields here... The form builder can even be made into the default builder when form_for is invoked: # anywhere in the sinatra application set :default_builder, 'MyCustomFormBuilder' And there you have it, a fairly complete form builder solution for sinatra. I hope to create or merge in an even better 'default' form_builder in the near future. ==== Format Helpers * escape_html (alias h and h!) * (from RackUtils) Escape ampersands, brackets and quotes to their HTML/XML entities. * relative_time_ago(date) * Returns relative time in words referencing the given date * relative_time_ago(2.days.ago) => "2 days" * relative_time_ago(5.minutes.ago) => "5 minutes" * relative_time_ago(2800.days.ago) => "over 7 years" * time_in_words(date) * Returns relative time in the past or future using appropriate date format * time_in_words(2.days.ago) => "2 days ago" * time_in_words(100.days.ago) => "Tuesday, July 21" * time_in_words(1.day.from_now) => "tomorrow" * escape_javascript(html_content) * Escapes html to allow passing information to javascript. Used for passing data inside an ajax .js.erb template * escape_javascript("