Working with associated models - ruby-on-rails

In my application I have a very simple association. A User has_many Emails, this association works as expected with create/update/delete.
How ever when I attempt to display the information this is where things become some what difficult.
In my controller I have the following bit of code:
def prospective_user
#users = Account::User.all_by_user_status(0)
#users.each do |u|
u.email = u.email.get_primary_email
end
end
What this should do is create an instance of the Account::Email model with a single record.
The method get_primary_email looks like this:
def self.get_primary_email
first :conditions => ["is_primary = 1"]
end
The issue I am seeing is with in my view, I am getting the exception below. This is a bit confusing as I am not looping over the email object.
undefined method each' for #<Account::Email:0x7fcc3a5c49d8>
The code I am using to test with is this:
<% #users.each do |u|%>
<p>
<%=debug(u.email)%>
</p>
<% end %>

Do something like that instead:
class User < ActiveRecord::Base
has_many :emails
has_one :primary_email, :class_name => 'Email',
:conditions => { is_primary: 1 }
end
Then you can call user.primary_email directly
# your controller
def prospective_user
#users = Account::User.includes(:primary_email).where(:status => 0)
end
# your view
<% for user in #users %>
<%= user.primary_email %>
<% end %>

Related

ROR: Search by Username and display their posts

I am learning Ruby on Rails and have a search form set up and its working. On the pins index view I can search for pins(posts) by their title. However if I wanted to search by Username which is not in the pins table and display the results on the Pins index page how would I do this? How do I access an attribute from a different table? (Sorry for the newbie attempt at explaining my issue)
Pins controller
def index
#pins = Pin.search(params[:term])
end
Pin Model
def self.search(term)
if term
where('description LIKE ?', "%#{term}%")
else
order('id DESC')
end
end
_search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
Assuming you have set up something like
class User < ApplicationRecord
has_many :pins
# the username is stored in the attribute 'username'
end
class Pin < ApplicationRecord
belongs_to :user
end
you may do the following
# PinsController
def index
terms = params[:term]
username = params[:username]
#pins = Pin
#pins = #pins.where("description LIKE '%?%'", term) if term
#pins = #pins.includes(:user).where("users.username LIKE '%?%'", username) if username
# you may want to sort by id anyway
#pins = #pins.order('id DESC')
end
Note that I put the code straight to the controller for brevity. You may refactor this to use your search method in pin model.
# _search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= text_field_tag :username, params[:username] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
In case you want to do some more searching and filtering you may have a look at the ransack gem although I think you're going the right path in trying to figure this out yourself.
Although those railscasts episodes are from the past I think they are applicable to the current rails versions. Anyway one can get the point from them
http://railscasts.com/episodes/37-simple-search-form
http://railscasts.com/episodes/111-advanced-search-form
http://railscasts.com/episodes/240-search-sort-paginate-with-ajax
Another good resource is gorails.com (not affiliate in any way!!). I can highly recommend them as a resource for learning
Provided you have an association between User and Pin
class User
has_many :pins
end
class Pin
belongs_to :user
end
You can join :user from Pin and set conditions on the association:
Pin.joins(:user).where('users.username ? AND awesome = ?', 'Max', true)
# or the preferred method
Pin.joins(:user).where(user: { username: 'Max', awesome: true })
Note that we use users.username and not user.username when writing a SQL string you're specifying the table name - not the association.
To search for pins based on the username you could do:
def self.by_username(term)
joins(:user).where('users.username LIKE ?', "%#{term}%")
end

Model properties in Ruby on Rails views

