9

I'm using an API instead of a database, so I'm not using ActiveRecord but ActiveModel (I mostly did like here: railscasts.com/episodes/219-active-model)

Thing is, when I try to edit an item (in my case a parking), the action of the form still remains the action of the create and not update.

so when I go on /parkings/2/edit to edit a parking, the form is still:

<form accept-charset="UTF-8" action="/parkings" class="form-horizontal" id="new_parking" method="post">

when it should be more like that with the put hidden field and the parkings/2 as the action:

<form accept-charset="UTF-8" action="/parkings/2" class="form-horizontal" id="edit_parking" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" />

Anybody knows where the method & action of the form_for is set according to the route? What I'm trying to do is be as close as if I was using ActiveRecord with a database.

Here is some code :

_form.html.erb

<%= form_for(@parking, :html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>

edit.html.erb & new.html.erb, simply has

<%= render 'form' %>

Controller

class ParkingsController < ApplicationController
  def index    
    @parkings = Parse.get("Parking")

    respond_to do |format|
      format.html
      format.json { render :json => @parking }
    end
  end

  def new
    @parking = Parking.new

    respond_to do |format|
      format.html
      format.json { render :json => @parking }
    end
  end

  def edit
    @parking = Parking.find(params[:id])

    respond_to do |format|
      format.html
      format.json { render :json => @parking }
    end
  end

  def create
    @parking = Parking.new(params[:parking])
    if (@parking.save)
       flash[:success] = "Parking was just added!"
       redirect_to :action => "new"
    else
      render :action => "new"
    end
  end

  def update
   # Testing
   parking = Parse.get("Parking", params[:id])
   parking.delete("updatedAt")
   parking["name"] = params[:parking][:name]
   parking.save

   redirect_to :action => "index"
  end

Model

class Parking
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  attr_accessor :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
  validates_presence_of :name, :address, :city, :longitude, :latitude

  @id = nil

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  def self.find(id)
    @id = id
    raw                       = Parse.get("Parking", @id.to_s)

    parking = Parking.new
    parking.name              = raw["name"]
    parking.address           = raw["address"]
    parking.city              = raw["city"]
    parking.longitude         = raw["location"]["longitude"]
    parking.latitude          = raw["location"]["latitude"]
    parking.contributor_name  = raw["contributorName"]
    parking.contributor_email = raw["contributorEmail"]

    return parking
  end

  def save
    if (!valid?)
      return false
    else
      parking = Parse::Object.new("Parking")

      data =
      {
        :longitude => longitude.to_f,
        :latitude  => latitude.to_f
      }

      point = Parse::GeoPoint.new(data)

      parking["location"]         = point
      parking["name"]             = name
      parking["address"]          = address
      parking["city"]             = city
      parking["contributorName"]  = contributor_name
      parking["contributorEmail"] = contributor_email

      if (parking.save)
        return true
      end
    end
  end

  def persisted?
    false
  end
end

Please note that the create is working and if I add the id of my parking in the form action="" using the Web Inspector or Firebug, and add :method => "put" in my form_for, my record successfully update.

The real problem here is really the form_for action & method who doesn't get updated when I'm editing a parking and remains like if I was adding a new one.

I'm still learning Rails, so sorry if some infos aren't clear!

Thank you!

--- SOLUTION ---

persisted? shouldn't only return false, and my model needed to define a method that returns the id of the object (so they can update the action="") so here's is my updated model:

class Parking
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  attr_accessor :objectId, :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
  validates_presence_of :name, :address, :city, :longitude, :latitude

  @id = nil

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  def self.find(id)
    raw                       = Parse.get("Parking", id.to_s)

    parking = Parking.new
    parking.objectId          = id
    parking.name              = raw["name"]
    parking.address           = raw["address"]
    parking.city              = raw["city"]
    parking.longitude         = raw["location"]["longitude"]
    parking.latitude          = raw["location"]["latitude"]
    parking.contributor_name  = raw["contributorName"]
    parking.contributor_email = raw["contributorEmail"]

    return parking
  end

  def save
    if (!valid?)
      return false
    else
      parking = Parse::Object.new("Parking")

      data =
      {
        :longitude => longitude.to_f,
        :latitude  => latitude.to_f
      }

      point = Parse::GeoPoint.new(data)

      parking["location"]         = point
      parking["name"]             = name
      parking["address"]          = address
      parking["city"]             = city
      parking["contributorName"]  = contributor_name
      parking["contributorEmail"] = contributor_email

      if (parking.save)
        return true
      end
    end
  end

  def update_attributes(aParking)
    parking = Parse.get("Parking", @id.to_s)
    parking.delete("updatedAt")

    parking["name"] = aParking["name"]
    parking.save

    return true
  end

  def destroy
    parking = Parse.get("Parking", @id)
    #parking.parse_delete
  end

  def id
    return self.objectId
  end

  def persisted?
    !(self.id.nil?)
  end
end
allaire
  • 5,995
  • 3
  • 41
  • 56

3 Answers3

6

I think your problem is in your model's persisted? method. Since it always returns false, Rails always thinks it's building a form for a newly created record, so it uses POST and submits to the collection URL.

You need some sort of logic in that method so that existing records return true and new records return false.

Brandan
  • 14,735
  • 3
  • 56
  • 71
  • Would you have any idea of that logic or hints? – allaire Mar 06 '12 at 13:54
  • I really can't help much more than to hazard a guess. Judging from your controller logic, it looks like a "fresh" `Parking` object won't have any of its attributes set. Perhaps your `persisted?` method could simply check for the existence of one or more of those attributes. It's application-specific, so it's up to you to figure out what it means. – Brandan Mar 06 '12 at 14:16
  • You may also need to set the id of the new object. – Yanhao Mar 06 '12 at 15:07
  • You were right! I just had to define a method id that returns the id of my object and that's it! I'll update my question! – allaire Mar 06 '12 at 15:08
1

Hi friend you can to tell the form builder which method to use.So try

<%= form_for(@parking, :method => ["new", "create"].include?(action_name) ? :post : :put,
    :html => { :class => "form-horizontal" }) do |f| %>
  ...
 <% end %>
Soundar Rathinasamy
  • 6,658
  • 6
  • 29
  • 47
-1

If you are not using ActiveRecord you should use 'form_tag' instead 'form_for'

Kleber S.
  • 8,110
  • 6
  • 43
  • 69
  • Why? ActiveModel is made for that no? Take a look at the railscast, they also use te form_for tag – allaire Mar 06 '12 at 13:53
  • Sorry, my mistake, I haven't see that you was using ActiveModel, thought was a form without ActiveModel and ActiveRecord. – Kleber S. Mar 06 '12 at 19:09