airbed 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +17 -0
- data/README.txt +5 -0
- data/Rakefile +125 -0
- data/examples/faces.rb +91 -0
- data/lib/airbed.rb +279 -0
- data/lib/airbed/version.rb +9 -0
- data/scripts/txt2html +67 -0
- data/setup.rb +1585 -0
- data/test/test_airbed.rb +11 -0
- data/test/test_helper.rb +2 -0
- data/website/index.html +95 -0
- data/website/index.txt +37 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +76 -0
data/History.txt
ADDED
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Lachie Cox
|
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/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
lib/airbed.rb
|
7
|
+
lib/airbed/version.rb
|
8
|
+
scripts/txt2html
|
9
|
+
setup.rb
|
10
|
+
test/test_airbed.rb
|
11
|
+
test/test_helper.rb
|
12
|
+
website/index.html
|
13
|
+
website/index.txt
|
14
|
+
website/javascripts/rounded_corners_lite.inc.js
|
15
|
+
website/stylesheets/screen.css
|
16
|
+
website/template.rhtml
|
17
|
+
examples/faces.rb
|
data/README.txt
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
Airbed makes camping restful.
|
2
|
+
|
3
|
+
Airbed works in much the same way that rails maps HTTP verbs (GET,POST,PUT and DELETE) onto CRUDdy actions (list, show, create, update, delete, (plus those two also-rans new and edit)).
|
4
|
+
|
5
|
+
However it goes a little deeper to provide default implementations and hooks for the CRUD actions.
|
data/Rakefile
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
|
12
|
+
include FileUtils
|
13
|
+
require File.join(File.dirname(__FILE__), 'lib', 'airbed', 'version')
|
14
|
+
|
15
|
+
AUTHOR = 'Lachie Cox' # can also be an array of Authors
|
16
|
+
EMAIL = "lachie@smartbomb.com.au"
|
17
|
+
DESCRIPTION = "airbed makes camping restful"
|
18
|
+
GEM_NAME = 'airbed' # what ppl will type to install your gem
|
19
|
+
|
20
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
21
|
+
@config = nil
|
22
|
+
def rubyforge_username
|
23
|
+
unless @config
|
24
|
+
begin
|
25
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
26
|
+
rescue
|
27
|
+
puts <<-EOS
|
28
|
+
ERROR: No rubyforge config file found: #{@config_file}"
|
29
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
30
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
31
|
+
EOS
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@rubyforge_username ||= @config["username"]
|
36
|
+
end
|
37
|
+
|
38
|
+
RUBYFORGE_PROJECT = 'rails-oceania' # The unix name for your project
|
39
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
40
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
41
|
+
|
42
|
+
NAME = "airbed"
|
43
|
+
REV = nil
|
44
|
+
# UNCOMMENT IF REQUIRED:
|
45
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
46
|
+
VERS = Airbed::VERSION::STRING + (REV ? ".#{REV}" : "")
|
47
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
|
48
|
+
RDOC_OPTS = ['--quiet', '--title', 'airbed documentation',
|
49
|
+
"--opname", "index.html",
|
50
|
+
"--line-numbers",
|
51
|
+
"--main", "README",
|
52
|
+
"--inline-source"]
|
53
|
+
|
54
|
+
class Hoe
|
55
|
+
def extra_deps
|
56
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generate all the Rake tasks
|
61
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
62
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
63
|
+
p.author = AUTHOR
|
64
|
+
p.description = DESCRIPTION
|
65
|
+
p.email = EMAIL
|
66
|
+
p.summary = DESCRIPTION
|
67
|
+
p.url = HOMEPATH
|
68
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
69
|
+
p.test_globs = ["test/**/test_*.rb"]
|
70
|
+
p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
|
71
|
+
|
72
|
+
# == Optional
|
73
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
74
|
+
p.extra_deps = [ ['camping', '>= 1.5'] ]
|
75
|
+
|
76
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
77
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
78
|
+
end
|
79
|
+
|
80
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
|
81
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
82
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
83
|
+
|
84
|
+
desc 'Generate website files'
|
85
|
+
task :website_generate do
|
86
|
+
Dir['website/**/*.txt'].each do |txt|
|
87
|
+
sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
desc 'Upload website files to rubyforge'
|
92
|
+
task :website_upload do
|
93
|
+
host = "#{rubyforge_username}@rubyforge.org"
|
94
|
+
remote_dir = "/var/www/gforge-projects/#{PATH}/"
|
95
|
+
local_dir = 'website'
|
96
|
+
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'Generate and upload website files'
|
100
|
+
task :website => [:website_generate, :website_upload, :publish_docs]
|
101
|
+
|
102
|
+
desc 'Release the website and new gem version'
|
103
|
+
task :deploy => [:check_version, :website, :release] do
|
104
|
+
puts "Remember to create SVN tag:"
|
105
|
+
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
106
|
+
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
107
|
+
puts "Suggested comment:"
|
108
|
+
puts "Tagging release #{CHANGES}"
|
109
|
+
end
|
110
|
+
|
111
|
+
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
112
|
+
task :local_deploy => [:website_generate, :install_gem]
|
113
|
+
|
114
|
+
task :check_version do
|
115
|
+
unless ENV['VERSION']
|
116
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
117
|
+
exit
|
118
|
+
end
|
119
|
+
unless ENV['VERSION'] == VERS
|
120
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
data/examples/faces.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'airbed'
|
2
|
+
# require File.dirname(__FILE__)+'/../lib/airbed'
|
3
|
+
|
4
|
+
Camping.goes :Faces
|
5
|
+
|
6
|
+
module Faces
|
7
|
+
module Models
|
8
|
+
class Person < Base; end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Controllers
|
12
|
+
include Airbed::Resources
|
13
|
+
|
14
|
+
class Index < R '/'
|
15
|
+
def get
|
16
|
+
redirect People
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class People < Resource Models::Person
|
21
|
+
def after_modification(instance)
|
22
|
+
redirect(People)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Views
|
28
|
+
# TODO make this work using
|
29
|
+
# include Airbed::Views
|
30
|
+
def form(options={})
|
31
|
+
verb = method = options[:method] || :post
|
32
|
+
options[:method] = :post unless [:get,:post].include? method.to_sym
|
33
|
+
|
34
|
+
tag!(:form, options) {
|
35
|
+
input(:type => 'hidden', :name => '_verb', :value => verb)
|
36
|
+
yield
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def _button(text,href,method=:post)
|
41
|
+
form(:action => href, :method => method) {
|
42
|
+
input :type => 'submit', :value => text
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def list_people
|
47
|
+
h1 'ppl!'
|
48
|
+
ul {
|
49
|
+
@people.each {|person|
|
50
|
+
li { a person.name, :href => R(People,person) }
|
51
|
+
_button('x',R(People,person),:delete)
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
form( :action => R(People), :method => 'post') {
|
56
|
+
input :name => 'person[name]'
|
57
|
+
input :type => 'submit', :value => '+'
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def show_person
|
62
|
+
h1 @person.name
|
63
|
+
|
64
|
+
form(:action => R(People,@person), :method => :put) {
|
65
|
+
input :name => 'person[name]', :value => @person.name
|
66
|
+
input :type => 'submit', :value => 'save'
|
67
|
+
}
|
68
|
+
a(:href => R(People)) {"← people"}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# schema
|
74
|
+
module Models
|
75
|
+
class CreateTheBasics < V 1.0
|
76
|
+
def self.up
|
77
|
+
create_table :faces_people do |t|
|
78
|
+
t.column :name, :string
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def Faces.create
|
87
|
+
Faces::Models.create_schema
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
data/lib/airbed.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'camping'
|
2
|
+
require 'airbed/version'
|
3
|
+
|
4
|
+
module Airbed
|
5
|
+
# The Airbed::Resources module is designed to be mixed into your Controllers module.
|
6
|
+
#
|
7
|
+
# == Getting Started
|
8
|
+
#
|
9
|
+
# To let your controllers be restful
|
10
|
+
#
|
11
|
+
# 1. <tt>require 'rubygems'</tt>
|
12
|
+
# 2. <tt>require 'airbed'</tt>
|
13
|
+
# 3. Mixin airbed: <tt>module YourApp::Controllers; include Airbed::Resources; end</tt>
|
14
|
+
#
|
15
|
+
# Now, instead of deriving your controller from <tt>R</tt>, derive it from <tt>Resource</tt>:
|
16
|
+
#
|
17
|
+
# module YourApp::Controllers
|
18
|
+
# class YourModels < Resource YourModel
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# You can override the various default implementations of the restful actions below by just overriding the methods.
|
23
|
+
#
|
24
|
+
module Resources
|
25
|
+
|
26
|
+
def self.included(base) # +nodoc+
|
27
|
+
base.send :extend, ClassMethods
|
28
|
+
end
|
29
|
+
|
30
|
+
# Acts as a dispatcher to map between HTTP verbs and actions
|
31
|
+
class Resty
|
32
|
+
|
33
|
+
# Maps from GET requests to restful actions
|
34
|
+
#
|
35
|
+
# * If +id+ and +action+ are provided, call the method +action+ (if implemented).
|
36
|
+
# * If only id is provided, load the +show_context+ and call show.
|
37
|
+
# * Otherwise, load the new context and call list
|
38
|
+
def get(id=nil,action=nil)
|
39
|
+
|
40
|
+
# we have id and action ... call arbitrary action
|
41
|
+
if !id.blank? and !action.blank?
|
42
|
+
model = instance_context(id)
|
43
|
+
|
44
|
+
# special case
|
45
|
+
action = (action == 'new' ? 'new_action' : action)
|
46
|
+
send(action,model)
|
47
|
+
|
48
|
+
elsif !id.blank?
|
49
|
+
model = show_context(id)
|
50
|
+
show(model)
|
51
|
+
|
52
|
+
else
|
53
|
+
list = list_context
|
54
|
+
list(list)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Maps from POST requests to restful actions. Also supports "exotic" methods PUT and DESTROY (via the _verb parameter).
|
59
|
+
#
|
60
|
+
# 1. POST with an id is undefined
|
61
|
+
# 2. POST with no id loads +new_context+ and calls +create+.
|
62
|
+
# 3. _verb == 'put' + id delegates to +put+ the handler
|
63
|
+
# 4. _verb == 'destroy' + id deletgates to the +destroy+ handler
|
64
|
+
#
|
65
|
+
def post(id=nil)
|
66
|
+
unless id.blank?
|
67
|
+
@model = instance_context(id)
|
68
|
+
|
69
|
+
case input['_verb']
|
70
|
+
when 'put'
|
71
|
+
put(id)
|
72
|
+
when 'delete'
|
73
|
+
delete(id)
|
74
|
+
else
|
75
|
+
raise "eh? unknown verb #{input['_verb']}"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
create(new_context)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Map from PUT requests to restful actions.
|
83
|
+
#
|
84
|
+
# put + id loads +instance_context+ and calls +update+
|
85
|
+
def put(id)
|
86
|
+
update(@model || instance_context(id))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Map from DELETE requests to restful actions.
|
90
|
+
#
|
91
|
+
# delete + id loads +instance_context+ and calls +destroy+
|
92
|
+
def delete(id)
|
93
|
+
destroy(@model || instance_context(id))
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
protected
|
98
|
+
# default implementation of show
|
99
|
+
#
|
100
|
+
# Renders the <tt>show_#{singular_name}</tt> view.
|
101
|
+
def show(instance)
|
102
|
+
render "show_#{singular_name}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# default implementation of list
|
106
|
+
#
|
107
|
+
# Renders the <tt>list_#{plural_name}</tt> view.
|
108
|
+
def list(list)
|
109
|
+
render "list_#{plural_name}"
|
110
|
+
end
|
111
|
+
|
112
|
+
# default implementation of create
|
113
|
+
#
|
114
|
+
# It extracts the model attributes from +input+ and calls +save!+
|
115
|
+
#
|
116
|
+
# Calls +after_modification+ if defined, other wise redirects to +show+.
|
117
|
+
def create(instance)
|
118
|
+
instance.attributes = input[singular_name]
|
119
|
+
instance.save!
|
120
|
+
|
121
|
+
if respond_to? :after_modification
|
122
|
+
after_modification(instance)
|
123
|
+
else
|
124
|
+
redirect(self.class,instance)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# default implementation of update
|
129
|
+
#
|
130
|
+
# delegates to create.
|
131
|
+
def update(instance)
|
132
|
+
create(instance)
|
133
|
+
end
|
134
|
+
|
135
|
+
# default implementation of destroy
|
136
|
+
#
|
137
|
+
# Destroys the instance and redirects to +list+.
|
138
|
+
def destroy(instance)
|
139
|
+
instance.destroy
|
140
|
+
redirect(self.class)
|
141
|
+
end
|
142
|
+
|
143
|
+
## pseudo-rest methods
|
144
|
+
# default implementation of edit
|
145
|
+
#
|
146
|
+
# Renders the <tt>form_#{singular_name}</tt> view.
|
147
|
+
def edit(instance)
|
148
|
+
render "form_#{singular_name}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# default implementation of new
|
152
|
+
#
|
153
|
+
# Renders the <tt>form_#{singular_name}</tt> view.
|
154
|
+
def new_action(instance)
|
155
|
+
render "form_#{singular_name}"
|
156
|
+
end
|
157
|
+
|
158
|
+
## utilities
|
159
|
+
# calls a method if its implemented
|
160
|
+
def hook(method,*args)
|
161
|
+
send(method,*args) if respond_to? method
|
162
|
+
end
|
163
|
+
|
164
|
+
# returns the singular, sluggy class name
|
165
|
+
def singular_name
|
166
|
+
@singular_name ||= model_class.to_s.demodulize.underscore
|
167
|
+
end
|
168
|
+
|
169
|
+
# returns the plural, sluggy class name
|
170
|
+
def plural_name
|
171
|
+
@plural_name ||= singular_name.pluralize
|
172
|
+
end
|
173
|
+
|
174
|
+
# instance version of Resty#model_class (meta defined)
|
175
|
+
def model_class; self.class.model_class ; end
|
176
|
+
|
177
|
+
# instance version of Resty#options (meta defined)
|
178
|
+
def options
|
179
|
+
@options ||= self.class.options || {}
|
180
|
+
end
|
181
|
+
|
182
|
+
# loads the context for show
|
183
|
+
#
|
184
|
+
# delegates to +instance_context+ with <tt>options[:show_options]</tt> if defined.
|
185
|
+
def show_context(id)
|
186
|
+
instance_context(id, options[:show_options] || {})
|
187
|
+
end
|
188
|
+
|
189
|
+
# loads the context for REST methods requiring an instance of the controlled model
|
190
|
+
#
|
191
|
+
# merges <tt>options[:show_options]</tt> with +instance_options+ (if supplied),
|
192
|
+
# and passes it as options to the model classes' +find+ method.
|
193
|
+
#
|
194
|
+
# Sets to <tt>@#{singular_name}</tt> to the loaded instance and returns it.
|
195
|
+
def instance_context(id,instance_options={})
|
196
|
+
instance_options = (options[:instance_options] || {}).merge(instance_options)
|
197
|
+
|
198
|
+
puts "instance context, #{instance_options.inspect}"
|
199
|
+
|
200
|
+
returning(model_class.find(id,instance_options)) do |instance|
|
201
|
+
instance_variable_set("@#{singular_name}",instance)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# loads the context for REST methods requiring a list of instances of the controlled model.
|
206
|
+
#
|
207
|
+
# Passes <tt>options[:list_options]</tt> (if defined) to the model classes' +find+ method.
|
208
|
+
#
|
209
|
+
# Sets <tt>@#{plural_name}</tt> to the list and returns it.
|
210
|
+
def list_context
|
211
|
+
list_options = options[:list_options] || {}
|
212
|
+
|
213
|
+
returning(model_class.find(:all, list_options)) do |list|
|
214
|
+
instance_variable_set("@#{plural_name}",list)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# load the context for REST methods requiring a fresh, unsaved, defaulted instance of the controlled model.
|
219
|
+
#
|
220
|
+
# Hooks +setup_defaults+ and sets <tt>@#{singular_name}</tt> to the instance and returns it.
|
221
|
+
def new_context
|
222
|
+
returning(self.class.model_class.new) do |instance|
|
223
|
+
hook(:setup_defaults,instance)
|
224
|
+
instance_variable_set("@#{singular_name}",instance)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
module ClassMethods
|
230
|
+
|
231
|
+
# Make a controller into a resource.
|
232
|
+
#
|
233
|
+
# Just do
|
234
|
+
#
|
235
|
+
# module Camping::Controllers
|
236
|
+
# class Users < Resource User; end
|
237
|
+
# end
|
238
|
+
#
|
239
|
+
# or maybe something more complex
|
240
|
+
#
|
241
|
+
# module Camping::Controllers
|
242
|
+
# class Users < Resource User, :show_options => {:include => [:achievements]}
|
243
|
+
# def show(user)
|
244
|
+
# user.tweak!
|
245
|
+
# super
|
246
|
+
# end
|
247
|
+
# end
|
248
|
+
# end
|
249
|
+
#
|
250
|
+
def Resource(model_class,options={})
|
251
|
+
|
252
|
+
class_name = model_class.to_s.demodulize
|
253
|
+
name = options[:name] || class_name.underscore.pluralize
|
254
|
+
|
255
|
+
r = @r
|
256
|
+
u = options[:urls] || ["/#{name}", "/#{name}/(\\d+)", "/#{name}/(\\d+)/(.+)"]
|
257
|
+
|
258
|
+
Class.new(Airbed::Resources::Resty) {
|
259
|
+
meta_def(:model_class) { model_class }
|
260
|
+
meta_def(:options) { options }
|
261
|
+
meta_def(:urls) { u }
|
262
|
+
meta_def(:inherited){|x| r << x}
|
263
|
+
}
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
module Views
|
269
|
+
def form(options={})
|
270
|
+
verb = method = options[:method] || :post
|
271
|
+
options[:method] = :post unless [:get,:post].include? method.to_sym
|
272
|
+
|
273
|
+
tag!(:form, options) {
|
274
|
+
input(:type => 'hidden', :name => '_verb', :value => verb)
|
275
|
+
yield
|
276
|
+
}
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|