Skip to content

Instantly share code, notes, and snippets.

@techarch
Forked from zuk/restful_blog2.rb
Created March 25, 2010 01:30
Show Gist options
  • Save techarch/343058 to your computer and use it in GitHub Desktop.
Save techarch/343058 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'rubygems'
#require 'ruby-debug' # @techarch : commented out since only needed for local debugging
require 'markaby' # @techarch : added explicit require
require 'camping' # @techarch
require 'camping/session' # @techarch : added explicit require since session has changed in Camping 2.0
gem 'RedCloth' # @techarch : added since it is referenced in the Posts model
require 'redcloth' # @techarch : added
#gem 'camping', '~> 2.0'
gem 'camping' , '>= 1.9.354' # @techarch : updated version
#gem 'reststop', '~> 0.3'
=begin # @techarch : commented out since only needed for local debugging
$: << '../../camping-camping/lib'
$: << '../lib'
require 'camping-unabridged'
require 'camping/ar'
require 'camping/session'
=end
#begin # @techarch : commented out since only needed for local debugging
# try to use local copy of library
#require '../lib/reststop2'
require './reststop2.rb' # @techarch : adjusted so that it is located in the same current folder
#rescue LoadError
# # ... otherwise default to rubygem
# require 'reststop'
#end
Camping.goes :Blog
module Blog
include Camping::Session
include Reststop
Controllers.extend Reststop::Controllers
end
module Blog::Base
alias camping_render render
alias camping_service service
include Reststop::Base
alias service reststop_service
alias render reststop_render
end
module Blog::Models
class Post < Base
belongs_to :user
before_save do |record|
cloth = RedCloth.new(record.body)
cloth.hard_breaks = false
record.html_body = cloth.to_html
end
end
class Comment < Base; belongs_to :user; end
class User < Base; end
class BasicFields < V 1.1
def self.up
create_table :blog_posts, :force => true do |t|
t.integer :user_id, :null => false
t.string :title, :limit => 255
t.text :body, :html_body
t.timestamps
end
create_table :blog_users, :force => true do |t|
t.string :username, :password
end
create_table :blog_comments, :force => true do |t|
t.integer :post_id, :null => false
t.string :username
t.text :body, :html_body
t.timestamps
end
User.create :username => 'admin', :password => 'camping'
end
def self.down
drop_table :blog_posts
drop_table :blog_users
drop_table :blog_comments
end
end
end
module Blog::Controllers
extend Reststop::Controllers
class Login < R '/login' # @techarch : added explicit login controller
def get
render :_login
end
end
class Posts < REST 'posts'
# POST /posts
def create
require_login!
@post = Post.create :title => (input.post_title || input.title), # @techarch : allow for REST-client based update
:body => (input.post_body || input.body), # @techarch : allow for REST-client based update
:user_id => @state.user_id
redirect R(@post)
end
# GET /posts/1
# GET /posts/1.xml
def read(post_id)
@post = Post.find(post_id)
@comments = Models::Comment.find(:all, :conditions => ['post_id = ?', post_id])
render :view
end
# PUT /posts/1
def update(post_id)
require_login!
@post = Post.find(post_id)
@post.update_attributes :title => (input.post_title || input.title), # @techarch : allow for REST-client based update
:body => (input.post_body || input.body) # @techarch : allow for REST-client based update
redirect R(@post)
end
# DELETE /posts/1
def delete(post_id)
require_login!
@post = Post.find post_id
if @post.destroy
redirect R(Posts)
else
_error("Unable to delete post #{@post.id}", 500)
end
end
# GET /posts
# GET /posts.xml
def list
@posts = Post.all(:order => 'updated_at DESC')
s=render :index
s
end
# GET /posts/new
def new
#@state.user_id = 1 # @techarch : commented out as was probably hard-coded for testing purpose
require_login!
@user = User.find @state.user_id # @techarch : added since we need the user info
@post = Post.new
render :add
end
# GET /posts/1/edit
def edit(post_id)
require_login!
@user = User.find @state.user_id # @techarch : added since we need the user info
@post = Post.find(post_id)
render :edit
end
end
class Comments < REST 'comments'
# POST /comments
def create
Models::Comment.create(:username => (input.post_username || input.username), # @techarch : allow for REST-client based update
:body => (input.post_body || input.body), # @techarch : allow for REST-client based update
:post_id => input.post_id)
redirect R(Posts, input.post_id)
end
end
class Sessions < REST 'sessions'
# POST /sessions
def create
@user = User.find_by_username_and_password(input.username, input.password)
if @user
@state.user_id = @user.id
redirect R(Posts)
else
@info = 'Wrong username or password.'
end
render :login
end
# DELETE /sessions
def delete
@state.user_id = nil
redirect R(Posts) # @techarch : changed redirect from Index (does not exist) to Posts
end
end
# You can use old-fashioned Camping controllers too!
class Style < R '/styles.css'
def get
@headers["Content-Type"] = "text/css; charset=utf-8"
@body = %{
body {
font-family: Utopia, Georga, serif;
}
h1.header {
background-color: #fef;
margin: 0; padding: 10px;
}
div.content {
padding: 10px;
}
}
end
end
end
module Blog::Helpers
alias_method :_R, :R
remove_method :R
include Reststop::Helpers
def logged_in?
!!@state.user_id
end
def require_login!
unless logged_in?
redirect(R(Blog::Controllers::Login)) # @techarch: add explicit route
throw :halt
end
end
end
module Blog::Views
extend Reststop::Views
module HTML
include Blog::Controllers
include Blog::Views
def layout
html do
head do
title 'blog'
link :rel => 'stylesheet', :type => 'text/css',
:href => self/'/styles.css', :media => 'screen'
end
body do
h1.header { a 'blog', :href => R(Posts) }
div.content do
self << yield
end
end
end
end
def index
if @posts.empty?
p 'No posts found.'
else
for post in @posts
_post(post)
end
end
p { a 'Add', :href => R(Posts, 'new') }
end
def login
p { b @login }
p { a 'Continue', :href => R(Posts, 'new') }
end
def logout
p "You have been logged out."
p { a 'Continue', :href => R(Posts) }
end
def add
if @user
_form(@post, :action => R(Posts))
else
_login
end
end
def edit
if @user
_form(@post, :action => R(@post), :method => :put)
else
_login
end
end
def view
_post(@post)
p "Comment for this post:"
for c in @comments
h1 c.username
p c.body
end
form :action => R(Comments), :method => 'post' do
label 'Name', :for => 'post_username'; br
input :name => 'post_username', :type => 'text'; br
label 'Comment', :for => 'post_body'; br
textarea :name => 'post_body' do; end; br
input :type => 'hidden', :name => 'post_id', :value => @post.id
input :type => 'submit'
end
end
# partials
def _login
form :action => R(Sessions), :method => 'post' do
label 'Username', :for => 'username'; br
input :name => 'username', :type => 'text'; br
label 'Password', :for => 'password'; br
input :name => 'password', :type => 'text'; br
input :type => 'submit', :name => 'login', :value => 'Login'
end
end
def _post(post)
h1 post.title
p post.body
p do
[a("Edit", :href => R(Posts, post.id, 'edit')), a("View", :href => R(Posts, post.id, 'edit'))].join " | "
end
end
def _form(post, opts)
form(:action => R(Sessions), :method => 'delete') do
p do
span "You are logged in as #{@user.username}"
span " | "
button(:type => 'submit') {'Logout'}
end
end
form({:method => 'post'}.merge(opts)) do
label 'Title', :for => 'post_title'; br
input :name => 'post_title', :type => 'text',
:value => post.title; br
label 'Body', :for => 'post_body'; br
textarea post.body, :name => 'post_body'; br
input :type => 'hidden', :name => 'post_id', :value => post.id
input :type => 'submit'
end
end
end
default_format :HTML
module XML
def layout
yield
end
def index
@posts.to_xml(:root => 'blog')
end
def view
@post.to_xml(:root => 'post')
end
end
end
def Blog.create
dbconfig = YAML.load(File.read('config/database.yml')) # @techarch
Camping::Models::Base.establish_connection dbconfig['development'] # @techarch
Blog::Models.create_schema :assume => (Blog::Models::Post.table_exists? ? 1.0 : 0.0)
end
$:.unshift File.dirname(__FILE__)
require 'net/http'
require 'net/https'
require 'uri'
require 'cgi'
require 'timeout'
begin
require 'xml_simple'
rescue LoadError
require 'rubygems'
require 'active_support'
begin
require 'xml_simple'
rescue LoadError
require 'xmlsimple'
end
end
# A very simple REST client, best explained by example:
#
# # Retrieve a Kitten and print its name and colour
# kitten = Restr.get('http://example.com/kittens/1.xml')
# puts kitten['name']
# puts kitten['colour']
#
# # Create a Kitten
# kitten = Restr.post('http://example.com/kittens.xml',
# :name => 'batman', :colour => 'black')
#
# # Update a Kitten
# kitten = Restr.put('http://example.com/kittens/1.xml',
# :age => '6 months')
#
# # Delete a Kitten :(
# kitten = Restr.delete('http://example.com/kittens/1.xml')
#
# # Retrieve a list of Kittens
# kittens = Restr.get('http://example.com/kittens.xml')
#
# When the response to a Restr request has content type 'text/xml', the
# response body will be parsed from XML into a nested Hash (using XmlSimple
# -- see http://xml-simple.rubyforge.org/). Otherwise the response is
# returned untouched, as a String.
#
#
# === Authentication
#
# If the remote REST resource requires authentication (Restr only supports
# HTTP Basic authentication, for now):
#
# Restr.get('http://example.com/kittens/1.xml, {},
# {:username => 'foo', :password => 'bar'})
#
# === Logging
#
# A standard Ruby Logger can be attached to the Restr client like so:
#
# logger = Logger.new('restr.log')
# logger.level = Logger::DEBUG
# Restr.logger = logger
#
# Restr will now log its activity to the given Logger.
# The default_logger can be overridden by supplying a :logger option to
# a client call:
#
# kitten_logger = Logger.new('kitten.log'}
# Restr.get('http://example.com/kittens/1.xml, {},
# {:logger => kitten_logger)
class Restr
module VERSION #:nodoc:
MAJOR = 0
MINOR = 5
TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end
@@logger = nil
@@request_timeout = 180
cattr_accessor :request_timeout
def self.logger=(logger)
@@logger = logger.dup
# ActiveSupport's BufferedLogger doesn't seem to support progname= :(
@@logger.progname = self.name if @@logger.respond_to?(:progname)
end
def self.logger
@@logger
end
def self.method_missing(method, *args)
self.do(method, args[0], args[1] || {}, args[2])
end
def self.do(method, url, params = {}, options = {})
#puts "METHOD: #{method.inspect}"
#puts "URL: #{url.inspect}"
#puts "PARAMS: #{params.inspect}"
#puts "OPTIONS: #{options.inspect}"
uri = URI.parse(url)
params = {} unless params
options = {} unless options
logger = options[:logger] || self.logger
method_mod = method.to_s.downcase.capitalize
unless Net::HTTP.const_defined?(method_mod)
raise InvalidRequestMethod,
"Callback method #{method.inspect} is not a valid HTTP request method."
end
if method_mod == 'Get'
q = params.collect{|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join("&")
if uri.query
uri.query += "&#{q}"
else
uri.query = q
end
end
req = Net::HTTP.const_get(method_mod).new(uri.request_uri)
if options[:username] || options['username']
req.basic_auth options[:username] || options['username'], options[:password] || options['password']
end
if params.kind_of?(Hash) && method_mod != 'Get' && method_mod != 'get'
req.set_form_data(params, '&')
end
logger.debug("Sending #{method.inspect} request to #{url.inspect} "+
(method.to_s == 'get' ? "params" : "data")+" #{params.inspect}"+
(options.blank? ? "" : " with options #{options.inspect}}")+".") if logger
client = Net::HTTP.new(uri.host, uri.port)
client.use_ssl = (uri.scheme == 'https')
timeout = Restr.request_timeout
client.read_timeout = timeout
req.add_field("Cookie", @cookie) if @cookie # @techarch : added the cookie header
begin
res = client.start do |http|
http.request(req)
end
rescue Timeout::Error
res = TimeoutError, "Request timed out after #{timeout} seconds."
end
@cookie = res.response['Set-Cookie'] # @techarch : save the cookie so we can send it back later (in the next request)
case res
when Net::HTTPSuccess
if res.content_type =~ /[\/+]xml$/
logger.debug("Got XML response: \n#{res.body}") if logger
return XmlSimple.xml_in(res.body,
'forcearray' => false,
'keeproot' => false
)
else
logger.debug("Got #{res.content_type.inspect} response: \n#{res.body}") if logger
return res.body
end
when TimeoutError
logger.debug(res) if logger
return XmlSimple.xml_in(res,
'forcearray' => false,
'keeproot' => false
)
else
$LAST_ERROR_BODY = res.body # FIXME: this is dumb... need a better way of reporting errors
$LAST_ERROR_RESPONSE = res # this is currently unused within Restr, but may be useful for debugging
logger.error("Got error response '#{res.message}(#{res.code})': #{res.body.blank? ? '(blank response body)' : res.body}") if logger
res.error!
end
end
class InvalidRequestMethod < Exception
end
end
# Unfinished attempt at re-writing Reststop for Camping 2.0
# Oogly, oogly, oogly.
#
# Might be easier to just fork Camping, implement the restful stuff, and call it Resting :)
#
# I think all of the routing is taken care of, but there's something weird going on with #reststop_render
# Rack complains about invalid output (or something).
#
# Right now you'll have to do some weird gymnastics to get this hooked in to a Camping app...
# Something like:
#
# Camping.goes :Blog
#
# module Blog
# include Reststop
# end
#
# module Blog::Base
# alias camping_render render
# alias camping_service service
# include Reststop::Base
# alias service reststop_service
# alias render reststop_render
# end
#
# module Blog::Controllers
# extend Reststop::Controllers
# ...
# end
#
# module Blog::Helpers
# alias_method :_R, :R
# remove_method :R
# include Reststop::Helpers
# ...
# end
#
# module Blog::Views
# extend Reststop::Views
# ...
# end
#
# The hope is that this could all get taken care of in a
# `include Reststop` call (via overriding of #extended)
$LOG = Logger.new(STDOUT)
module Reststop
module Base
def reststop_service(*a)
if @env['REQUEST_METHOD'] == 'POST' && (input['_method'] == 'put' || input['_method'] == 'delete')
@env['REQUEST_METHOD'] = input._method.upcase
@method = input._method
end
@a0=a[0] if !a.empty?
camping_service(*a)
end
# Overrides Camping's render method to add the ability to specify a format
# module when rendering a view.
#
# The format can also be specified in other ways (shown in this order
# of precedence):
#
# 1. By providing a second parameter to render()
# (eg: <tt>render(:foo, :HTML)</tt>)
# 2. By setting the @format variable
# 3. By providing a 'format' parameter in the request (i.e. input[:format])
# 4. By adding a file-format extension to the url (e.g. /items.xml or
# /items/2.html).
#
# For example, you could have:
#
# module Foobar::Views
#
# module HTML
# def foo
# # ... render some HTML content
# end
# end
#
# module RSS
# def foo
# # ... render some RSS content
# end
# end
#
# end
#
# Then in your controller, you would call render() like this:
#
# render(:foo, :HTML) # render the HTML version of foo
#
# or
#
# render(:foo, :RSS) # render the RSS version of foo
#
# or
#
# @format = :RSS
# render(:foo) # render the RSS version of foo
#
# or
#
# # url is /foobar/1?format=RSS
# render(:foo) # render the RSS version of foo
#
# or
#
# # url is /foobar/1.rss
# render(:foo) # render the RSS version of foo
#
# If no format is specified, render() will behave like it normally does in
# Camping, by looking for a matching view method directly
# in the Views module.
#
# You can also specify a default format module by calling
# <tt>default_format</tt> after the format module definition.
# For example:
#
# module Foobar::Views
# module HTML
# # ... etc.
# end
# default_format :HTML
# end
#
def reststop_render(action, format = nil)
format ||= @format
if format.nil?
begin
ct = CONTENT_TYPE
rescue NameError
ct = 'text/html'
end
@headers['Content-Type'] ||= ct
camping_render(action)
else
# m = Mab.new({}, self) # @techarch : replaced by section below
# mod = "Camping::Views::#{format.to_s}".constantize # @techarch : replaced by section below
# @techarch - begin added section
app_name = self.class.name.split("::").first # @techarch : get the name of the app
mab = (app_name + '::Mab').constantize # @techarch : get the Mab class
m = mab.new({}, self) # @techarch : instantiate Mab
mod = (app_name + "::Views::#{format.to_s}").constantize # @techarch : get the right Views format class
# @techarch - end added section
m.extend mod
begin
ct = mod::CONTENT_TYPE
rescue NameError
ct = "text/#{format.to_s.downcase}"
end
@headers['Content-Type'] = ct
s = m.capture{m.send(action)}
#s = m.capture{send(:layout){s}} if /^_/!~a[0].to_s and m.respond_to?(:layout) # @techarch : commented since a[0] no longer exist
s = m.capture{send(:layout){s}} if /^_/!~@method.to_s and m.respond_to?(:layout) # @techarch : replaced a[0] by @method (not 100% sure that's right though)
s
end
end
end
module Views
# Call this inside your Views module to set a default format.
#
# For example:
#
# module Foobar::Views
# module HTML
# # ... etc.
# end
# default_format :XML
# end
def default_format(m)
mod = "#{self}::#{m.to_s}".constantize
mab = self.to_s.gsub('::Views','::Mab').constantize # @techarch : get the Mab class
mab.class_eval{include mod}
end
end
module Helpers
# Overrides Camping's routing helper to make it possible to route RESTful resources.
#
# Some usage examples:
#
# R(Kittens) # /kittens
# R(Kittens, 'new') # /kittens/new
# R(Kittens, 1, 'meow') # /kittens/1/meow
# R(@kitten) # /kittens/1
# R(@kitten, 'meow') # /kittens/1/meow
# R(Kittens, 'list', :colour => 'black') # /kittens/list?colour=black
#
# The current output format is retained, so if the current <tt>@format</tt> is <tt>:XML</tt>,
# the URL will be /kittens/1.xml rather than /kittens/1.
#
# Note that your controller names might not be loaded if you're calling <tt>R</tt> inside a
# view module. In that case you should use the fully qualified name (i.e. Myapp::Controllers::Kittens)
# or include the Controllers module into your view module.
def R(c, *g)
# @techarch : broke down the code to make it easier to troubleshoot
# @techarch : begin added section
cl = c.class.name.split("::").last.pluralize
app_name = c.class.name.split("::").first
ctrl_cl = app_name + '::Controllers' # @techarch : get to the Controllers using the current app
ctrl = (app_name != 'Class') ? ctrl_cl.constantize : Controllers
# @techarch - end added section
if ctrl.constants.include?(cl) #@techarch updated to use new cl variable
path = "/#{cl.underscore}/#{c.id}"
path << ".#{@format.to_s.downcase}" if @format
path << "/#{g.shift}" unless g.empty?
self / path
elsif c.respond_to?(:restful?) && c.restful?
base = c.name.split("::").last.underscore
id_or_action = g.shift
if id_or_action.to_s =~ /\d+/ #@techarch needed a to_s after id_or_action to allow pattern matching
id = id_or_action
action = g.shift
else
action = id_or_action
end
path = "/#{base}"
path << "/#{id}" if id
path << "/#{action}" if action
path << ".#{@format.to_s.downcase}" if @format
#@techarch substituted U for u=Rack::Utils
u=Rack::Utils
path << "?#{g.collect{|a|a.collect{|k,v| u.escape(k)+"="+u.escape(v)}.join("&")}.join("&")}" unless g.empty? # FIXME: undefined behaviour if there are multiple arguments left
return path
else
_R(c, *g)
end
end # def R
end # module Helpers
module Controllers
def self.determine_format(input, env) #:nodoc:
if input[:format] && !input[:format].empty?
input[:format].upcase.intern
elsif env['PATH_INFO'] =~ /\.([a-z]+)$/
$~[1].upcase.intern
end
end
# Calling <tt>REST "<resource name>"</tt> creates a controller with the
# appropriate routes and maps your REST methods to standard
# Camping controller mehods. This is meant to be used in your Controllers
# module in place of <tt>R <routes></tt>.
#
# Your REST class should define the following methods:
#
# * create
# * read(id)
# * update(id)
# * destroy(id)
# * list
#
# Routes will be automatically created based on the resource name fed to the
# REST method. <b>Your class must have the same (but CamelCaps'ed)
# name as the resource name.</b> So if your resource name is 'kittens',
# your controller class must be Kittens.
#
# For example:
#
# module Foobar::Controllers
# class Kittens < REST 'kittens'
# # POST /kittens
# def create
# end
#
# # GET /kittens/(\d+)
# def read(id)
# end
#
# # PUT /kittens/(\d+)
# def update(id)
# end
#
# # DELETE /kittens/(\d+)
# def destroy(id)
# end
#
# # GET /kittens
# def list
# end
# end
# end
#
# Custom actions are also possible. For example, to implement a 'meow'
# action simply add a 'meow' method to the above controller:
#
# # POST/GET/PUT/DELETE /kittens/meow
# # POST/GET/PUT/DELETE /kittens/(\d+)/meow
# def meow(id)
# end
#
# Note that a custom action will respond to all four HTTP methods
# (POST/GET/PUT/DELETE).
#
# Optionally, you can specify a <tt>:prefix</tt> key that will prepend the
# given string to the routes. For example, the following will create all
# of the above routes, prefixed with "/pets"
# (i.e. <tt>POST '/pets/kittens'</tt>, <tt>GET '/pets/kittens/(\d+)'</tt>,
# etc.):
#
# module Foobar::Controllers
# class Items < REST 'kittens', :prefix => '/pets'
# # ...
# end
# end
#
# Format-based routing similar to that in ActiveResource is also implemented.
# For example, to get a list of kittens in XML format, place a
# <tt>GET</tt> call to <tt>/kittens.xml</tt>.
# See the documentation for the render() method for more info.
#
def REST(r, options = {})
crud = R "#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)/([a-z_]+)(?:\.[a-z]+)?",
"#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)(?:\.[a-z]+)?",
"#{options[:prefix]}/#{r}/([a-z_]+)(?:\.[a-z]+)?",
"#{options[:prefix]}/#{r}(?:\.[a-z]+)?"
crud.module_eval do
meta_def(:restful?){true}
$LOG.debug("Creating RESTful controller for #{r.inspect} using Reststop #{'pull version number here'}") if $LOG
def get(id_or_custom_action = nil, custom_action = nil) # :nodoc:
id = input['id'] if input['id']
custom_action = input['action'] if input['action']
if self.methods.include? id_or_custom_action
custom_action ||= id_or_custom_action
id ||= nil
else
id ||= id_or_custom_action
end
id = id.to_i if id && id =~ /^[0-9]+$/
@format = Reststop::Controllers.determine_format(input, @env)
begin
if id.nil? && input['id'].nil?
custom_action ? send(custom_action) : list
else
custom_action ? send(custom_action, id || input['id']) : read(id || input['id'])
end
rescue NoMethodError => e
# FIXME: this is probably not a good way to do this, but we need to somehow differentiate
# between 'no such route' vs. other NoMethodErrors
if e.message =~ /no such method/
return no_method(e)
else
raise e
end
rescue ActiveRecord::RecordNotFound => e
return not_found(e)
end
end
def post(custom_action = nil) # :nodoc:
@format = Reststop::Controllers.determine_format(input, @env)
custom_action ? send(custom_action) : create
end
def put(id, custom_action = nil) # :nodoc:
id = id.to_i if id =~ /^[0-9]+$/
@format = Reststop::Controllers.determine_format(input, @env)
custom_action ? send(custom_action, id || input['id']) : update(id || input['id'])
end
def delete(id, custom_action = nil) # :nodoc:
id = id.to_i if id =~ /^[0-9]+$/
@format = Reststop::Controllers.determine_format(input, @env)
custom_action ? send(custom_action, id || input['id']) : destroy(id || input['id'])
end
private
def _error(message, status_code = 500, e = nil)
@status = status_code
@message = message
begin
render "error_#{status_code}".intern
rescue NoMethodError
if @format.to_s == 'XML'
"<error code='#{status_code}'>#{@message}</error>"
else
out = "<strong>#{@message}</strong>"
out += "<pre style='color: #bbb'><strong>#{e.class}: #{e}</strong>\n#{e.backtrace.join("\n")}</pre>" if e
out
end
end
end
def no_method(e)
_error("No controller method responds to this route!", 501, e)
end
def not_found(e)
_error("Record not found!", 404, e)
end
end
crud
end # def REST
end # module Controllers
end # module Reststop
module Markaby
class Builder
# Modifies Markaby's 'form' generator so that if a 'method' parameter
# is supplied, a hidden '_method' input is automatically added.
def form(*args, &block)
options = args[0] if args && args[0] && args[0].kind_of?(Hash)
inside = capture &block
if options && options.has_key?(:method)
inside = input(:type => 'hidden', :name => '_method', :value => options[:method]) +
inside
if options[:method].to_s === 'put' || options[:method].to_s == 'delete'
options[:method] = 'post'
end
end
tag!(:form, options || args[0]) {inside}
end
end
end
# Prerequisites:
# 1) gem install xml-simple
# 2) gem install restr
gem 'xml-simple'
gem 'restr'
require 'restr'
logger = Logger.new('restr.log')
logger.level = Logger::DEBUG
Restr.logger = logger
u0 = "http://localhost:3301/sessions.xml"
o = { :username=>'admin', :password=>'camping'}
p0=Restr.post(u0,o)
u1 = "http://localhost:3301/posts/1.xml"
p = Restr.get(u1,o)
# Modify the title
p['title']='HOT off the presses: ' + p['title']
# Update the resource
p2=Restr.put(u1,p,o)
u3="http://localhost:3301/posts.xml"
p3={ :title=>'Brand new REST-issued post', :body=>'RESTstop makes it happen!!!'}
p4=Restr.post(u2,p3)
u3="http://localhost:3301/posts/4.xml"
p5=Restr.delete(u3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment