Class: Resourceful::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/resourceful/builder.rb

Overview

The Maker#make_resourceful block is evaluated in the context of an instance of this class. It provides various methods for customizing the behavior of the actions built by make_resourceful.

All instance methods of this class are available in the make_resourceful block.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(kontroller) ⇒ Builder

The constructor is only meant to be called internally.

This takes the klass (class object) of a controller and constructs a Builder ready to apply the make_resourceful additions to the controller.



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/resourceful/builder.rb', line 21

def initialize(kontroller)
  @controller       = kontroller
  @inherited        = !kontroller.resourceful_responses.blank?
  @action_module    = Resourceful::Default::Actions.dup
  @ok_actions       = []
  @callbacks        = {:before => {}, :after => {}}
  @responses        = {}
  @publish          = {}
  @parents          = []
  @shallow_parent   = nil
  @custom_member_actions = []
  @custom_collection_actions = []
end

Instance Attribute Details

#controllerObject

The klass of the controller on which the builder is working.



14
15
16
# File 'lib/resourceful/builder.rb', line 14

def controller
  @controller
end

Instance Method Details

#actions(*available_actions) ⇒ Object Also known as: build

:call-seq:

actions(*available_actions)
actions :all

Adds the default RESTful actions to the controller.

If the only argument is :all, adds all the actions listed in Resourceful::ACTIONS (or Resourceful::SINGULAR_ACTIONS for a singular controller).

Otherwise, this adds all actions whose names were passed as arguments.

For example:

actions :show, :new, :create

This adds the show, new, and create actions to the controller.

The available actions are defined in Default::Actions.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/resourceful/builder.rb', line 94

def actions(*available_actions)
  # FIXME HACK
  # made all methods private, so plural?, too.
  # Did not want to make an exception for that and i do not like it to
  # come up on actions_methods.
  # TODO: maybe we can define plural? as class_method
  if available_actions.first == :all
    if controller.respond_to?(:new_without_capture)
      available_actions = controller.new_without_capture.send(:plural?) ? ACTIONS : SINGULAR_ACTIONS
    else
      available_actions = controller.new.send(:plural?) ? ACTIONS : SINGULAR_ACTIONS
    end
  end

  available_actions.each { |action| @ok_actions << action.to_sym }
end

#after(*events, &block) ⇒ Object

:call-seq:

after(*events) { ... }

Sets up a block of code to run after one or more events.

There are two sorts of after events. :create, :update, and :destroy are run after their respective database operations have been completed successfully. :create_fails, :update_fails, and :destroy_fails, on the other hand, are run after the database operations fail.

after events are run after the database operations but before any responses.

For example:

after :create_fails, :update_fails do
  current_object.password = nil
end

This will nillify the password of the current object if the object creation/modification failed.



194
195
196
# File 'lib/resourceful/builder.rb', line 194

def after(*events, &block)
  add_callback :after, *events, &block
end

#applyObject

This method is only meant to be called internally.

Applies all the changes that have been declared via the instance methods of this Builder to the kontroller passed in to the constructor.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/resourceful/builder.rb', line 40

def apply
  apply_publish

  kontroller = @controller
  
  Resourceful::ACTIONS.each do |action_named|
    # See if this is a method listed by #actions
    unless @ok_actions.include? action_named
      # If its not listed, then remove the method
      # No one can hit it... if its DEAD!
      @action_module.send :remove_method, action_named
    end
  end

  kontroller.hidden_actions.reject! &@ok_actions.method(:include?)
  kontroller.send :include, @action_module
  
  merged_callbacks = kontroller.resourceful_callbacks.merge @callbacks
  merged_responses = kontroller.resourceful_responses.merge @responses
  
  kontroller.resourceful_callbacks = merged_callbacks
  kontroller.resourceful_responses = merged_responses
  kontroller.made_resourceful = true

  kontroller.parents = @parents
  kontroller.shallow_parent = @shallow_parent
  kontroller.model_namespace = @model_namespace
  kontroller.before_filter :load_object, :only => (@ok_actions & SINGULAR_PRELOADED_ACTIONS) + @custom_member_actions
  kontroller.before_filter :load_objects, :only => (@ok_actions & PLURAL_ACTIONS) + @custom_collection_actions
  kontroller.before_filter :load_parent_object, :only => @ok_actions + @custom_member_actions + @custom_collection_actions
end

#before(*events, &block) ⇒ Object

:call-seq:

before(*events) { ... }

Sets up a block of code to run before one or more events.

All the default actions can be used as before events: :index, :show, :create, :update, :new, :edit, and :destroy.

before events are run after any objects are loaded, but before any database operations or responses.

For example:

before :show, :edit do
  @page_title = current_object.title
end

This will set the @page_title variable to the current object’s title for the show and edit actions.

Successive before blocks for the same action will be chained and executed in order when the event occurs.

For example:

before :show, :edit do
  @page_title = current_object.title
end

before :show do
  @side_bar = true
end

These before blocks will both be executed for the show action and in the same order as they were defined.



166
167
168
# File 'lib/resourceful/builder.rb', line 166

def before(*events, &block)
  add_callback :before, *events, &block
end

#belongs_to(*parents) ⇒ Object

Specifies parent resources for the current resource. Each of these parents will be loaded automatically if the proper id parameter is given. For example,

# cake_controller.rb
belongs_to :baker, :customer

Then on GET /bakers/12/cakes,

params[:baker_id] #=> 12
parent?           #=> true
parent_name       #=> "baker"
parent_model      #=> Baker
parent_object     #=> Baker.find(12)
current_objects   #=> Baker.find(12).cakes