How to use model's properties at Rubi on Rails' views?
My model:
class EntryItems < ActiveRecord::Base
attr_accessible :created_on, :updated_on, :activity_type, :date_from, :date_to
attr_protected :user_id, :from_tyear, :from_tmonth, :from_tday
##date_from = nil
def date_from
##date_from = Date.new(read_attribute(:from_tyear), read_attribute(:from_tmonth), read_attribute(:from_tday))
end
def date_from=(date_from_value)
if date_from_value.is_a?(Time)
##date_from = date_from_value.to_date
end
write_attribute(:from_tyear, date_from_value.year)
write_attribute(:from_tmonth, date_from_value.mon)
write_attribute(:from_tday, date_from_value.mday)
end
end
My controller:
class ItemEntriesSetupController < ApplicationController
unloadable
def index
#item_entry = DayoffEntries.new(:user => User.current, :created_on => Time.now, :updated_on => Time.now)
#item_entry_post_url = url_for(:controller => 'item_entries_setup', :action => 'update')
end
def update
#code is skipped
end
end
My view:
<%= form_tag(#item_entry_post_url) do %>
<dl>
<label>Issues:</label>
<%=date_field_tag 'date_from', true, #item_entry.date_from %>
</dl>
<%= submit_tag(l(:button_create)) %>
<% end %>
It fails with underfined method 'div' for nil:NilClass.
What do I do wrong?
(I'm really new in Ruby on Rails, so I still have problems with finding right practicies, because there're plenty of code examples written n different style).

Rails generalizing a method that uses models

I am trying to generalize few methods that will be used by multiple models/views/controllers but i'm having no luck. Here is the original code that works when it is just for 1 set called Trucks.
View
<h2>Trucks</h2>
<%= form_tag trucks_path, :method => 'get' do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<p>
Search:
<%= text_field_tag :search %>
by
<%= select_tag :search_column, options_for_select(Truck.translated_searchable_columns(['attribute1']), params[:search_column]) %>
<%= submit_tag "Search" %>
</p>
<% end %>
<!-- Display code goes here, but im not showing since its just a table -->
Controller
def index
#trucks = Truck.search(params[:search], params[:search_column]).order(sort_column(Truck, "truck_no") + " " + sort_direction)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #trucks }
end
end
Model
class Truck < ActiveRecord::Base
attr_accessible :attribute1, :attribute2, :attribute3
def self.search(keyword, column_name)
if self.column_names.include?(column_name.to_s)
where("trucks.#{column_name} LIKE ?", "%#{keyword}%")
else
scoped
end
end
def self.searchable_columns(unwanted_columns)
self.column_names.reject{ |column| unwanted_columns.include?(column) }
end
def self.translated_searchable_columns(unwanted_columns)
columns = self.searchable_columns(unwanted_columns)
result = columns.map{ |column| [Truck.human_attribute_name(column.to_sym), column] }
result
end
end
All this works without a hitch, now I can't figure out for the life of me how to move these methods to lib and have them generalized so that lets say Trailers is able to call in the same method and pass in its information and achieve the same result. I am trying to make this code DRY as possible. Could anyone explain me what I need to do to achieve this? How does lib access the database?
The concept you're looking for is called a "concern". Rails has a convenience module for implementing concerns called ActiveSupport::Concern. Here's how you might extract your model methods:
module Searchable
extend ActiveSupport::Concern
module ClassMethods
def search(keyword, column_name)
if column_names.include?(column_name.to_s)
where("#{table_name}.#{column_name} LIKE ?", "%#{keyword}%")
else
scoped
end
end
def searchable_columns(unwanted_columns)
column_names.reject{ |column| unwanted_columns.include?(column) }
end
def translated_searchable_columns(unwanted_columns)
columns = searchable_columns(unwanted_columns)
columns.map{ |column| [human_attribute_name(column.to_sym), column] }
end
end
end
And then in your model:
class Truck < ActiveRecord::Base
include Searchable
attr_accessible :attribute1, :attribute2, :attribute3
end
As for where exactly you should store the Searchable module, it's up to you -- it just has to be someplace that's included in config.autoload_paths, just like a model or controller. Rails 4 introduced a convention that model concerns are stored in app/models/concerns, and controller concerns in app/controllers/concerns, but there is nothing special about these locations other than being autoloaded by default.

Using View page, Controller and Helper

I am new in Rails. And I have a project that;
I should get a value from user in View page (for example index), and I should use the value in Helper then send the result of Helper to Controller and show the result in a new View page (for example details). Additionally I have to save results to database. Right now I have helper, controller and view pages but I can't connect these three part to each other. I need help
Controller;
def index
#user = Mpeople.new[:user]
redirect_to "secondstep"
end
def secondstep
# helper should have controled here
redirect_to "following"
end
def following
#user = Mpeople.all
end
Model;
class Mpeople < ActiveRecord::Base
has_one :username
accepts_nested_attributes_for :username
end
View;
<% form_for :user, :url => {:action => "index"} do |pform| %>
<% pform.fields_for :person do |namefield| %>
Twitter Name : <%= namefield.text_field :username %>
<%= button_to "OK", :action => "following" %>
<% end %>
<% end %>
And helper is more longer, it sends twitter name to twitter and get following of a user from api.twitter.com
This is some of my helper; I edit it after your comment but I am not sure if it is correct or not.
module FafHelper
class PeopleController
require 'people_helper'
# txtname = indexteki textbox'un adına eşitle
#txtname = tname
txtname = namefiled.text_field
.....
a_get("1/users/lookup.#{json}").
with(:query => {:screen_name => txtname, :user_id => id_list}).
end
end
..
You no need to connect views and helper as by default all the helper modules are included in the views.
And do include the helper in you controller. Helper is a module and controller is a class. Just include the module in the class.
To get the clear picture please post exactly your structure.

Rails 3: Polymorphic liking of Entities by User, how?

Background:
I followed the tutorial here to setup a polymorphic User favorites data model in my application. This allows me to let a User make pretty much any Entity in the system which I add 'has_many :favorites, :as => :favorable' line to its model a favorite. I plan on using this to implement a Facebook style 'Like' system as well as several other similar systems.
To start off I added the favoritability to a Post model (each user can create status updates like on Facebook). I have it all done and unit tested so I know the data model is sound and functioning from either side of the relationship (User and Post).
Details:
I have a Home controller with a single index method and view.
on the index view I render out the posts for the user and the user's friends
I want the user to be able to like posts from their friends
The Posts controller has only a create and a destroy method with associated routes (not a full fledged resource) and through the Post method via AJAX posts are created and deleted without issue
Where I am stuck
How do I add the link or button to add the post to the user's Favorites?
According to the tutorial the way to create a new Favorite through the polymorphic association is to do it from the Post.favorites.build(:user_id => current_user.id). From this direction the build handles pulling out the Post's ID and TYPE and all I have to do is pass in the user's id
Do I use an AJAX form post to a Favorites controller with a Create and Destroy method similar to the Post controller?
I am still struggling to uncross the wires in my brain from ASP.Net N-Tier web application development over to Rails MVC. Hasn't been too bad until now ;)
I bet there are Gems out there that might do this but I need to learn and the best way is to suffer through it. Maybe a tutorial or sample code from someone who has implemented liking functionality within their application would be helpful.
Thanks in advance for the assistance!
Jaap, I appreciate your comment on my question. After writing the question I pretty much didn't want to wait because the real learning takes place through trial and error, so I errored it up ;)
It turns out that what you suggested was pretty much in line with exactly what I ended up doing myself (it's always nice to find out that what you decide to do is what others would do as well, I love the sanity check value of it all).
So here is what I did and it is all working through post-backs. Now I just need to implement AJAX and style it:
My favorite model because my Polymorphic Favorites model requires that an Entity can only be favorited once by a user I added to the validations 'Scopes' which indicate that for each attribute it has to be unique in the scope of the other 2 required attributes. This solves the issue of multiple favorites by the same user.
class Favorite < ActiveRecord::Base
before_save :associate_user
belongs_to :favorable
belongs_to :user
# Validations
validates :user_id, :presence => true,
:uniqueness => {:scope => [:favorable_id, :favorable_type], :message => "item is already in favorites list."}
validates :favorable_id, :presence => true,
:uniqueness => {:scope => [:user_id, :favorable_type], :message => "item is already in favorites list."}
validates :favorable_type, :presence => true,
:uniqueness => {:scope => [:favorable_id, :user_id], :message => "item is already in favorites list."}
# Callbacks
protected
def associate_user
unless self.user_id
return self.user_id = session[:user_id] if session[:user_id]
return false
end
end
end
My User Model (that which is relevant): I added 2 methods, the get_favorites which is the same as favorable one from the tutorial and a Favorite? method which checks to see if the Entity in question has already been added to the user's favorites.
class User < ActiveRecord::Base
# Relationships
has_many :microposts, :dependent => :destroy
has_many :favorites
# Methods
def favorite?(id, type)
if get_favorites({:id => id, :type => type}).length > 0
return true
end
return false
end
def get_favorites(opts={})
# Polymorphic Favoritability: allows any model in the
# application to be favorited by the user.
# favorable_type
type = opts[:type] ? opts[:type] : :topic
type = type.to_s.capitalize
# add favorable_id to condition if id is provided
con = ["user_id = ? AND favorable_type = ?", self.id, type]
# append favorable id to the query if an :id is passed as an option into the
# function, and then append that id as a string to the "con" Array
if opts[:id]
con[0] += " AND favorable_id = ?"
con << opts[:id].to_s
end
# Return all Favorite objects matching the above conditions
favs = Favorite.all(:conditions => con)
case opts[:delve]
when nil, false, :false
return favs
when true, :true
# get a list of all favorited object ids
fav_ids = favs.collect{|f| f.favorable_id.to_s}
if fav_ids.size > 0
# turn the Capitalized favorable_type into an actual class Constant
type_class = type.constantize
# build a query that only selects
query = []
fav_ids.size.times do
query << "id = ?"
end
type_conditions = [query.join(" AND ")] + fav_ids
return type_class.all(:conditions => type_conditions)
else
return []
end
end
end
end
My Micropost Model (that which is relevant): note the Polymorphic association in the has_many relationship titled :favorites.
class Micropost < ActiveRecord::Base
attr_accessible :content
# Scopes
default_scope :order => 'microposts.created_at DESC'
# Relationships
belongs_to :user
has_many :favorites, :as => :favorable # Polymorphic Association
# Validations
validates :content, :presence => true, :length => { :minimum => 1, :maximum => 140 }
validates :user_id, :presence => true
end
My Micropost Form: as you can see I am passing in the entity that will be mapped to the Favorite model as a local variable to the 2 Favorite forms as 'local_entity'. This way I can pull out the ID and the TYPE of the Entity for the Polymorphic association.
<div class="post">
<span class="value">
<%= micropost.content %>
</span>
<span>
<% if current_user.favorite?(micropost.id, micropost.class.to_s) %>
<%= render :partial => 'favorites/remove_favorite', :locals => {:local_entity => micropost} %>
<% else %>
<%= render :partial => 'favorites/make_favorite', :locals => {:local_entity => micropost} %>
<% end %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<div class="clear"></div>
</div>
My Make Favorite Form:
<%= form_for current_user.favorites.build do |f| %>
<div><%= f.hidden_field :favorable_id, :value => local_entity.id %></div>
<div><%= f.hidden_field :favorable_type, :value => local_entity.class.to_s %></div>
<div class="actions"><%= f.submit "make favorite" %></div>
<% end %>
My Remove Favorite Form:
<%= form_for current_user.get_favorites(
{:id => local_entity.id,
:type => local_entity.class.to_s}),
:html => { :method => :delete } do |f| %>
<div class="actions"><%= f.submit "remove favorite" %></div>
<% end %>
If you don't want to call this on the current_user, you would have to have these routes in your config/routes.rb to make nested routes for favorites on a user. I assume you have a Favorite model which belongs_to :user:
resources :users do
resources :favorites
end
Then make sure your favorites controller loads the user in some kind of before_filter:
def load_user
#user = User.load params[:user_id]
end
And then you can render a remote form to create a new favorite for any kind of object (it will only show a button):
<%= remote_form_for [#user, Favorite.new] do |f| -%>
<%= f.hidden_field :favorable_type, object.class.to_s %>
<%= f.hidden_field :favorable_id, object.id %>
<%= f.submit 'Like' %>
<%- end -%>
You would have to render that form as a partial sending along an object (e.g. a Post) and then it will create an AJAX POST call to /users/:id/favorites/ which will create the favorite object and render some kind of javascript response in a create.rjs file.
I hope this helps. The code itself is untested, but it might get you moving.

Resources