Rails: checking modified fields - ruby-on-rails

First, I've generated scaffold called 'item'
I'd like to check which fields of the item are modified. and I've tried two possible attempts, those're not work tho.
First Attempt!
def edit
#item = Item.find(params[:id])
#item_before_update = #item.dup
end
def update
#item = Item.find(params[:id])
# compare #item_before_update and #item here, but #item_before_update is NIL !!!
end
Second Attempt!
I looked for the way passing data from view to controller and I couldn't.
edit.html.erb
<% #item_before_update = #item.dup %> # I thought #item_before_update can be read in update method of item controller. But NO.
<% params[:item_before_update] = #item.dup %> # And I also thought params[:item_before_update] can be read in update mothod of item controller. But AGAIN NO
<% form_for(#item) do |f| %>
# omitted
<% end %>
Please let me know how to solve this problem :(

Attributes that have changes that have not been persisted will respond true to changed?
#item.title_changed? #=> true
You can get an array of changed attributes by using changed
#item.changed #=> ['title']
For your #update action, you need to use attributes= to change the object, then you can use changed and changed? before persisting:
def update
#item = Item.find(params[:id])
#item.attributes = params[:item]
#item.changed #=> ['title']
... do stuff
#item.save
end

Related

Name attribute is coming up Nil?

I am trying to create a destination, but it keeps telling me in my browser that 'name' is nil when it redirects redirects to my 'show' view.
Error I receive
undefined method `name' for nil:NilClass
Here are my controller actions for new, create, and show:
def show
#destination = Destination.find_by(id: params[:id])
end
def new
#destination = Destination.new
end
def create
#destination = Destination.create(dest_params)
redirect_to user_destination_path(current_user, #destination.id )
end
private
def dest_params
params.require(:destination).permit(:name,:user_id)
end
My new form where I enter the name of the destination:
<h2>Add a destination</h2>
<div>
<%= form_for #destination do |f|%>
<%= f.label :name %>
<%= f.text_field :name %><br>
<%= f.submit %>
<% end %>
</div>
here is my read/show view:
<h3>Added destination</h3>
<div>
<p><%= #destination.name %></p>
</div>
Before all this I was getting missing required keys [:id] errors, but I seemed to fix that but for some reason I suspect that might have something to do with the issue I am having now. Let me know if you are able to spot the issue
Updated Error
No route matches {:action=>"show", :controller=>"destinations", :id=>nil, :user_id=>"1"}, missing required keys: [:id]
The main problem here is a total lack of error handling. You're not checking at all if the user provided valid input or if the record was even saved in your create method.
def create
#destination = Destination.create(dest_params)
redirect_to user_destination_path(current_user, #destination.id )
end
If the record is not saved for example due to a failed validation #destination.id is nil.
In your show method you're using find_by instead of find which just lets the error slide instead of raising a ActiveRecord::RecordNotFound error.
Your controller should actually look like:
class DestinationsController
def show
# will raise if the record is not found and return a 404 not found response
# instead of just exploding
#destination = Destination.find(params[:id])
end
def new
#destination = Destination.new
end
def create
# never just assume that the record is created unless you want
# to get kicked in the pants.
#destination = Destination.new(dest_params)
if #destination.save
# this route really does not need to be nested.
# see https://guides.rubyonrails.org/routing.html#shallow-nesting
redirect_to user_destination_path(current_user, #destination)
else
# re-render the form with errors
render :new
end
end
private
def dest_params
params.require(:destination).permit(:name,:user_id)
end
end

How to filter by params and user input in Rails

I am trying to display only the rows that belong to certain states in my application. I can do it the long way in Javascript, but I would prefer to better understand Rails and queries in the controller. I want to take the users to another page and then show them only that the companies in that state. It would be great to not have to link them to another page. Does anyone know how to do this?
Here is what I have in my controller
def vendors
#vendors = Collective.where(sort: 'Vendor').all
#vendors = #vendors.where(params[:state])
end
My route
get '/vendors/:state', to: 'collectives#vendors'
Then I use the stereotypical method to print a table in a html.erb file.
<% #vendors.each do |company| %>
<tr>
<td><%= company.name %></td>
<td><%= company.state %></td>
etc...
Should your controller code change the where as follows:
def vendors
#vendors = Collective.where(sort: 'Vendor').all
#vendors = #vendors.where(state: params[:state])
end
or better:
def vendors
#vendors = Collective.where(sort: 'Vendor', state: params[:state])
end
Using sessions instead of url params.
This is more or less what you can do, sorry if it is not completly working for your case, just to give an idea.
# view collectives/index (or whatever you have)
<%= form_tag (controller: :collectives, action: :set_status_filter, method: :post) do %>
<%= select_tag(:session_status_filter, options_for_select(#your_list_of_options_for_the_filter)) %>
<%= submit_tag "Set filter" %>
<% end %>
# collectives controller
def index # or whatever, this is the page containing the form and the list to show
#vendors = Collective.where(sort: 'Vendor').all
if session[:session_status_filter] == # etcetera
then #vendors = #vendors.where(state: session[:session_status_filter]) # for example
else # another option just in case, etcetera
end
end
def set_status_filter # this action is called by the form
session[:session_status_filter] = params[:session_status_filter]
respond_to do |format|
format.html { redirect_to *** the view where the form is placed ***, notice: 'the filter is set to: ....' + session[:session_status_filter] } # after the session variable is set the redirects goes to index which uses the session to filter records
end
end
params[:session_status_filter] is passed by the form to collectives#set_status_filter. The value is used to set the session variables. After that the action collectives#set_status_filter redirects to the index, or whatever page you placed the form and the list to show.

undefined method paypal_url in Rails

I have a piece of code in Rails,
def create
#registration = Registration.new(registration_params)
if #registration.save
redirect_to #registration.paypal_url(registration_path(#registration))
else
render :new
end
end
I took it from tutorial. But I need just in this line:
#registration.paypal_url(registration_path(#registration))
Now, about my own controller, feed_controller, where
def create
#feed = Feed.new(check_params)
end
In the view erb file I put:
#feed.paypal_url(feed_path(#feed))
In my feed.rb (model):
def paypal_url(return_path)
values = {
business: "merchant#gotealeaf.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
invoice: id,
amount: course.price,
item_name: course.name,
item_number: course.id,
quantity: '1'
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
Rake routes:
feed GET /:locale/feed(.:format) feed#index
feed#create POST /:locale/feed/create(.:format)
feed#new feed_new GET /:locale/feed/new(.:format)
feed#destroy feed_destroy GET /:locale/feed/destroy(.:format)
feed#edit feed_edit GET /:locale/feed/edit(.:format)
feed#update feed_update GET /:locale/feed/update(.:format)
But it prints the next error:
undefined method `paypal_url' for <#Feed::ActiveRecord_Relation:0x007fee24f5fc98>
How can I fix it? What is the problem?
UPDATE
def index
#current_user_is = current_user.email
session[:email] = #current_user_is
session[:id] = current_user.id
unless (current_user.member.present?)
#member = Member.new(:user_id => current_user.id)
#member.save()
redirect_to '/feed'
else
#new_feed = Feed.new
#feed = Feed.where(:member_id => current_user.member.id)
#category = Category.all
render 'home/uploads'
end
end
Simply use def self.paypal_url(return_path) instead of def paypal_url(return_path).
Explanation
You ran into your problem by defining a Class Method instead of an Instance Method, there's other posts discussing this.
The basic difference is, when defining:
def self.get_some_url
# code to return url of an instance
end
you can easily get the desired url of any objects, as in a view:
<% #feeds.each do |feed| %>
<%= feeds.get_some_url %>
<% end %>
Now calling Feed.get_some_url on the class would make no sense. Which url of the thousands would it call?
But there is a lot of use for class methods (where you define the method without self as you did)
def get_top_5
# code to return the top 5 most viewed feeds
end
Since this has nothing to do with a single instance, you define it for the entire Class. Leading to this call: Feed.get_top_5, which makes perfectly sense.
The second problem was not understanding the difference between where & find, this post will help you out with that.

Rails session variable (shopping cart)

I'm trying to save the items the customer can add to my shopping cart in a session variable.
Below is my index method in the cart controller. The ID of the item is saved in #idtemp. Next I create a new array session[:content]. Then I find the item with the ID saved in #idtemp and return it as an array, which is used in the view to display the table. The problem is, everytime an item is added and the index function is called, session[:contenc] is set to [], deleting the cart, which means that a newly added item overwrites the last one.
Now I want this fixed so that a newly added item is added to the array without overwriting the last one, but I dont know how. I tried to initialize the session variable outside of index but that didnt work somehow. I know this is piss easy but I am pretty exhausted and cant figure it out.
def index
#idtemp = session[:cart]
session[:content] = []
session[:content] = session[:content] << (Artikel.find([#idtemp]))
#cart = session[:content]
end
-----view:
<% #cart.each do |id,quantity| %>
...
...
If i was you i would store the cart and its contents in the database. Then all the session has to do is to remember either the cart id, or the id of the user, if they're logged in (once you've got the user you can get the cart). I'd set it up something like this (which you can adjust for your own schema - Artikle or whatever instead of Product)
class Cart
belongs_to :user
has_many :cart_products
class CartProduct
belongs_to :cart
belongs_to :product
#extra field - quantity
When the page loads, look to see if you have session[:cart_id]. if you do, you can load the cart, and its associated products if need be. If not, then create a cart object and set session[:cart_id] to that.
If you have a logged in user (ie session[:user_id] or somesuch) then you can set current_user to that user, and set their cart.
I would manage this via some protected methods in ApplicationHelper
#in application.rb
def current_user
if #current_user
return #current_user
elsif session[:user_id] && user = User.where(:id => session[:user_id]).first
#current_user = user
return #current_user
end
end
#there's a bit of repetition in here but it should give you the idea
def current_cart
unless #current_cart
if current_user
cart = current_user.cart || Cart.create(:user => current_user)
#current_cart = cart
elsif session[:cart_id]
#current_cart = Cart.where(:id => session[:cart_id]).first || Cart.create
else
#current_cart = Cart.create
end
end
return #current_cart
end
Now, whenever you want to refer to the current user, or the current cart, in your controller and view code, just say current_user or current_cart. The instance variables inside each method mean that if it's been set once in this request then it won't use the logic again - it will just use the one it already saved in the instance variable.
Ok, adapting your existing system, i would do it like this. We'll have one session variable, which is a hash storing the ids of Artikels and the quantity of each, with this structure: session[:cart] = {123 => 1, 456 => 2} where 123 and 456 are Artikel ids.
def add
session[:cart] ||= {}
session[:cart][params[:id]] ||= 0
session[:cart][params[:id]] += 1
end
def index
session[:cart] ||= {}
end
now in your view you can say
<% session[:cart].each do |artikel_id, quantity| %>
<% artikel = Artikel.where(:id => artikel_id).first %>
...
Here is what i dont understand:
def index
#idtemp = session[:cart] # #idtemp is now an ID e.g. '1'
session[:inhalt] = [] # session[:inhalt] is now an empty array []
addedarticle = Artikel.find(#idtemp) # addedarticle is an article e.g.:{ID=>'1',title=>'Mouse'}
session[:inhalt].concat([addedarticle]) # session[:inhalt] is now [{...}] where {...} = article
#cart = session[:inhalt] # #cart = [{...}]
end
This works. An item is added to the cart and #cart is an array containing this item, it can now be used in the view. The only thing that has to be done now is to make sure that the next added item wont override the old ond. You said i can simply replace session[:inhalt] = [] with session[:inhalt] ||= [],
however, doing this leads to an error:
NoMethodError in Cart#index
undefined method `title' for "#":String
<% #cart.each do |id| %>
<tr>
<td><%= image_tag id.Image_URL %></td>
<td>
<div class="title"><%= id.title %></div>
<br>
It seems to me that #cart is not properly "filled". Its supposed to be an array of articles (=hashes) e.g. [{id => 1, title ='mouse'}, {id => 2, title = 'whatever'}], so i can iterate over it with the each do method and display them. But according to the error it is trying to extract the title from a string. What could be the cause of that?
If I can fix this, I am done with the task. Btw, thanks for helping me out so far :)

best way to prevent each on a nil variable on rails 4

I have a query that is checking if a certain values exist in the DB and returns an array if they exist
#canEditTask = Accessor.where("accessor_id = ? AND access_right = ?", current_user, true)
The problem is that this can return nil
#taskEdit = #canEditTask
but my each method in my html still fails due to nil undefined methodeach' for nil:NilClass`
.
<% #taskEdit.each do |task| %>
<%= task.id %>
In that case what is the best way to prevent a nil from breaking my code?
Edit
Controller code
def index
#canEditTasks = Accessor.where("accessor_id = ? AND access_right = ?", current_user, true)
end
def show
#taskEdit = #canEditTasks
rescue ActiveRecord::RecordNotFound
render :file => 'public/404.html'
end
View code
<div>
<% #taskEdit.each do |task| %>
<%= task.id %>
<% end %>
</div>
You are setting #canEditTasks in your index method and trying to use it in your show method... if you are confused by this you should probably go back to the basics and read/watch some Rails tutorials (sorry if I'm missing something here...).
When you assign #taskEdit = #canEditTasks in the show action, #canEditTasks is nil, which means #taskEdit is also nil.
Actions are run one at a time, it seems your code is expecting index to run first, and then show would run after that. That isn't how Rails works by default. If you want to run some code that is shared between several actions, I would suggest a before_filter.
before_filter :set_can_edit_tasks
def index; end
def show
#taskEdit = #canEditTasks
rescue ActiveRecord::RecordNotFound
render :file => 'public/404.html'
end
private
def set_can_edit_tasks
#canEditTasks = Accessor.where("accessor_id = ? AND access_right = ?", current_user, true)
end
I would prefer to use a .to_a after the active record where. so no error when a nil array is looped ;) avoids condition in views.

Resources