resourcelogic 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +167 -0
- data/Rakefile +49 -0
- data/VERSION.yml +4 -0
- data/init.rb +1 -0
- data/lib/resourcelogic.rb +16 -0
- data/lib/resourcelogic/accessors.rb +53 -0
- data/lib/resourcelogic/action_options.rb +40 -0
- data/lib/resourcelogic/actions.rb +174 -0
- data/lib/resourcelogic/aliases.rb +134 -0
- data/lib/resourcelogic/base.rb +130 -0
- data/lib/resourcelogic/child.rb +55 -0
- data/lib/resourcelogic/context.rb +51 -0
- data/lib/resourcelogic/context_options.rb +5 -0
- data/lib/resourcelogic/failable_action_options.rb +25 -0
- data/lib/resourcelogic/parent.rb +142 -0
- data/lib/resourcelogic/response_collector.rb +32 -0
- data/lib/resourcelogic/scope.rb +36 -0
- data/lib/resourcelogic/self.rb +148 -0
- data/lib/resourcelogic/sibling.rb +20 -0
- data/lib/resourcelogic/singleton.rb +55 -0
- data/lib/resourcelogic/sub_views.rb +98 -0
- data/lib/resourcelogic/urligence.rb +128 -0
- data/resourcelogic.gemspec +62 -0
- metadata +90 -0
data/.gitignore
ADDED
data/CHANGELOG.rdoc
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ben Johnson of Binary Logic (binarylogic.com)
|
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,167 @@
|
|
1
|
+
= Resourcelogic
|
2
|
+
|
3
|
+
<b>Beta warning: right now this plugin is in what I like to call a "semi-beta". The only thing left to do is write documentation and add in the tests. I have already thoroughly tested this in a separate application, and am in the process of moving over the tests without having to pull over the entire rails application.</b>
|
4
|
+
|
5
|
+
The purpose of Resourcelogic is to support a development style I created called <b>"Contextual Development"</b> (see contextual development below). This is an idea I've had for a while and finally decided to give it a try on one of my projects. It worked out great, and as a result, is probably the cleanest app I've built to date. So I decided to package this idea up and release it as Resourcelogic. This library spawned out of the {resource_controller plugin}[http://github.com/giraffesoft/resource_controller] by James Gollick, which is an excellent plugin. I eventually made so many changes to it that it made more sense to rewrite my own.
|
6
|
+
|
7
|
+
== Helpful links
|
8
|
+
|
9
|
+
* <b>Documentation:</b> http://rdoc.info/projects/binarylogic/resourcelogic
|
10
|
+
* <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/28581-resourcelogic
|
11
|
+
|
12
|
+
== Contextual Development
|
13
|
+
|
14
|
+
The idea behind contextual development is simple: <b>an API should be a be a byproduct of good design</b>. Meaning you should never have to explicitly build an API. It should, in essence, be an "accident".
|
15
|
+
|
16
|
+
=== 1. No need to namespace controllers when you have context
|
17
|
+
|
18
|
+
Your interface should not dictate the structure of your controllers. Instead, your controllers should represent a collection of resources that your interface can use (AKA an API). The problem is that rails does so much magic, and makes so many assumptions, that sometimes it distorts the basics behind the MVC pattern.
|
19
|
+
|
20
|
+
A good example is using {Flex}[http://www.adobe.com/products/flex/] to build your interface. This allows you to go back to the basics behind RESTful development by forcing you to separate your interface from your application. A flex interface communicates with your application via XML. As a result, when you complete your application you also have a "production ready" API. You basically completed two projects in one.
|
21
|
+
|
22
|
+
That being said, why can't we accomplish the same thing with an HTML interface? For one, an HTML interface isn't as cleanly decoupled from the controllers as a Flex interface. For two, rails doesn't give us all of the tools to properly accomplish this. Instead, people create work-arounds by namespacing controllers to represent context and/or scope (Ex: Admin::CommentsController). Why not have a single controller that is <b>context aware</b>? Hence the name <b>"Contextual Development"</b>. So instead of an Admin namespace, you can use the context to modify the interface / scope. Example:
|
23
|
+
|
24
|
+
class CommentsController < ApplicationController
|
25
|
+
layout Proc.new { context == :admin ? "admin" : "application" }
|
26
|
+
|
27
|
+
private
|
28
|
+
# context is irrelevant when determining scope
|
29
|
+
def scope
|
30
|
+
current_user.admin? ? Comment : current_user.comments
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
=== 2. Single point of access for resources
|
35
|
+
|
36
|
+
Having a <b>single</b> controller is great, because it represents a <b>single</b> resource. This means that <b>every</b> request for that resource must pass through that single controller. Think about it, what is an API essentially? Controlled access to your database. You don't go and create an entirely new model for every scope/context you use the model, do you? Why do the same for your API? Your API resources are the "models" for anyone using it, so it doesn't make sense to have /comments and /admin/comments.
|
37
|
+
|
38
|
+
To bring it all home, all requests for a resource should pass through a single controller, in the same manner that all database activity for a table must pass through a single model. You can feel confident adding controller specific logic to that resource without having to worry about duplicating it across multiple controllers. A very simple example: storing the user's IP address with a record.
|
39
|
+
|
40
|
+
=== 3. Relative URL and path methods
|
41
|
+
|
42
|
+
Another big problem with using the same controller in various contexts is the need to specify relative paths instead of absolute paths. When you specify resources in your routes you get all kinds of useful <b>absolute</b> path methods, but where are the relative ones? Let's say you have the following paths:
|
43
|
+
|
44
|
+
1. /articles
|
45
|
+
2. /admin/articles
|
46
|
+
|
47
|
+
Articles have many comments. What if you want to link to that article's comments? Do you use <b>article_comments_path</b> or <b>admin_article_comments_path</b>? You could use the <b>context</b> method described above to determine this, but this will get messy and annoying very fast. Instead why not use <b>child_collection_path(:comments)</b>? The paths are relative based on a tree structure. So you also get <b>parent</b> and <b>sibling</b> paths. Now you can easily nest resources without having to worry about linking to them. See the documentation for more details on the various URL and path methods that are available to you.
|
48
|
+
|
49
|
+
== Install and use
|
50
|
+
|
51
|
+
=== 1. Install the gem
|
52
|
+
|
53
|
+
Install the gem / plugin (recommended)
|
54
|
+
|
55
|
+
$ sudo gem install resourcelogic
|
56
|
+
|
57
|
+
Now add the gem dependency in your config:
|
58
|
+
|
59
|
+
# config/environment.rb
|
60
|
+
config.gem "resourcelogic"
|
61
|
+
|
62
|
+
Or you install this as a plugin (for older versions of rails)
|
63
|
+
|
64
|
+
script/plugin install git://github.com/binarylogic/resourcelogic.git
|
65
|
+
|
66
|
+
=== 2. Create your ResourceController
|
67
|
+
|
68
|
+
script/generate controller resource
|
69
|
+
|
70
|
+
Your ResourceController should look something like:
|
71
|
+
|
72
|
+
class ResourceController < ApplicationController
|
73
|
+
acts_as_resource
|
74
|
+
end
|
75
|
+
|
76
|
+
Now all of your controllers that are "resources" can extend this controller. Why do this? So you can set your default behavior for resources in one spot. This idea what brought over from the resource_controller plugin. The DSL resource_controller came up with is pretty cool:
|
77
|
+
|
78
|
+
class ResourceController < ApplicationController
|
79
|
+
acts_as_resource
|
80
|
+
|
81
|
+
create.flash { # code to evaluate to create the flash message for a successful create }
|
82
|
+
create.before { # code you want to execute before the create action }
|
83
|
+
create.wants.js { # code you want to execute in the wants.js for a successful create }
|
84
|
+
create.failure.flash { # code to evaluate to create the flash message for an unsuccessful create }
|
85
|
+
create.failure.js { # code you want to execute in the wants.js for an unsuccessful create }
|
86
|
+
# etc...See Resourcelogic::ActionOptions and Resourcelogic::FailableActionOptions for more details
|
87
|
+
end
|
88
|
+
|
89
|
+
All of these are overrideable, meaning your subclasses can change behavior in a granular manner, and preserve the defaults where necessary.
|
90
|
+
|
91
|
+
=== 3. Make sure you don't namespace your controllers
|
92
|
+
|
93
|
+
Instead of namespacing your controllers, give them context. For example, let's say you you have /commments and /admin/comments. This controller has 2 contexts: root and admin. So your routes should look something like:
|
94
|
+
|
95
|
+
map.with_options(:path_prefix => "admin", :name_prefix => "admin_") do |admin|
|
96
|
+
admin.resource :comments
|
97
|
+
end
|
98
|
+
map.resources :comments
|
99
|
+
|
100
|
+
Then in your controller use the context method to make adjustments:
|
101
|
+
|
102
|
+
class CommentsController < ResourceController
|
103
|
+
layout Proc.new { context == :admin ? "admin" : "application" }
|
104
|
+
end
|
105
|
+
|
106
|
+
You also have the same context method in your views. Lastly, if you feel your views are getting cluttered by trying to determine what context you are in, you can use the namespace_views_by_context option (See Resourcelogic::Context::Config for more info). This will change your default view path to a context subfolder. Ex:
|
107
|
+
|
108
|
+
/comments
|
109
|
+
/admin
|
110
|
+
/root
|
111
|
+
any other contexts..
|
112
|
+
|
113
|
+
See the Feature Highlights section below for more options, and the documentation for a complete list.
|
114
|
+
|
115
|
+
== Feature highlights
|
116
|
+
|
117
|
+
I don't want to repeat what is already in the documentation, but there are a lot of really nice configuration and utility methods. <b>Here are just a few</b>:
|
118
|
+
|
119
|
+
<b>Class level methods</b>
|
120
|
+
|
121
|
+
belongs_to :relationship_name # will check to see if the resource is being scoped by a parent and give you some nifty methods for this (see below). You can call this multiple times. Just like ActiveRecord.
|
122
|
+
contextual_views true # will split up your views into subfolders: comments/context1, comments/context2, and will change your default view path to the respective folder
|
123
|
+
|
124
|
+
<b>Instance level methods</b>
|
125
|
+
|
126
|
+
Lets pretend we are dealing with a products resource that belongs to a category.
|
127
|
+
|
128
|
+
context # the name of the context you are in
|
129
|
+
|
130
|
+
object # current product object, if any
|
131
|
+
collection # current collection of products
|
132
|
+
parent # current category object, if any
|
133
|
+
|
134
|
+
Now you have all of the "relative routes" to help you easily nest resources and use them in different contexts:
|
135
|
+
|
136
|
+
object_path # /products/:id
|
137
|
+
edit_object_path # /products/:id/edit
|
138
|
+
new_object_path # /products/new
|
139
|
+
collection_path # /products
|
140
|
+
|
141
|
+
parent_path # /categories/:parent_id
|
142
|
+
edit_parent_path # /categories/:parent_id/edit
|
143
|
+
parent_collection_path # /categories
|
144
|
+
new_parent_path # /categories/new
|
145
|
+
|
146
|
+
sibling_path(sibling) # /sibling_name/:id
|
147
|
+
edit_sibling_path(sibling) # /sibling_name/:id/edit
|
148
|
+
new_sibling_path(:sibling_name) # /sibling_name/new
|
149
|
+
sibling_collection_path(:sibling_name) # /sibling_name
|
150
|
+
|
151
|
+
child_path(child) # /products/:product_id/child_name/:id
|
152
|
+
edit_child_path(child) # /products/:product_id/child_name/:id/edit
|
153
|
+
new_child_path(:child_name) # /products/:product_id/child_name/new
|
154
|
+
child_collection_path(:child_name) # /products/:product_id/child_name
|
155
|
+
|
156
|
+
Notice you have the edit_* paths. The above paths are implemented in a very flexible manner. So you are not limited them, you can call your custom actions too:
|
157
|
+
|
158
|
+
map.resources :products, :member => [:approve], :collection => [:approve_all]
|
159
|
+
approve_object_path # /products/:id/approve
|
160
|
+
approve_all_collection_path # /products/approve
|
161
|
+
|
162
|
+
The above example is probably not the best one, but you get the point.
|
163
|
+
|
164
|
+
Lastly, all of the above can end with _url instead of _path. <b>See docs for a complete list of available methods.</b>
|
165
|
+
|
166
|
+
|
167
|
+
Copyright (c) 2008 {Ben Johnson of Binary Logic}[http://www.binarylogic.com], released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "resourcelogic"
|
8
|
+
gem.summary = "Removes the need to namespace controllers by adding context and relative url functions among other things."
|
9
|
+
gem.email = "bjohnson@binarylogic.com"
|
10
|
+
gem.homepage = "http://github.com/binarylogic/resourcelogic"
|
11
|
+
gem.authors = ["Ben Johnson of Binary Logic"]
|
12
|
+
gem.rubyforge_project = "resourcelogic"
|
13
|
+
gem.add_dependency "activesupport"
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :test
|
40
|
+
|
41
|
+
begin
|
42
|
+
require 'rake/contrib/sshpublisher'
|
43
|
+
namespace :rubyforge do
|
44
|
+
desc "Release gem to RubyForge"
|
45
|
+
task :release => ["rubyforge:release:gem"]
|
46
|
+
end
|
47
|
+
rescue LoadError
|
48
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
49
|
+
end
|
data/VERSION.yml
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "resourcelogic"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/resourcelogic/accessors"
|
2
|
+
require File.dirname(__FILE__) + "/resourcelogic/action_options"
|
3
|
+
require File.dirname(__FILE__) + "/resourcelogic/actions"
|
4
|
+
require File.dirname(__FILE__) + "/resourcelogic/aliases"
|
5
|
+
require File.dirname(__FILE__) + "/resourcelogic/child"
|
6
|
+
require File.dirname(__FILE__) + "/resourcelogic/context"
|
7
|
+
require File.dirname(__FILE__) + "/resourcelogic/failable_action_options"
|
8
|
+
require File.dirname(__FILE__) + "/resourcelogic/parent"
|
9
|
+
require File.dirname(__FILE__) + "/resourcelogic/response_collector"
|
10
|
+
require File.dirname(__FILE__) + "/resourcelogic/scope"
|
11
|
+
require File.dirname(__FILE__) + "/resourcelogic/self"
|
12
|
+
require File.dirname(__FILE__) + "/resourcelogic/sibling"
|
13
|
+
require File.dirname(__FILE__) + "/resourcelogic/singleton"
|
14
|
+
require File.dirname(__FILE__) + "/resourcelogic/sub_views"
|
15
|
+
require File.dirname(__FILE__) + "/resourcelogic/urligence"
|
16
|
+
require File.dirname(__FILE__) + "/resourcelogic/base"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Resourcelogic # :nodoc:
|
2
|
+
module Accessors # :nodoc:
|
3
|
+
private
|
4
|
+
def block_accessor(*accessors)
|
5
|
+
accessors.each do |block_accessor|
|
6
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
7
|
+
|
8
|
+
def #{block_accessor}(*args, &block)
|
9
|
+
unless args.empty? && block.nil?
|
10
|
+
args.push block if block_given?
|
11
|
+
@#{block_accessor} = [args].flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
@#{block_accessor}
|
15
|
+
end
|
16
|
+
|
17
|
+
end_eval
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def scoping_reader(*accessor_names)
|
22
|
+
accessor_names.each do |accessor_name|
|
23
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
24
|
+
def #{accessor_name}(&block)
|
25
|
+
@#{accessor_name}.instance_eval &block if block_given?
|
26
|
+
@#{accessor_name}
|
27
|
+
end
|
28
|
+
end_eval
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def class_scoping_reader(accessor_name, start_value)
|
33
|
+
write_inheritable_attribute accessor_name, start_value
|
34
|
+
|
35
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
36
|
+
def self.#{accessor_name}(context = :root, &block)
|
37
|
+
read_inheritable_attribute(:#{accessor_name}).instance_eval(&block) if block_given?
|
38
|
+
read_inheritable_attribute(:#{accessor_name})
|
39
|
+
end
|
40
|
+
end_eval
|
41
|
+
end
|
42
|
+
|
43
|
+
def reader_writer(accessor_name)
|
44
|
+
class_eval <<-"end_eval", __FILE__, __LINE__
|
45
|
+
def #{accessor_name}(*args, &block)
|
46
|
+
args << block unless block.nil?
|
47
|
+
@#{accessor_name} = args.first unless args.empty?
|
48
|
+
@#{accessor_name}
|
49
|
+
end
|
50
|
+
end_eval
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
class ActionOptions
|
3
|
+
extend Resourcelogic::Accessors
|
4
|
+
|
5
|
+
reader_writer :flash
|
6
|
+
reader_writer :flash_now
|
7
|
+
|
8
|
+
block_accessor :after, :before
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@collector = Resourcelogic::ResponseCollector.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def response(*args, &block)
|
15
|
+
if !args.empty? || block_given?
|
16
|
+
@collector.clear
|
17
|
+
args.flatten.each { |symbol| @collector.send(symbol) }
|
18
|
+
block.call(@collector) if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
@collector.responses
|
22
|
+
end
|
23
|
+
alias_method :respond_to, :response
|
24
|
+
alias_method :responds_to, :response
|
25
|
+
|
26
|
+
def wants
|
27
|
+
@collector
|
28
|
+
end
|
29
|
+
|
30
|
+
def dup
|
31
|
+
returning self.class.new do |duplicate|
|
32
|
+
duplicate.instance_variable_set(:@collector, wants.dup)
|
33
|
+
duplicate.instance_variable_set(:@before, before.dup) unless before.nil?
|
34
|
+
duplicate.instance_variable_set(:@after, after.dup) unless after.nil?
|
35
|
+
duplicate.instance_variable_set(:@flash, flash.dup) unless flash.nil?
|
36
|
+
duplicate.instance_variable_set(:@flash_now, flash_now.dup) unless flash_now.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
module Actions
|
3
|
+
ACTIONS = [:index, :show, :new_action, :create, :edit, :update, :destroy].freeze
|
4
|
+
FAILABLE_ACTIONS = ACTIONS - [:index, :new_action, :edit, :destroy].freeze
|
5
|
+
|
6
|
+
def self.included(klass)
|
7
|
+
klass.class_eval do
|
8
|
+
ACTIONS.each do |action|
|
9
|
+
class_scoping_reader action, FAILABLE_ACTIONS.include?(action) ? FailableActionOptions.new : ActionOptions.new
|
10
|
+
end
|
11
|
+
class_scoping_reader :context, ContextOptions.new
|
12
|
+
add_acts_as_resource_module(Methods)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Methods
|
17
|
+
def new
|
18
|
+
load_object
|
19
|
+
before :new_action
|
20
|
+
response_for :new_action
|
21
|
+
end
|
22
|
+
|
23
|
+
def create
|
24
|
+
load_object
|
25
|
+
before :create
|
26
|
+
if object.save
|
27
|
+
after :create
|
28
|
+
set_flash :create
|
29
|
+
response_for :create
|
30
|
+
else
|
31
|
+
after :create_fails
|
32
|
+
set_flash :create_fails
|
33
|
+
response_for :create_fails
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def edit
|
38
|
+
load_object
|
39
|
+
before :edit
|
40
|
+
response_for :edit
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
load_object
|
45
|
+
object.attributes = attributes
|
46
|
+
before :update
|
47
|
+
if object.save
|
48
|
+
after :update
|
49
|
+
set_flash :update
|
50
|
+
response_for :update
|
51
|
+
else
|
52
|
+
after :update_fails
|
53
|
+
set_flash :update_fails
|
54
|
+
response_for :update_fails
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy
|
59
|
+
load_object
|
60
|
+
before :destroy
|
61
|
+
object.destroy
|
62
|
+
after :destroy
|
63
|
+
set_flash :destroy
|
64
|
+
response_for :destroy
|
65
|
+
end
|
66
|
+
|
67
|
+
def show
|
68
|
+
load_object
|
69
|
+
before :show
|
70
|
+
response_for :show
|
71
|
+
rescue ActiveRecord::RecordNotFound
|
72
|
+
response_for :show_fails
|
73
|
+
end
|
74
|
+
|
75
|
+
def index
|
76
|
+
load_collection
|
77
|
+
before :index
|
78
|
+
response_for :index
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
# Used to actually pass the responses along to the controller's respond_to method.
|
83
|
+
#
|
84
|
+
def response_for(action)
|
85
|
+
begin
|
86
|
+
respond_to do |wants|
|
87
|
+
options_for(action).response.each do |method, block|
|
88
|
+
if block.nil?
|
89
|
+
wants.send(method)
|
90
|
+
else
|
91
|
+
wants.send(method) { instance_eval(&block) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
rescue ActionController::DoubleRenderError
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Calls the after callbacks for the action, if one is present.
|
100
|
+
#
|
101
|
+
def after(action)
|
102
|
+
invoke_callbacks *options_for(action).after
|
103
|
+
end
|
104
|
+
|
105
|
+
# Calls the before block for the action, if one is present.
|
106
|
+
#
|
107
|
+
def before(action)
|
108
|
+
invoke_callbacks *self.class.send(action).before
|
109
|
+
end
|
110
|
+
|
111
|
+
# Sets the flash and flash_now for the action, if it is present.
|
112
|
+
#
|
113
|
+
def set_flash(action)
|
114
|
+
set_normal_flash(action)
|
115
|
+
set_flash_now(action)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Sets the regular flash (i.e. flash[:notice] = '...')
|
119
|
+
#
|
120
|
+
def set_normal_flash(action)
|
121
|
+
if f = options_for(action).flash
|
122
|
+
flash[:notice] = f.is_a?(Proc) ? instance_eval(&f) : options_for(action).flash
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Sets the flash.now (i.e. flash.now[:notice] = '...')
|
127
|
+
#
|
128
|
+
def set_flash_now(action)
|
129
|
+
if f = options_for(action).flash_now
|
130
|
+
flash.now[:notice] = f.is_a?(Proc) ? instance_eval(&f) : options_for(action).flash_now
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns the options for an action, which is a symbol.
|
135
|
+
#
|
136
|
+
# Manages splitting things like :create_fails.
|
137
|
+
#
|
138
|
+
def options_for(action)
|
139
|
+
action = action == :new_action ? [action] : "#{action}".split('_').map(&:to_sym)
|
140
|
+
options = self.class.send(action.first)
|
141
|
+
options = options.send(action.last == :fails ? :fails : :success) if Resourcelogic::Actions::FAILABLE_ACTIONS.include? action.first
|
142
|
+
|
143
|
+
options
|
144
|
+
end
|
145
|
+
|
146
|
+
def invoke_callbacks(*callbacks)
|
147
|
+
unless callbacks.empty?
|
148
|
+
callbacks.select { |callback| callback.is_a? Symbol }.each { |symbol| send(symbol) }
|
149
|
+
|
150
|
+
block = callbacks.detect { |callback| callback.is_a? Proc }
|
151
|
+
instance_eval &block unless block.nil?
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def load_parent
|
156
|
+
instance_variable_set "@#{parent_model_name}", parent_object if parent?
|
157
|
+
end
|
158
|
+
|
159
|
+
# Used internally to load the member object in to an instance variable @#{model_name} (i.e. @post)
|
160
|
+
#
|
161
|
+
def load_object
|
162
|
+
load_parent
|
163
|
+
instance_variable_set "@#{object_name}", object
|
164
|
+
end
|
165
|
+
|
166
|
+
# Used internally to load the collection in to an instance variable @#{model_name.pluralize} (i.e. @posts)
|
167
|
+
#
|
168
|
+
def load_collection
|
169
|
+
load_parent
|
170
|
+
instance_variable_set "@#{object_name.to_s.pluralize}", collection
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|