Favouriting a picture in a Rails app - ruby-on-rails

I am trying to figure out how to execute a "favourite a picture" method in a Rails app, which I am very new to, going from a JS/Meteor background.
The point is I have a User, FavPic, Pic classes:
class User < ActiveRecord::Base
#some user oauth stuff would be here
has_many :fav_pics
has_many :pics_favorited,
class_name: 'Pic',
through: :fav_pics
end
class FavPic < ActiveRecord::Base
belongs_to :user
belongs_to :pic
end
class Pic < ActiveRecord::Base
has_many :fav_pics
has_many :fav_users,
class_name: 'User',
through: :fav_pics
end
and here's my template:
<% if current_user %>
<%= form_tag(root_path, :method => "get") do %>
<p>
<%= text_field_tag :username, params[:username] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<ul>
<% #mentions.each do |mention| %>
<li>
<%= mention.text %>
<div class="row">
<% mention.media.each do |media| %>
<div class="col-xs-3">
<%=image_tag(media.media_url, class:"img-responsive")%>
<i class="fa fa-star fa-2x"></i>
</div>
<% end %>
</div>
</li>
<% end %>
</ul>
<% else %>
<p>
Sign in to be able to use the app.
</p>
<% end %>
current_user is a user signed in through Twitter and #mentions is a list of tweets that has the username inputed in the form mentioned. media.media_url is a picture url that is associated with that tweet.
I am trying to get that link (or whatever way it's done in Rails) to add that media URL into the DB so I can list all the images at a separate URL.

You've got a choice: change the <a href="#"> into a form, or have your action link include the url encoded url as a parameter. Either way, you can still use AJAX to invoke the call to the server.
Your server would have an action on a controller that accepted the appropriate call to mark something as a favorite. Since you don't have your Controller code listed let's call it your PicController I'm also assuming your Pic class has a url property on it.
I highly recommend this primer: http://guides.rubyonrails.org/association_basics.html and the very basic http://guides.rubyonrails.org/getting_started.html which covers how to do action links and forms.
class PicController < ApplicationController
def favorite
pic = Pic.find(params[:url])
favorite = FavPic.new
favorite.pic = pic
favorite.user = current_user # however you get at this...
favorite.save
pic.fav_users.add(favorite)
pic.save
current_user.fav_pics.add(favorite)
current_user.save
end
end
The above code covers how to create the new FavPic instance and save it appropriately.

Putting together that
have a current_user, that want's to add a picture to her/his favourites
pictures are identified by the url that is also the media_url in your template
You need
an action to add the picture as a favourite, i.e. in the pics_controler
a route to this action, since it's a data changeing action, it should be a POST route
so in your config/routes.rb, to define a named route add
put 'pics/:url/avourite' => 'pics#favourite', as: 'add_favourite'
and in your template change the link to
<%= link_to add_favourite_url(url: media.media_url), class: 'fav-img', method: :put do %>
<i class="fa fa-star fa-2x"></i>
<% end %>
Then in the controller:
class PicController < ApplicationController
def favorite
if current_user.present?
pic = Pic.find(params[:url])
FavPic.create pic: pic, user: current_user
# user and pic automaically have this `FavPic` assigned
end
end
end

Related

User must exist in Rails 5

My form partial is as follows:
<%= form_with(model: guitar, local: true) do |form| % >
<% if guitar.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(guitar.errors.count, "error") %> prohibited this
guitar from being saved:</h2>
<ul>
<% guitar.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<div class="form-group">
<%= form.label :title %>
<%= form.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= form.submit "Create New Guitar Lesson", class: "btn btn-primary"
%>
</div>
<% end %>
When I try to create a new entry in a table via a form, an error message pops up 'user must exist', which is fine by me.
How do I auto insert (which file / section should I place the code in) current logged in user ID to the form in the background without having the user keying it in manually?
I've pushed the entire app up to the cloud at http://github.com/cheese1884/197451 –
Assuming your field in table is called user_id and you are using Devise.
You should insert the following in your form
<%= form.hidden_field :user_id, value: current_user.id %>
The user's id (taken from Devise's current_user) will be prepopulated in a hidden field that they won't be able to see.
As per doc mentioned of user model
app/models/guitar.rb
class Guitar < ApplicationRecord
belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
has_many :guitars
end
In rails 5 belongs_to association required by default
which means at the time of creation of each record of guitar user_id is required.
So here by at controller you can solve it by: -
In devise after sign_in there is a helper method which current_user which is current logged_in user
in guitars_controller.rb
def create
new_guitar_record = current_user.guitars.new(guitar_params)
if new_guitar_record.save
#guitar created successfully for current logged in user
else
#current_user.guitar.errors.full_messages
end
end
def guitar_params
params.require(:guitar).permit(:name, :description)
end
Please amend controllers/application_controller as you have messed up devise's current_user and your custom current_user
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
Note: - You can also pass current_user.id by hidden_field :user_id with guitar form but as per security concern it's not good, as user can invoke any user_id via browser.
There are many way to implement this, as i understand you have an user_id field in Guitar model.
Simple solution is need to attach user_id into Guitar object on create action.
In GuitarController go for create and add this line. .merge(user_id: current_user.id).
Remember User must be signed in to get current_user object.
Sample:
#g = Guitar.new(guitar_params.merge(user_id: current_user.id))
Edited
You have many bugs there, 1st of all you need to clean up your controllers.
ApplicationController: remove lines between 6-18. No need it, because Devise gem will provide you these features already.
GuitarsController:
def guitar_params
params.require(:guitar).permit(:name, :description)
end
// guitar view from
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
// models/user.rb
//add this line
has_many :guitars
If you want to sign in a user in the background, use the sign_in helper inside your controller's action:
sign_in(:user, user)

