How to use a button to post in rails? - ruby-on-rails

So I'm trying to create a new "transaction" each time I click the button "Purchase", but doesn't seem to work. I get the error "undefined method `[]' for nil:NilClass".
<% #comics.each do |comic|%>
<ul>
<li><%= comic.title %> </li>
<li><%= comic.author %> </li>
<li><%= comic.year %> </li>
<li><%= comic.publisher %> </li>
<% if user_signed_in? %>
<%= button_to 'Purchase', {:controller => "transactions", :action => "create", :seller_id => comic.user_id, :buyer_id=> current_user.id, :comic_id => comic.id} , {:method=>:post} %>
<% end %>
</ul>
This is what you can find in the transactions controller:
def create
#my_transaction = Transaction.new(
buyer_id: params[:transaction][:buyer_id],
seller_id: params[:transaction][:seller_id],
comic_id: params[:transaction][:comic_id]
)
#my_transaction.save
redirect_to "/transactions/"
end
Do you have any idea why this might be happening?
Thanks!

There are a couple of ways you can debug this:
Look in the console logs to see what is being posted in the params hash.
add a puts statement at the top of the create statement to view what is in the params variable e.g.
controller
def create
puts params.inspect
end
I suspect you'll find that the params hash does not have a transaction key and the create method should be
def create
#my_transaction = Transaction.new(
buyer_id: params[:buyer_id],
seller_id: params[:seller_id],
comic_id: params[:comic_id]
)
end

