rails: create posts using STI - ruby-on-rails

I am using single tablable inheritance(STI) to create different types of articles.
But now I have problem while creating articles.(I can do it only in console).
Here are my models
Article.rb
class Article < ActiveRecord::Base
attr_accessible :content, :title
validates :title, :presence => true
end
And TutorialArticle.rb
class TutorialArticle < Article
attr_accessible :author
validates :author, :presence => true
end
Here is my _form
<%= form_for(#article) do |f| %>
<%= f.hidden_field :type %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<%= render :partial => "edit" + f.object.type.downcase, :locals=>{:f=>f} %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But now I have a problem in article_controller.rb in create method
def create
# create the desired subtype based on the hidden field :type in the form
#article = Object.const_get(params[:article][:type]).new(params[:article])
if #article.save
flash[:notice] = "Successfully created post."
redirect_to #article
else
render :action => 'new'
end
end
Now when I fill in the form and press Create Article button, I get the folloiwing error
undefined method '[]' for nil:NilClass
I even tried to hard code to understand what is wrong
and if I try to change #article = Object.const_get(params[:article][:type]).new(params[:article])
to
#article = TutorialArticle.new(params[:article])
my create method doesn't save the article. it just redirects to create new article page.
Could you please help me to solve the problem?

Ok, the printing of params helped. Add this to your TutorialArticle model:
def self.model_name
return Article.model_name
end
This will make your forms pass article instead of tutorial_article, and the rest should work as expected. You'll need this override in all your other Article subclasses.

Related

Rails: File to Upload does not get passed from Form to the controller

This is the Form. All of the fields get passed (and saved) except the one containing the File.
I have checked that using the
render plain: params[:article].inspect method
giving out this (I have entered the value "n" for all fields):
{"country"=>"n", "region"=>"n", "town"=>"n", "street"=>"n", "company"=>"n", "title"=>"n", "content"=>"n"}
I am leaving out superfluous fields here to make the Form shorter:
<%= form_for(#article, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :country %>
<%= f.text_field :country, :required => true,
placeholder: "enter country" %>
</div>
<%= f.label :content %>
<%= f.text_field :content, :required => true, placeholder: "town..." %>
</div>
</div>
</div>
</div>
<span class="picture">
<%= form_for #article, html: { multipart: true } do |f| %>
<%= f.text_field :title %>
<%= fields_for :pictures do |ff| %>
<%= ff.file_field :picture %>
<% end %>
<% end %>
</div>
I have also tried the slight variation here, but no change
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
The create method at the Controller is like this:
def create
#article = current_user.articles.build(article_params)
if #article.save
flash[:success] = "Article created!"
redirect_to root_url
else
render 'articles/new'
end
end
and yes, the new method in the Articles controller, is like I was indicated by peers here:
def new
#article = current_user.articles.build
#article.pictures.build
end
The Article Model
class Article < ActiveRecord::Base
belongs_to :user
has_many :pictures
accepts_nested_attributes_for :pictures, allow_destroy: true
And the pictures Model
class Picture < ActiveRecord::Base
belongs_to :article
mount_uploader :picture, PictureUploader
end
Change your <%= fields_for :pictures do |ff| %> to <%= f.fields_for :pictures do |ff| %>

rails getting value from view to conroller

Having an issue getting a value from a form to the controller. I am using rails 4.0.
My view looks like this (new.html.erb)
<h1> POST A NEW LISTING </h>
<% if current_user.nil? %>
<h2>You must be logged in to view this page </h2>
<% else %>
<%= form_for [#user, #listing] do |f| %>
<%= f.label :title, 'Title' %> <br />
<%= f.text_field :title %>
<%= f.label :general_info, 'General Information' %> <br />
<%= f.text_area :general_info %>
<%= f.label :included, 'Included' %> <br />
<%= f.text_field :included %>
<%= f.label :length, 'Length' %> <br />
<%= f.text_field :length %>
<%= f.label :price, 'Price' %> <br />
<%= f.text_field :price %>
<%= fields_for #tagging do |u| %>
<%= u.label :tag, 'Tag' %> <br />
<%= u.text_field :tag %>
<% end %>
<%= f.submit "submit" %>
<% end %>
<% end %>
I am trying to add tags. I have 2 models to handle the tags:
models -> tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :listings, through: :taggings
end
models -> tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :listing
end
tags keep track of the tag names themselves, while taggings keeps track of the connection to the listings.
When a user submits the form they will type in a string tag such as: "exampletag". I then need to search my tag model to get the tag_id of that specific tag. If it exists I need to put the tag_id and listing_id into taggings. Currently I have the listing_id correct, but I am having a problem even accessing the :tag symbol from the form.
This is what I have so far. Not that currently :tag_id is hardcoded in because I cant get #current_tag to return the information I need.
listings_conroller.rb #create
def create
#user = User.find(current_user.id)
#listing = #user.listings.build(listing_params)
#save before we get the listing ID
if #listing.save
#current_tag = Tag.where(:name => params[:tag])
#taggings = Tagging.new(:tag_id => 1, :listing_id => #listing.id)
if #taggings.save
flash[:success] = "Success"
redirect_to root_path
else
render :action => 'new'
end
else
render :action => 'new'
end
end
I thought that #current_tag = Tag.where(:name => params[:tag]) would return the correct listing but it seems to be returning null when I submit the form with a name which is in the database.
got it!
Since tags is nested under taggings I needed to access the param as:
params[:tagging][:tag]
instead of params[:tag]

undefined method `comments' for nil:NilClass

I'm trying to create a way to have users comment on my posts. Currently I have All user posts showing up on my home page and then in the user profile only the current users posts. I would like to have it so that the comments appear only on the posts in the users profile. I tried to add a comment form in the user profile but I got an undefined method `comments' for nil:NilClass error.
My comments_controller looks like
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post])
#comment = #post.comments.create(params[:comment])
redirect_to post_path(#post)
end
I have a partial (_comment_form.html.erb) that I am rendering in the user profile which looks like
<h2>Add a comment:</h2>
<%= form_for ([#post, #post.comments.build]) do |f| %>
<div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My Comment model looks like
class Comment < ActiveRecord::Base
belongs_to :post
end
My Post model looks like
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :user
validates :content, :presence => true
validates :user_id, :presence => true
validates :user, :presence => true
validates :title, :presence => true
has_many :comments
default_scope :order => 'posts.created_at DESC'
end
My User Profile Looks like show.html.erb
<table class="profile" summary="Profile information">
<tr>
<td class="main">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
<% unless #user.posts.empty? %>
<table class="posts" summary="User posts">
<%= render #posts %>
<%= render 'comments/comment_form' %>
</table>
<% end %>
</td>
<td class="sidebar round">
<strong>Name</strong> <%= #user.name %><br />
<strong>URL</strong> <%= link_to user_path(#user), #user %><br />
<strong>Tasks</strong> <%= #user.posts.count %>
</td>
</tr>
</table>
It might be that you haven't initialized #post in the new method of your controller and it's being used as nil. Always construct an empty model for your new form if it's practical:
def new
#post = Post.new(params[:post])
end
#post = Post.find_by_id(params[:post_id])
Are you initializing the #post in the show action of your PostsController? That would be required because you are redirecting from the create action of your CommentsController.
<%= render #posts %>
This line should reference #post instead. Note the trailing s, compared to all the other references to it in your code.
Can you see log/development.log to see where the error occurred? It wasn't clear from the question. But judging from your code, there are two possible locations:
#comment = #post.comments.create(params[:comment])
here is unlikely because the last line of code is Post.find which will raise a RecordNotFound if the id is not found
<%= form_for ([#post, #post.comments.build]) do |f| %>
This is very likely, can you do a puts #post.inspect and check your development.log to see if that is null. Assuming that it is null, you need to instantiate a Post object wherever you rendered _comment_form.html.erb

Rails populate edit form for non-column attributes

I have the following form:
<% form_for(#account, :url => admin_accounts_path) do |f| %>
<%= f.error_messages %>
<%= render :partial => 'form', :locals => {:f => f} %>
<h2>Account Details</h2>
<% f.fields_for :customer do |customer_fields| %>
<p>
<%= customer_fields.label :company %><br />
<%= customer_fields.text_field :company %>
</p>
<p>
<%= customer_fields.label :first_name %><br />
<%= customer_fields.text_field :first_name %>
</p>
<p>
<%= customer_fields.label :last_name %><br />
<%= customer_fields.text_field :last_name %>
</p>
<p>
<%= customer_fields.label :phone %><br />
<%= customer_fields.text_field :phone %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
As well as
attr_accessor :customer
And I have a before_create method for the account model which does not store the customer_fields, but instead uses them to submit data to an API.. The only thing I store are in the form partial..
The problem I'm running into is that when a validation error gets thrown, the page renders the new action (expected) but none of the non-column attributes within the Account Detail form will show?
Any ideas as to how I can change this code around a bit to make this work me??
This same solution may be the help I need for the edit form, I have a getter for the data which it asks the API for, but without place a :value => "asdf" within each text box, it doesn't populate the fields either..
Okay, what you need to do is create a class to handle your customer with and without a Braintree gateway connection. First, create the class:
class Customer
attr_accessor :company, :first_name, :last_name, :phone, :gateway
def initialize gateway_id=nil
begin
#gateway = Braintree::Customer.find(gateway_id) unless gateway_id.nil?
rescue Braintree::NotFoundError
end
end
def company
#gateway.nil? ? #company : #gateway.company
end
# Implement the rest of the methods this way as well. You can even use
# meta-programming so that you don't repeat yourself.
end
You'll notice that calling Customer.new(id).company will work with and without an id or gateway, because if a gateway non-existent #company will be returned, and if a gateway is present the gateway's company will be returned.
Finally, hook this into your model:
class Account
def customer
#customer ||= Customer.new(self.gateway_customer_id)
end
def customer= h
#customer = Customer.new
#customer.company = h[:company]
...
#customer
end
end
You'll have to modify how you write code to the API so that you use customer.company instead of customer[:company] for example, but you can probably write a function within the Customer class to do this easily.
You'll have to modify your form to:
<% f.fields_for :customer, #account.customer do |customer_fields| %>

undefined method "_index_path" form_for problem

I'm trying to generate a form using the form_for helper in RoR but I am encountering what seems to be a routing error. Here are the relevant files:
models/equipment.rb
class Equipment < ActiveRecord::Base
attr_accessible :name, :tracking_number
validates :tracking_number, :presence => true,
:uniqueness => { :case_sensitive => true }
end
controllers/equipments_controllers.rb
class EquipmentsController < ApplicationController
def index
#equipments = Equipment.paginate(:page => params[:page])
end
def new
#equipment = Equipment.new
end
end
views/equipments/new.html.rb
<h1>Add an equipment</h1>
<%= form_for (#equipment) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :name %> <br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :tracking_number %><br />
<%= f.text_field :tracking_number %>
</div>
<%= f.submit "Add" %>
<% end %>
routes.rb
EquipmentTracking::Application.routes.draw do
root :to => "equipments#index"
resources :equipments
end
I don't see anything wrong but they output the following:
NoMethodError in Equipments#new
Showing /opt/ror/equipment_tracking/app/views/equipments/new.html.erb where line #2 raised:
undefined method `equipment_index_path' for #<#<Class:0xb6725a2c>:0xb6724640>
If I changed it to
<%= form_for (:equipment) do |f| %>
it seems to work ok. I'm also certain that the static variable #equipment is getting passed since
<%= #equipment %>
returns
#<Equipment:0xb685ece0>
I am at a loss here. I just did what I did while I was following the railstutorial.org book and I was able to finish the book.
I think your problem lies in your use of the word "equipments". If you open the Rails console run 'equipment'.pluralize you'll see that the plural of "equipment" is "equipment".
So I'd do a search through your project and replace any instance of "equipments" with "equipment" and I'd bet that would fix it.

Resources