Can't Get Photos to Work in Rails

I am having all kinds of trouble doing a simple task, displaying a photo as part of a result set for each one. I'm pretty new to Rails in general, coming from another language where I can do this in seconds.
The #photo query finds 0 records even though multiple records for photos are in the DB matching the property ID.
I'm not too sure what I'm doing wrong here.
Here are my relevant files:
app/controllers/properties_controller.rb:
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
# should get the first positioned photo matching the results in the #properties query
#photos = Photo.where(:property_id => #properties.map(&:id)).order("position").first
end
# ......
end
app/models/property.rb:
class Property < ActiveRecord::Base
belongs_to :listing_type
belongs_to :property_type
belongs_to :status
has_many :photos
# ......
end
app/models/photo.rb:
class Photo < ActiveRecord::Base
mount_uploader :photoname, PhotoUploader
belongs_to :property
acts_as_list scope: :property_id
validates :photoname, presence: true
validates :property_id, presence: true
end
details.html.erb:
<% #properties.reverse_each do |property| %>
<div class="item col-md-4">
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(property) %>
</div>
# ......
<% end %>
Since you have a has_many realtion in the Property, you have to just access to the relation of the property to read all teh photoes:
photo = #property.photos.order("position").first
Well if you need to grab all the photoes of properties, use include to properties grab:
#properties = Property.includes(:photos).paginate(page: params[:page]).reverse
the include is needed to avoid N + 1 problem, then try replacing it with a first photo:
#photos = #properties.map { |pr| pr.photos.order("position").first }
Mpve reverse to controller, and to use #photos along with #properties use index:
<% #properties.each.with_index do |property, i| %>
#...
<%= image_tag(#photos[i]) %>
<- end >
NOTE that the code selecting an image pr.photos... be better moved to a decorator (see gem draper).
After a TON of help from #МалъСкрылевъ, which also had me learning a few new ways of "thinking" this in general, his answer led me to rethink what I was doing and go back to simply starting over and reconstructing what ended up being VERY basic. All I needed to do was make the photo query for the first photo in my loop over the properties. DUH! Here's what I did in case it helps some other poor new Rails developer!
properties_controller.rb
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
......
end
details.html.erb
<% #properties.each do |property| %>
<div class="item col-md-4">
<% #photo = Photo.where(property_id: property.id).order("position").first %>
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(#photo.photoname.medium) %>
</div>
......

Rails 4 problems with updating/deleting fields in a model with nested fields

I've been working on introducing a nested form in my app following Railscast episode 196 http://railscasts.com/episodes/196-nested-model-form-revised and the remastered version for rails 4 https://github.com/dnewkerk/nested-model-form.
Let's say we have a 1-to-many association between receipts and articles.
Here's how their models look like:
receipt.rb:
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: true, reject_if: :all_blank
belongs_to :shop
belongs_to :user
def display_name
self.name
end
end
article.rb:
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
end
Here's how the receipts_controller.rb looks like:
class ReceiptsController < ApplicationController
before_action :set_shop, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
#receipts = current_user.receipts
respond_with(#receipts)
end
def show
respond_with(#receipt)
end
def new
#receipt = Receipt.new
2.times do
#receipt.articles.build
end
respond_with(#receipt)
end
def edit
end
def create
#receipt = Receipt.new(receipt_params)
user_id = current_user.id
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
#receipt.user_id = user_id
#receipt.save
respond_with(#receipt)
end
def update
if #receipt.update(receipt_params)
redirect_to #receipt, notice: "Successfully updated receipt."
else
render :edit
end
end
def destroy
#receipt.destroy
respond_with(#receipt)
end
private
def set_shop
#receipt = Receipt.find(params[:id])
end
def receipt_params
params.require(:receipt).permit(:name, :shopping_date, :shop_id, :file,
articles_attributes: [:id, :name, :brand, :warranty_time, :warranty_expires,
:receipt_id, :_destroy])
end
end
Here's how my receipts.js.coffee looks like:
jQuery ->
$('#receipt_shopping_date').datepicker(dateFormat: 'yy-mm-dd')
$.datepicker.setDefaults($.datepicker.regional['PL']);
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
$(document).ready(jQuery)
$(document).on('page:load', jQuery)
And finally here's my view for adding a new receipt and adding articles to it:
(other fields...)
<div class="large-12 columns">
<p>Add articles on the receipt:</p>
</div>
<div class="field">
<div class="large-12 columns">
<%= f.fields_for :articles do |builder| %>
<div class="article_fields">
<%= render "article_fields", :f => builder %>
</div>
<% end %>
<%= link_to_add_fields "Add another article", f, :articles %>
</div>
</div>
<div class="actions">
<div class="large-12 columns">
<%= f.submit "Sumbit Receipt" %>
</div>
</div>
<% end %>
As you can see I'm using a link_to_add_fields helper method, here's how it looks like:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields small button", data: {id: id, fields: fields.gsub("\n", "")})
end
And finally as you can see I'm generating a partial called _article_fields.html.erb, here's how it looks like:
<fieldset style="width:1400px">
<legend>new article</legend>
<div class="large-2 columns">
<%= f.text_field :name%>
</div>
<div class="large-2 columns">
<%= f.text_field :brand%>
</div>
<div class="large-2 columns">
<%= f.text_field :warranty_time, class: "warranty" %>
</div>
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
</fieldset>
Now let's get down to my problem. When creating a receipt for the first time everything is fine - I see the number of articles in a receipt in my show view and the warranty_expires in every article.
Things get messed up when I'm updating or deleting article_fields through receipts/edit:
1) When I edit a receipt and want to remove any of the articles (although visually in my edit view they disappear - the JS seems to work), the fields are not removed from my DB, thus the show view remains exactly the same like it was before.
Simple example:
before edit: my receipt has 6 articles
during edit: pressed 3 times the 'delete article' button, so the receipt should have 3 articles
after edit: the receipt has still 6 articles
2) When I edit a receipt and want to add an another article field, the value warranty_expires is always nil - how can I make it work with the update action in my receipts controller? I tried using the same code as in my create action:
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
but it won't work. Any idea why?
Simple example:
A receipt has 2 articles already. When I add the 3rd one I get the following result:
3 articles - all of them have names and warranty_time fields, but only 2 of them have a warranty_expires value.
All of your help would be deeply appreciated. Thank you in advance.
I think you can use some callbacks in your Article model for solve your 2nd problem,
Start deleting this, try to keep your controller as simple as possible and handle the operations in your models.
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
In your Article Model add some callbacks
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
before_update :set_warranty_expires
before_create :set_warranty_expires
def set_warranty_expires
self.warranty_expires = self.receipt.shopping_date.advance(months: self.warranty_time)
end
end
Code its not tested, but its the idea. Hope it helps.
Check this two gems simple_form and nested_form this helps a lot when writing large forms and they play well with each other.
Update: I managed to fix the 1st issue.
The fix for the 1st solution is the following:
the hidden field :_destroy was missing for removing articles.
So I needed to changed the following code:
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
to:
<div class="large-12 columns">
<%= f.hidden_field :_destroy %>
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
Still have no idea how to fix the 2nd issue though.
First of all i noticed that, you have a loop in your reciepts_controller new action
2.times do
#receipt.articles.build
end
This means that, article will be created for only 2 times for that reciept.
Better remove the loop so that you can add as many as article you want.
For Issue number two add bellow line to edit action to your controller
#receipt.articles.build
I guess that would help you.
Also nested_form is a great gem for manage this kind of tasks.
https://github.com/ryanb/nested_form
Check it out.
This is an issue where .hide() is called in receipts.js.coffee. The simplest way to fix this I can think of is to simply replace .hide() with .remove()

Why does link_to when used in a partial return a link to the parent object rather than the actual object?

So I have two models, Users that belong_to Organization (which has_many users). I'm using a partial to display all the users that belong to any one particular organization, and this is working fine if I output just the name (it correctly names all the users). However when I change the partial code to provide link_to user.name, the returned links are all links to the parent Organization rather than the individual child objects. What am I doing wrong?
Relevant code:
Organizations Controller
def show
#organization = Organization.find(params[:id])
#users_index = User.joins(:organization).where(organizations: {id: #organization.id})
end
Organization.show.html.erb
<% provide(:title, #organization.organization_name) %>
<h1><%= #organization.organization_name %></h1>
<h2>Your Organization's Users:</h2>
<%= render partial: "users_index", collection: #users_index %>
_users_index.html.erb code:
<p class="users"><%= link_to users_index.name %></p>
If you set up your relationship properly then you can use:
#users_index = #organization.users
And then you need to loop through #users_index and pass that to your partial.
#users_index.each do |user|
<%= render "users_index", :user => user %>
end
And in your partial, change to:
<p class="users"><%= link_to user.name, user %></p>
link_to
I think the core of the issue, as alluded to in the other answer, is your link_to:
<%= link_to users_index.name %>
The link_to helper method basically works like this:
<%= link_to "Your Link Text", link_path %>
I don't see how your application will be able to load the correct link path without your definition, irrespective of whether you added it to the helper or not.
I presume that your rendered link will point to "#", not the "parent object" as you alluded.
--
Fix
I'd do this:
#app/views/organization/show.html.erb
<%= render partial: "users_index", collection: #users_index, as: :user %>
#app/views/organization/_users_index.html.erb
<p class="users"><%= link_to user.name, user %></p>
This should set the correct link_to helper method for you
--
Models
Further, I would also address the associations in your models
You're currently calling users in the most inefficient way. ActiveRecord associations work to remedy this:
#app/models/user.rb
Class User < ActiveRecord::Base
belongs_to :organization
end
#app/models/organization.rb
Class Organization < ActiveRecord::Base
has_many :users
end
This will allow you to call the following:
#app/controllers/organizations_controller.rb
Class OrganizationsController < ApplicationController
def show
#organization = Organization.find params[:id]
#users_index = #organization.users
end
end

Odd way of using nested forms (Rails)

I'm writing a (text) messaging app with Rails. I'm using nested_forms to allow you to send a message to multiple people.
In the controller, I instantiate a new Message object, then for each Member, build a Recipient object (child of Message). In the form, I display a checkbox next to each recipient. I want it so that the new Message object only has the recipients that have checks next to them. This is not working.
So by the time the form is rendered, Recipient objects are instantiated for all members. In other words, by default, a message gets sent to each member, unless specified not to. But I want to use the form to allow the user to specify who he wants the messages sent to
Here are my models:
class Message < ActiveRecord::Base
has_many :recipients
accepts_nested_attributes_for :recipients
#columns: body:string, from:string, from_member_id:integer
end
class Member < ActiveRecord::Base
#columns phone:string, name:string
end
class Recipient < ActiveRecord::Base
belongs_to :message
belongs_to :member
#columns: member_id:integer, message_id:integer
end
messages_controller.rb:
def new
#message = Message.new
#members = Member.all
#members.each do |member|
#message.recipients << Recipient.new(:member_id => member.id)
end
end
def create
#message = Message.new(params[:message])
redirect_to '/somewhere'
end
...
And here's my form for Message (app/views/message/new/html.erb)
<%= form_for(#message) do |f| %>
<% if #message.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#message.errors.count, "error") %> prohibited this message from being saved:</h2>
<ul>
<% #message.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :recipients do |builder| %>
<div class="field">
<input type="checkbox" value="<%= builder.object.member_id %>" name="recipients[id]" />
/*WHAT GOES ^^^HERE^^^? */
<%= builder.object.member.name %>
</div>
<% end %>
<div class="field">
<%= f.label :body %><br />
<%= f.text_field :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The commented line in the form is where I'm having trouble. Also, it seems I might need to modify some code in MessagesController#create, but I'm not sure where to start.
First, instead of writing your checkbox HTML by hand, you should use the Rails helpers for this. It'll save you a lot of work, particularly in redisplaying the form upon a validation failure.
To do this, you'll need to create an attribute on your Recipient class:
class Recipient
attr_accessor :selected
end
Then you can hook up that attribute to the checkbox:
<%= builder.check_box :selected %>
The next step is to make that attribute do something. You could try using the :reject_if option for accepts_nested_attributes_for. You could pass it a proc that returns true if the checkbox is not checked, e.g.:
accepts_nested_attributes_for :recipients, :reject_if => proc { |attributes| attributes['selected'] != '1' }
See these docs for details on :reject_if:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Resources