The params[:transaction] is nil, you can see the sent parameters in the log (tail -f log/development.log if the server doesn't log). In your case you access the required data like params[:comic_id]
A few tips:
Never trust the input coming from the client:
:buyer_id=> current_user.id here an attacker could send any ID since the button_to helper will create a html form which is easily accessible using the devtool. Instead check it on the server side. Same goes for the seller_id, you can just fetch the related comic comic = Comic.find params[:comic_id].
You might want to consider an another API approach like POST /comics/1/buy this is a bit more restfull, and you could use the built in path helpers for that url like buy_comic_path(comic)

Related

How to add checkbox values (client ids) to a User attribute (User.clients)

I've been struggling to get this to work following a tutorial. I've got Users that have profiles, and a Client model that, through a 'many to many' join table, establishes the relationships between the user and the clients.
What I'm trying to do is create a list of check boxes generated from the list of clients in the DB that you can tick on or off, and then when you submit it, the user will have the relationship to those clients through the join table.
It's sort of working with static data as you can see below:
/profiles/show.html.erb
<% #clients.all.each do |client| %>
<li>
<%= check_box_tag "user[client_ids][]", client.id %>
<%= client.client_name %>
</li>
<% end %>
<%= link_to 'Add Clients', '../assign_clients/' + #profile.user.id.to_s , class: 'btn btn-default' %>
Routes
get 'assign_clients/:id', to: 'users#assign_clients'
And finally in my users_controller.erb
def assign_clients
#user = User.find(params[:id])
#user.client_ids = [1,2]
redirect_to :back, alert: 'Assigned Users'
end
Obviously it's just using hard coded values of 1 and 2. What I'm not sure how to do is wrap the checkboxes in the correct form tag/simple_form (which I am using), and then with the 'submit' button, have that do the 'assign_clients' action that passes through the values.
Thank you for any help.
What I'm not sure how to do is wrap the checkboxes in the correct form
tag/simple_form (which I am using), and then with the 'submit' button,
have that do the 'assign_clients' action that passes through the
values.
In order to create a form that will trigger the assign_clients method a route needs to be setup in your routes.rb file like the following:
resources :users do
patch 'assign_clients', to: 'users#assign_clients', as: 'assign_clients'
end
This sets up a route for a user that you can use the http patch method with (ie. UPDATE). The plan is to pass the client_ids to the users controller as params from the form. I gave it a path name so that we can reference it in the form as user_assign_clients_path(:user_id)
Now that we have the route set up...using the default rails form tags you can structure the form along the lines of this:
<%= form_for #user, url: user_assign_clients_path(#user) do |f| %>
<% #clients.each do |client| %>
<li>
<%= check_box_tag "user[client_ids][]", client.id, #user.clients.include?(client) %>
<%= client.client_name %>
</li>
<% end %>
<%= f.submit "Add Clients", class: "btn btn-default" %>
<% end %>
This will create a form allowing you to post the selected clients as an array of ids to the assign_clients method.
Finally, the assign_clients method can then retrieve the client_ids from the params hash (via params[:user][:client_ids] most likely) and update the user instance (retrieved using user_id from params hash also). You will probably have to add client_ids: [] to the end of your strong parameters list for user to whitelist it - but this essentially should behave like a typical update method.
def assign_clients
#user = User.find(params[:user_id])
#user.update(user_params)
redirect_to wherever_path
end
def user_params
params.require(:user).permit(
client_ids: []
)
end
You need to understand several basic concepts, let me explain to you:
on: member routing - in order to solve your issue directly, your route should be something like:
resources :users do
post '/assign_clients/:client_id', on: :member
end
so that other than user_id, the :client_id can be also passed in as a parameter. For the details, you can read about rails guides on routing.
For the checkbox way, you need nested_attributes - http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html. So that you can achieve what u need with a simple update call on users.
You can also implement a customised logic, with the client_ids passed in as parameters. In order to learn how forms & parameters work in rails, you can build a form, submit it, and see how it goes in the rails server log.
If anything is unclear, simply comment below & I'll try to update.

Truncate text and link to Read more

Come across a little stumbling block when linking to a post within my app. I am truncating the text of a post and providing a 'Read More' link. There are 2 areas where posts are viewed, one for everyone ( public) and also one for the admin user to edit/delete posts.
So in my public view I am doing this
<% #toppost.each do |t| %>
<div class="post-item">
<h2><%= t.title %></h2>
<ul id="newsHome">
<li><%= date_output(t.published_on) %></li>
<li><%= t.department.name %></li>
<li>by Admin</li>
</ul>
<% if t.comments.length > 350 %>
<p class="post-description"><%= truncate(t.comments, length: 350).html_safe %>
<%= link_to '...Read more', t %></p>
<% else %>
<p class="post-description"><%= t.comments.html_safe %></p>
<% end %>
</div>
<% end %>
However when clicking read more it takes me to the url
/posts/:id
which is actually the place where an admin user views posts, so at the moment a user will get redirected back to the home page as the posts controller has
before_filter :authenticate_user!
The place where all posts are viewed publicly are on specific news pages , for example
localhost:3000/tynewyddnews
localhost:3000/woodside
localhost:3000/sandpiper
localhost:3000/outreach
My question is how to link to that post at its location in the public part of the site.
Index action where top_posts method used(see below for method)
def index
#title = 'Home'
#toppost = Post.top_posts
end
top_posts method
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
Controller
def tynewyddnews
#title = 'Ty Newydd News'
tynewyddpost = Post.tynewydd_posts.reverse
tynewyddpost.pop
#tynewyddpost = tynewyddpost
#tynewyddpostlatest = Post.tynewydd_posts.first
end
Model
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
There are another 3 scopes for the other departments, all the same looking for the condition department name
Hopefully ive added enough info, anything else needed please ask
EDIT
Been thinking this through and not sure if im on right track, but for each post i need it to link to its appropriate news page in the public pages controller.
tynewyddnews
sandpipernews
outreachnews
Woodsidenews
So in my link_to i need to pass a route to the appropriate action depending upon the type of post.. so how to give each post a type and then link to that?
Thanks
Wow, ok, where do I begin.
You need a route that points to that controller action, for example
`get '/tynewyddnews' => 'news#tynewyddnews', :as => 'public_news' # gives you the route public_news_path
in your view
= link_to 'Read More', public_news_path
So ya, that should do it. BTW you can also use the .truncate() method. You pass in the length as an argument, and it adds the ... for you

Render a rails partial based on the id of an action

I'm building a small ecommerce site that sells a variety of mens and womens clothing. i would like to render a partial based on which taxonomy the user is in. For example, if the user is at mysite.com/t/women/pants I would like to render _women.html.erb, or, if the user is at mysite.com/t/men/shirts I would like to render _men.html.erb.
I have a Taxonomy model that has_many taxons, and the Taxon model has_many products.
In taxons_controller.rb I have:
def show
#taxon = Taxon.find_by_permalink(params[:id])
return unless #taxon
#taxonomy = Spree::Taxonomy.all
#taxon_title = Spree::Taxon.all
#searcher = Spree::Config.searcher_class.new(params.merge(:taxon => #taxon.id))
#searcher.current_user = try_spree_current_user
#searcher.current_currency = current_currency
#products = #searcher.retrieve_products
respond_with(#taxon)
end
And in taxons#show I have: (which I know is wrong)
<% #taxon_title.each do |title| %>
<% #taxonomy.each do |taxonomy| %>
<% if title.name == taxonomy.name %>
<%= render "spree/shared/#{title.name.downcase}" %>
<% end %>
<% end %>
<% end %>
When I go to mysite.com/t/women/long-sleeve the rails debugger displays :
controller: spree/taxons
action: show
id: women/long-sleeve
How do I access the id of the action im inside, so that in the controller/view I can do something like:
'if id equals 'women' render "spree/shared/#{title.name.downcase}"'
where title is the name of the taxonomy?
I imagine I need to find(params[:something] in the show action of the controller, but I'm a little unclear about params.
*
*
*
#beck03076 That's a great trick. Thank you very much. But it's still not working.
In my controller I put:
#taxon_id = Spree::Taxon.find(params[:id])
Then in the action I put:
render 'spree/shared/women' if #taxon_id == params[:id]
And when I load the page it says 'the page you were looking for doesn't exist'. My partial is in the correct directory. Is my syntax correct?
My params are:
{"controller"=>"spree/taxons", "action"=>"show", "id"=>"women/long-sleeve"}
Thanks again for your help!
Whenever you are unclear about params, just put the lines below in the action and execute the action.
p "****************************"
p params
p "****************************"
Now, goto the terminal in which you started your server.
Locate those two "*******" and everything thats in between them are params.
params is basically a ruby hash.
example:
params look like this, {:controller => "hello",:action => "bye", :id => 7, :others => "OK"}
In your controller to access the id, use params[:id].(=7)
to access others, use params[:others].(="OK")

how to delete/update several instances with one form in rails

I've just started programming in rails 3 days ago, learned ruby by the same time, and I'm having a hard time since yesterday figuring how to with one form, delete/update some of my instances, I only use one model "Task". Here's the code:
<%= form_for #task do |f| %>
<ul>
<% #tasks.each do |task| %>
<li id="task"><%= f.check_box :done %> <%= f.label :name %> </li>
<% end %>
<button onclick="doUpdate()">Mark Selected as done </button>
<%= button_to "Delete selected", :method => :delete %>
</ul>
<% end %>
Here's the controller:
def delete
#tasks = Task.find(:all, :conditions => ["task.done = ?", true])
#tasks.each do |task|
task.delete
end
#tasks = Task.all
end
My model have only 2 parameters. name:String and done:Boolean, I wan't to delete all the selected checkboxes. But this don't work for me
Thanks in advance.
The problem is, you are doing it wrong(and I'll tell you why). I could paste the code that would make it work but I'd rather explain, as you are probably doing it to learn.
Task.find(:all, :conditions => ["done = ?", true]) will return EVERYTHING in your database where done = true. You will be erasing everything that is marked as done in the DATABASE, not what were marked on the form. Task is your model, you can access the database by using find, where and other methods from activerecord(if activerecord doesn't sound natural to you, activerecord lets you get stuff from the database without the need of writing SQL queries).
What you really need to do in your controller is:
- You have to get what was sent from the form (check the documentation/web resources for the usage of param[] ).
- For every checkbox marked true, you erase a record. (you got the each part right, this is good!)
I don't think your view is right, I advise you to first be sure that the data that you receive is right(the params[]), then proceed to try to erase the record, or do whatever you want to do with it.
To "test" if your variables and code that is inside your controllers and models, use print #variable or something else(check rails docs how to debug).
I advise you use destroy instead of delete as other fellow stackoverflowers have said. Read the docs of destroy and delete.
Keep going :)
On a first glance, try using task.destroy instead of task.delete, and done instead of task.done See delete vs. destroy.
def destroy
#tasks = Task.find(:all, :conditions => ["done = ?", true])
#tasks.each do |task|
task.destroy
end
#tasks = Task.all
end

how to connect my model to my app

Hey all,(im a beginner in rails)
i've created a controller that look like that:
class HomeController < ApplicationController
def homepage
end
def showmsg
#postword = params[:p]
end
end
the showmsg view looks like that:
<%= #postword %>
and my homepage view looks like that:
<%= form_tag( {:controller => 'home', :action => 'showmsg'}, :method => "post") do %>
<%= text_field_tag(:p,#postword) %>
<%= submit_tag("post") %>
<% end %>
now i have a form that i can write something in it and it will show on the showmsg view.
i created a model with the param :posts with a :description "text" field too.
MY QUESTION is how do i implement the model in the code so any thing i write will be in a list with the things i wrote before, because now (obviously) anything if i write something its deleting the one i wrote before.
thank you all!
I would argue that you're approach is not very rail's like... so if you're learning rails... you're learning it wrong.
Make a Model. Call it "Message":
rails generate model Message content:string
remember to migrate (hopefully you have your databases setup properly):
rake db:migrate
Then in your controller, when you post, you can create message like this:
def create #instead of showmsg... 'create' is the standard name for this
Message.create(params[:message])
#messages = Message.all
end
This will create the message in the database, and then it will get all the messages out of the database and put them into #messages.
You need to edit your form so that it uses form_for. You need to pass it #message, which is an instance of Message.new that your first controller action created. You should call this new
In your create.erb.html file, you show all the messages like this:
<ul>
<% #messages.each do |message| %>
<li><%= message.content %></li>
<% end %>
</ul>
I actually wouldn't recommend showing all the messages in the create action - it should really happen in the index action and you should redirect... but we need to keep this simple. Just google this or watch some of Ryan's screencasts and you'll get it.
And you're done. This is the "Rails Way" to do things. It's best to learn it the way they want you to learn it.
I would also commend that you format your code properly by indenting, and start naming your methods to be real english. For example, showmsg is bad and show_message is a lot better.
If all of this is totally confusing, then just create a new project, and then type:
rails generate scaffold message content:string
It will basically build the application you want and a lot more. You can just read the code and see how they did it.
Hope it helps.
Your approach is not really rails like so some tweaks and fixes are needed. Suggestions: check rails approach to REST. The following code will work it is a little more rails like, but still not all the way there.
Generate a model
rails generate model Message postword:string
this will generate the model and create the migration necessary to create the table in the database.
Create the table
rake db:migrate
Define a post action
It will save the postword in the database. In your controller:
def create
#message = Message.create!(params[:message])
if #message.save
redirect_to "/home/showmsg"
else
render :action => "/home/homepage"
end
end
Create and instance of Message to use in your form
def homepage
#message = Message.new
end
Fix your form tag
<%= form_for #message, :url => "/home/create" do |f| %>
<%= f.label :postword %>
<%= f.text_field :postword %>
<%= f.submit "Create" %>
<% end %>
Now let's show the words in the showmsg page
In the controller select the postwords from the database:
def showmsg
#postwords = Message.all
end
Showing them: /showmsg.html.erb
<H1>postwords list</H1>
<ul>
<% #postwords.each do |p| %>
<li><%= p.postword %></li>
<% end %>
</ul>
Your routes.rb file will have this routes:
get "home/homepage"
get "home/showmsg"
post "home/create"
Define an attribute :new_text in a way similar to this:
class TheModel
# Virtual writer - everything assigned to this attribute
# will be added to self.text
#
def new_text=(v)
self.text += v.to_s
end
def new_text
"" # This is write-only attribute
end
end
Now, use the field 'new_text' in your form.
Of course, this is a very simple example. You should decide whether you want to add the content on every call to :new_text=, maybe some validation would help, the read accessor may need some care, and so on.
For some good guides which may help you start, see the site http://guides.rubyonrails.org/

Resources