Skip to content

Commit

Permalink
Hello world!
Browse files Browse the repository at this point in the history
  • Loading branch information
nu7hatch committed Jul 20, 2010
0 parents commit 751071b
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 0 deletions.
20 changes: 20 additions & 0 deletions LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2010 Kriss Kowalik

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63 changes: 63 additions & 0 deletions README.rdoc
@@ -0,0 +1,63 @@
== Padrino responders

This component is used to create slim controllers without unnecessery and repetitive code.

=== Installation

You can install Padrino responders via rubygems:

gem install padrino-responders

=== Getting started

Default responder is responsible for exposing a resource to different mime
requests, usually depending on the HTTP verb. The responder is triggered when
<code>respond</code> is called. The simplest case to study is a GET request:

SampleApp.controller :examples do
provides :html, :xml, :json

get :index do
@examples = Example.find(:all)
respond(@examples)
end
end

When a request comes in, for example for an XML response, three steps happen:

1) the responder searches for a template at extensions/index.xml;
2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.

=== Builtin HTTP verb semantics

Using this responder, a POST request for creating an object could
be written as:

post :create do
@user = User.new(params[:user])
@user.save
respond(@user)
end

Which is exactly the same as:

post :create do
@user = User.new(params[:user])

if @user.save
flash[:notice] = 'User was successfully created.'
case content_type
when :html then redirect url(:users, :show, :id => @user.id)
when :xml then render :xml => @user, :status => :created, :location => url(:users, :show, :id => @user.id)
end
else
case content_type
when :html then render 'index/new'
when :xml then render :xml => @user.errors, :status => :unprocessable_entity
end
end
end

The same happens for PUT and DELETE requests.

20 changes: 20 additions & 0 deletions Rakefile
@@ -0,0 +1,20 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

begin
require 'jeweler'
Jeweler::Tasks.new do |gemspec|
gemspec.name = "padrino-responders"
gemspec.version = "0.1.0"
gemspec.summary = "Simplified responders for Padrino framework"
gemspec.description = "This component is used to create slim controllers
without unnecessery and repetitive code."
gemspec.email = "kriss.kowalik@gmail.com"
gemspec.homepage = "http://github.com/kriss/padrino-responders"
gemspec.authors = ["Kris Kowalik"]
end
rescue LoadError
puts "Jeweler not available. Install it with: gem install jeweler"
end

32 changes: 32 additions & 0 deletions lib/padrino-responders.rb
@@ -0,0 +1,32 @@
require 'padrino-core'
require 'padrino-gen'
require 'padrino-helpers'

FileSet.glob_require('padrino-responders/*.rb', __FILE__)
FileSet.glob_require('padrino-responders/{helpers,notifiers}/*.rb', __FILE__)

module Padrino
##
# This component is used to create slim controllers without unnecessery
# and repetitive code.
#
module Responders
##
# Method used by Padrino::Application when we register the extension
#
class << self
def registered(app)
app.set :notifier, Padrino::Responders::Notifiers::FlashNotifier
app.helpers Padrino::Responders::Helpers::ControllerHelpers
app.send :include, Padrino::Responders::Default
end
alias :included :registered
end
end
end

##
# Load our Padrino::Responders locales
#
I18n.load_path += Dir["#{File.dirname(__FILE__)}/padrino-responders/locale/**/*.yml"]

165 changes: 165 additions & 0 deletions lib/padrino-responders/default.rb
@@ -0,0 +1,165 @@
module Padrino
module Responders
# Default responder is responsible for exposing a resource to different mime
# requests, usually depending on the HTTP verb. The responder is triggered when
# <code>respond</code> is called. The simplest case to study is a GET request:
#
# SampleApp.controller :examples do
# provides :html, :xml, :json
#
# get :index do
# @examples = Example.find(:all)
# respond(@examples)
# end
# end
#
# When a request comes in, for example for an XML response, three steps happen:
#
# 1) the responder searches for a template at extensions/index.xml;
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
#
# === Builtin HTTP verb semantics
#
# Using this responder, a POST request for creating an object could
# be written as:
#
# post :create do
# @user = User.new(params[:user])
# @user.save
# respond(@user)
# end
#
# Which is exactly the same as:
#
# post :create do
# @user = User.new(params[:user])
#
# if @user.save
# flash[:notice] = 'User was successfully created.'
# case content_type
# when :html then redirect url(:users, :show, :id => @user.id)
# when :xml then render :xml => @user, :status => :created, :location => url(:users, :show, :id => @user.id)
# end
# else
# case content_type
# when :html then render 'index/new'
# when :xml then render :xml => @user.errors, :status => :unprocessable_entity
# end
# end
# end
#
# The same happens for PUT and DELETE requests.
#
module Default

