views:

51

answers:

0

My goal is to enable a user to be able to submit multiple NewsImages from a parent Blog form.

My Blog model looks like this:

# == Schema Information
# Schema version: 20091006171847
#
# Table name: blogs
#
#  id         :integer(4)    not null, primary key
#  title      :string(255)   
#  body       :text          
#  profile_id :integer(4)    
#  created_at :datetime      
#  updated_at :datetime      
#

class Blog < ActiveRecord::Base
  has_many :comments, :as => :commentable, :order => "created_at asc"
  has_many :news_images, :dependent => :destroy
  belongs_to :profile

  accepts_nested_attributes_for :news_images

  validates_presence_of :title, :body
  attr_immutable :id, :profile_id

  def after_create
    feed_item = FeedItem.create(:item => self)
    ([profile] + profile.friends + profile.followers).each{ |p| p.feed_items << feed_item }
  end


  def to_param
    "#{self.id}-#{title.to_safe_uri}"
  end

end

My NewsImage model looks like this:

class NewsImage < ActiveRecord::Base
  belongs_to :blog

  validates_each :blog do |news_image, attr, value|
    news_item.errors.add attr, "You are only limited to 5 images." if news_item.blog.news_items.size >= 5
  end

  has_attached_file :image, :styles => { :original => "1920x1080>", :square => "158x158#", :thumb => "386x155#", :slide_thumb => "165x67#"},
    :url => "/system/:attachment/:id/:style/:basename.:extension",
    :path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension"

  def validate
    dimensions = Paperclip::Geometry.from_file(self.image.queued_for_write[:original])
    self.errors.add(:image, "Please upload an image that is at least 476 pixels wide") if dimensions.width < 476
    self.errors.add(:image, "Please upload an image that is at least 319 pixels high") if dimensions.height < 319
  end
end

My Blog controller looks like this:

class BlogsController < ApplicationController
  skip_filter :login_required, :only => [:index, :show]
  prepend_before_filter :get_profile
  before_filter :setup


  def index
    if @p && @p == @profile && @p.blogs.empty?
      flash[:notice] = 'You have not create any blog posts.  Try creating one now.'
      redirect_to new_profile_blog_path(@p) and return
    end
    respond_to do |wants|
      wants.html {render}
      wants.rss {render :layout=>false}
    end
  end



  def create
    @blog = @p.blogs.build params[:blog]

    respond_to do |wants|
      if @blog.save
        wants.html do
          flash[:notice] = 'New blog post created.'
          redirect_to profile_blogs_path(@p)
        end
      else
        wants.html do
          flash.now[:error] = 'Failed to create a new blog post.'
          render :action => :new
        end
      end
    end
  end

  def show
    render
  end

  def edit
    render
  end

  def update
    respond_to do |wants|
      if @blog.update_attributes(params[:blog])
        wants.html do
          flash[:notice]='Blog post updated.'
          redirect_to profile_blogs_path(@p)
        end
      else
        wants.html do
          flash.now[:error]='Failed to update the blog post.'
          render :action => :edit
        end
      end
    end
  end

  def destroy
    @blog.destroy
    respond_to do |wants|
      wants.html do
        flash[:notice]='Blog post deleted.'
        redirect_to profile_blogs_path(@p)
      end
    end
  end

  protected

  def get_profile
    @profile = Profile[params[:profile_id]]
  end

  def setup
    @user = @profile.user
    @blogs = @profile.blogs.paginate(:page => @page, :per_page => @per_page)

    if params[:id]
      @blog = Blog[params[:id]]
    else
      @blog = Blog.new
      3.times {@blog.news_images.build }
    end
  end



  def allow_to
    super :owner, :all => true
    super :all, :only => [:index, :show]
  end

end

My form currently is this:

<%
#locals
blog ||= @blog
%>

<div id="blog">
  <% less_form_for [@profile, blog], :html => {:multipart => true} do |f| %>
    <%= f.text_field :title %>
    <%= f.text_area :body %>
    <% f.fields_for :news_images do |builder| %>
      <%= builder.label :caption %>
      <%= builder.text_field :caption %>
      <%= builder.label :image %>
      <%= builder.file_field :image %>

    <% end %>

    To include a youtube video use: [youtube: address_of_video]
    <div class="row button">
    <%= f.submit 'Save', :class=>'button' %>
    </div>
  <% end %>
</div>

I keep getting the following error:

ActionView::TemplateError (`@blog[news_images_attributes][0]' is not allowed as an instance variable name) on line #12 of app/views/blogs/_form.html.erb:

If I remove:

<%= builder.text_field :caption %>

the error goes away. In fact if i change the text field to a file field the error goes away.

I'm really confused here as I'm not sure why one form helper works fine and the other doesn't.

Thank you very much for looking =)

Update:

This is my less_form_builder.rb file:

class LessFormBuilder < ActionView::Helpers::FormBuilder
  include ActionView::Helpers::ActiveRecordHelper

  def method_missing *args
    options = args.extract_options!
    label = get_label '', options
    front(label) + super(*args) + back(label)
  end


  def wrap method, options = {}
    s = front(method, options) 
    s += yield if block_given?
    s += back(method, options)
  end

  # Generates a label
  #
  # If +options+ includes :for,
  # that is used as the :for of the label.  Otherwise,
  # "#{this form's object name}_#{method}" is used.
  #
  # If +options+ includes :label,
  # that value is used for the text of the label.  Otherwise,
  # "#{method titleized}: " is used.
  def label method, options = {}
    text = options.delete(:label) ||  "#{method.to_s.titleize}: "
    if options[:for]
      "<label for='#{options.delete(:for)}'>#{text}</label>"
    else
      #need to use InstanceTag to build the correct ID for :for
      ActionView::Helpers::InstanceTag.new(@object_name, method, self, @object).to_label_tag(text, options)
    end
  end

  def select method, options = {}
    front(method, options) + super + back(method, options)
  end

  def text_field method, options = {}
    front(method, options) + super + back(method, options)
  end

  def password_field method, options = {}
    front(method, options) + super + back(method, options)
  end

  def text_area method, options = {}
    front(method, options) + super + back(method, options)
  end

  def check_box method, options = {}
    front(method, options) + super + back(method, options)
  end


  def calendar_field method, options = {}
    expired = options.delete(:expired) || false
    if not expired; options.merge!(:class => 'calendar'); else; options.merge!(:disabled => true); end
    text_field method, options
  end

  def front method = '', options = {}
    "<div class='row clear'>#{label(method, options)}"
  end

  def back method = '', options = {}  
    "#{error_messages_on( object_name, method  ) unless method.blank?}
    <div class='clear'></div>
    </div>"
  end

end