364
365
366
367
368
369
370
371
372
# File 'lib/resourceful/builder.rb', line 364

def belongs_to(*parents)
  options = parents.extract_options!
  @parents = parents.map(&:to_s)
  if options[:shallow]
    options[:shallow] = options[:shallow].to_s
    raise ArgumentError, ":shallow needs the name of a parent resource" unless @parents.include? options[:shallow]
    @shallow_parent = options[:shallow]
  end
end

#collection_actions(*available_actions) ⇒ Object

:call-seq:

collection_actions(*available_actions)

Registers custom collection actions which will use the load_objects before_filter. These actions are not created, but merely registered for filtering.



126
127
128
# File 'lib/resourceful/builder.rb', line 126

def collection_actions(*available_actions)
  available_actions.each { |action| @custom_collection_actions << action.to_sym }
end

#inherited?Boolean

This method is only meant to be called internally.

Returns whether or not the Builder’s controller inherits make_resourceful settings from a parent controller.

Returns:

  • (Boolean)


385
386
387
# File 'lib/resourceful/builder.rb', line 385

def inherited?
  @inherited
end

#member_actions(*available_actions) ⇒ Object

:call-seq:

member_actions(*available_actions)

Registers custom member actions which will use the load_object before_filter. These actions are not created, but merely registered for filtering.



117
118
119
# File 'lib/resourceful/builder.rb', line 117

def member_actions(*available_actions)
  available_actions.each { |action| @custom_member_actions << action.to_sym }
end

#model_namespace(ns) ⇒ Object

Specifies a namespace for the resource model. It can be given as a Module::NameSpace, ‘Module::NameSpace’ (in a string), or ‘module/name_space’ (underscored form).



377
378
379
# File 'lib/resourceful/builder.rb', line 377

def model_namespace(ns)
  @model_namespace = ns.to_s.camelize
end

#publish(*formats) ⇒ Object

:call-seq:

publish *formats, options = {}, :attributes => [ ... ]

publish allows you to easily expose information about resourcess in a variety of formats. The formats parameter is a list of formats in which to publish the resources. The formats supported by default are xml, yaml, and json, but other formats may be added by defining to_format methods for the Array and Hash classes and registering the mime type with Rails’ Mime::Type.register. See Resourceful::Serialize for more details..

The :attributes option is mandatory. It takes an array of attributes (as symbols) to make public. These attributes can refer to any method on current_object; they aren’t limited to database fields. For example:

# posts_controller.rb
publish :yaml, :attributes => [:title, :created_at, :rendered_content]

Then GET /posts/12.yaml would render

--- 
post: 
  title: Cool Stuff
  rendered_content: |-
    <p>This is a post.</p>
    <p>It's about <strong>really</strong> cool stuff.</p>
  created_at: 2007-04-28 04:32:08 -07:00

The :attributes array can even contain attributes that are themselves models. In this case, you must use a hash to specify their attributes as well. For example:

# person_controller.rb
publish :xml, :json, :attributes => [
  :name, :favorite_color, {
  :pet_cat => [:name, :breed],
  :hat => [:type]
}]

Then GET /people/18.xml would render

<?xml version="1.0" encoding="UTF-8"?>
<person>
  <name>Nathan</name>
  <favorite-color>blue</favorite_color>
  <pet-cat>
    <name>Jasmine</name>
    <breed>panther</breed>
  </pet-cat>
  <hat>
    <type>top</type>
  </hat>
</person>

publish will also allow the index action to render lists of objects. An example would be too big, but play with it a little on your own to see.

publish takes only one optional option: only. This specifies which action to publish the resources for. By default, they’re published for both show and index. For example:

# cats_controller.rb
publish :json, :only => :index, :attributes => [:name, :breed]

Then GET /cats.json would work, but GET /cats/294.json would fail.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/resourceful/builder.rb', line 329

def publish(*formats)
  options = {
    :only => [:show, :index]
  }.merge(Hash === formats.last ? formats.pop : {})
  raise "Must specify :attributes option" unless options[:attributes]
  
  Array(options.delete(:only)).each do |action|
    @publish[action] ||= []
    formats.each do |format|
      format = format.to_sym
      @publish[action] << [format, proc do
        render_action = [:json, :xml].include?(format) ? format : :text
        render render_action => (plural_action? ? current_objects : current_object).serialize(format, options)
      end]
    end
  end
end

#response_for(*actions, &block) ⇒ Object

:call-seq:

response_for(*actions) { ... }
response_for(*actions) { |format| ... }

Sets up a block of code to run instead of the default responses for one or more events.

If the block takes a format parameter, it has the same semantics as Rails’ respond_to method. Various format methods are called on the format object with blocks that say what to do for each format. For example:

response_for :index do |format|
  format.html
  format.atom do
    headers['Content-Type'] = 'application/atom+xml; charset=utf-8'
    render :action => 'atom', :layout => false
  end
end

This doesn’t do anything special for the HTML other than ensure that the proper view will be rendered, but for ATOM it sets the proper content type and renders the atom template.

If you only need to set the HTML response, you can omit the format parameter. For example:

response_for :new do
  render :action => 'edit'
end

This is the same as

response_for :new do |format|
  format.html { render :action => 'edit' }
end

The default responses are defined by Default::Responses.included.



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/resourceful/builder.rb', line 240

def response_for(*actions, &block)
  raise "Must specify one or more actions for response_for." if actions.empty?

  if block.arity < 1
    response_for(*actions) do |format|
      format.html(&block)
    end
  else
    response = Response.new
    block.call response

    actions.each do |action|
      @responses[action.to_sym] = response.formats
    end
  end
end