def respond(object, location=nil) # :nodoc:
if request.put?
default_response_for_save('edit', object, location)
elsif request.post?
default_response_for_save('new', object, location)
elsif request.delete?
default_response_for_destroy(object, location)
else
default_response(object)
end
end

private

##
# Displays default template, or if it not exists then is trying to display
# serialized object with specified response params.
#
def process_rendering_chain(type, object, params={})
render "#{controller_name}/#{action_name}"
rescue
render params.merge(type.to_sym => object)
end

##
# It's default response for GET requests, eg. listing, show, new, edit, etc.
# This is basic action - when request wants html or js, then will be rendered
# default template for action, otherwise object will be serialized to
# needed format.
#
def default_response(object)
if [:html, :js].include?(content_type)
render "#{controller_name}/#{action_name}"
else
render content_type, object
end
end

##
# In this response we can decide where system will redirect us after successfully
# executed action. Redirections are allowed only for html requests. If
# location is not specified then by default for html and js we will see
# template, for other formats serialised object. System also will show flash
# message about destroyed object.
#
def default_response_for_destroy(object, location=nil)
object_notice = "responder.messages.#{controller_name}.destroy"
alternative_notice = "responder.messages.default.destroy"

if content_type == :html && location
notify(:notice, t(object_notice,
:model => human_model_name(object),
:default => t(alternative_notice, human_model_name(object))
))
redirect location
else
if [:html, :js].include?(content_type)
render "#{controller_name}/destroy"
else
render content_type, object, :location => location
end
end
end

##
# Default response for savings, similar to destroy response allow to store
# redirection after success and also have automatically generated and translated
# flash messages. When action is successfully executed then we will see
# redirection or default views for html and js, and serialized object for
# other formats. Otherwise we will see default form view or serialized errors.
#
def default_response_for_save(kind, object, location=nil)
valid = false
valid = object.valid? if object.respond_to?(:valid?)
current_action = kind == 'new' ? 'create' : 'update'
default_success_view = "#{object.class.name.underscore.pluralize}/#{current_action}"
default_form_view = "#{object.class.name.underscore.pluralize}/#{kind}"
object_notice = "responder.messages.#{object.class.name.underscore.pluralize}.#{current_action}"
alternative_notice = "responder.messages.default.#{current_action}"

if valid
case content_type
when :html
if location
notify(:notice, t(object_notice,
:model => human_model_name(object),
:default => t(alternative_notice, human_model_name(object))
))
redirect location
else
render default_success_view
end
when :js
render default_success_view
else
render content_type, object, :location => location
end
else
if [:html, :js].include?(content_type)
render default_form_view
else
errors = false
errors = object.errors if object.respond_to?(:errors)
render content_type, errors, :status => :unprocessible_entity
end
end
end
end # Default
end # Responders
end # Padrino
43 changes: 43 additions & 0 deletions lib/padrino-responders/helpers/controller_helpers.rb
@@ -0,0 +1,43 @@
module Padrino
module Responders
module Helpers
module ControllerHelpers
protected
##
# Shortcut for <code>notifier.say</code> method.
#
def notify(kind, message, *args, &block)
self.class.notifier.say(self, kind, message, *args, &block)
end

##
# Returns name of current action
#
def action_name
name = request.match.route.instance_variable_get('@name').to_s
name.gsub!(/^#{controller_name}_?/, '')
name = 'index' if name == ''
name
end

##
# Returns name of current controller
#
def controller_name
request.match.route.controller.to_s
end

##
# Returns translated, human readable name for specified model.
#
def human_model_name(object)
if object.class.respond_to?(:human_name)
object.class.human_name
else
t("models.#{object.class.to_s.underscore}", :default => object.class.to_s.humanize)
end
end
end # ControllerHelpers
end # Helpers
end # Responders
end # Padrino
7 changes: 7 additions & 0 deletions lib/padrino-responders/locale/en.yml
@@ -0,0 +1,7 @@
en:
responder:
messages:
default:
destroy: "%{model} has been successfully deleted."
create: "%{model} has been successfully created."
update: "%{model} has been successfully updated."
20 changes: 20 additions & 0 deletions lib/padrino-responders/notifiers/flash_notifier.rb
@@ -0,0 +1,20 @@
module Padrino
module Responders
module Notifiers
module FlashNotifier
##
# Saves specified message as flash notification with specified type.
#
# ==== Examples
#
# notifier.say(self, :error, "Something went wrong")
#
# ... will save message to <code>flash[:notice]</code>
#
def self.say(app, kind, message, *args, &block)
app.flash[kind.to_sym] = message
end
end # FlashNotifier
end # Notifiers
end # Responders
end # Padrino

0 comments on commit 751071b

Please sign in to comment.