I am totally new to Ruby, and Rails. Currently, I am using helper methods. How can I write the same code as this in my Model 'User' so as to access all these variables from controller and view?
Writting code this way in helper is 100% functional:
module HomeHelper
def init(user_id)
#friends = Array.new
#followers = Array.new
#user = User.find_by_id(user_id) #Get User
#friends = #user.users #Get all his friends
#
#statuses = Array.new #
#friends.each do |friend| #
#statuses += friend.statuses #Get all statuses for 'a' friend, then loop
end #
#statuses += #user.statuses #
#statuses = #statuses.sort_by {|status| status.created_at}.reverse!
#friendsof = Array.new
#filtered_friendsof = Array.new
#friends.each do |friend|
#friendsof += friend.users
end
#friendsof.each do |friendof|
unless (#friends.include?(friendof))
if #user != friendof
#filtered_friendsof << friendof
end
end
end
end
#filtered_friendsof = #filtered_friendsof.uniq
end
Controller
class HomeController < ApplicationController
def index
#user_id=3
end
end
Model:
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many(:users,
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id")
#has_many :user_connections
end
Home controller:
class HomeController < ApplicationController
def index
#user = User.find(3)
end
end
User model:
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many :friends,
:class_name => 'User'
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id"
def combined_statuses
(friends.map(&:statuses) + statuses).flatten.
sort_by {|status| status.created_at}.reverse!
end
end
Now, you don't need your helper method and in your view you can use:
#user.friends # instead of #friends
#user.combined_statuses # instead of #statuses
I'll let you figure out the rest, but I hope you get the general idea of pushing the logic into the model.
Most of that logic belongs in the User model. Nothing else needs to be actually doing those computations, and the User model has access to all the relevant pieces. There are additionally several other improvements that can be made. I'll try to add comments below to indicate these improvements.
Model
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many :friends, # now you can just say user.friends
:class_name => 'User', # makes more sense semantically
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id"
def friends_statuses
(friends.map(&:statuses).flatten + statuses).sort_by!(&:created_at).reverse
# Ruby has many great methods for Arrays you should use.
# You can often avoid instantiating variables like the empty Arrays you have.
end
def second_order_friends
(friends.map(&:friends).flatten.uniq - friends) - [self]
end
end
Controller
class HomeController < ApplicationController
def index
user = User.find(7) # how do you decide which user you're displaying things for?
# this might be better off in 'show' rather than 'index'
# here you can call all the methods you have for 'User', such as:
# user.friends, user.statuses, user.friends_statuses, user.second_order_friends
# to make things accessible in the view, you just need an #variable, e.g.:
#friends = user.friends
#latest_statuses = user.friends_statuses.first(10)
end
Related
I'm trying to paginate my posts under it's forum_thread_id.
When I paginate #forum_posts I'm getting all the posts and not the ones specific to the thread id that I'm in.
I'm using will_paginate for pagination.
It's probably any easy fix that I'm not seeing.
You have to filter the query with the ForumThread id, try something like this (modify the code accordingly)
def show
#forum_post = ForumPost.new
#forum_posts = ForumPost.where(forum_thread_id: #forum_thread.id).paginate(:page => params[:page], :per_page => 3)
end
Your problem is here:
def show
#forum_post = ForumPost.new
#forum_posts = ForumThread.find(params[:id])
#forum_posts = ForumPost.paginate(:page => params[:page], :per_page => 3)
end
You're paginating the equivalent of ForumPost.all, which means you're going to get back all the posts, regardless of which thread they're a part of.
You need:
def show
#forum_post = ForumPost.new
#forum_thread = ForumThread.find params[:id]
#forum_posts = #forum_thread.paginate(page: params[:page], per_page: 3)
end
This is assuming you have the following setup:
#app/models/forum_thread.rb
class ForumThread < ActiveRecord::Base
has_many :forum_posts
end
#app/models/forum_post.rb
class ForumPost < ActiveRecord::Base
belongs_to :forum_thread
end
As an aside (this is advanced), you'd be much better putting your thread and post models into a Forum module:
#app/models/forum.rb
class Forum < ActiveRecord::Base
has_many :threads, class_name: "Forum::Thread"
end
#app/models/forum/thread.rb
class Forum::Thread < ActiveRecord::Base
belongs_to :forum
has_many :posts, class_name: "Forum::Post"
end
#app/models/forum/post.rb
class Forum::Post < ActiveRecord::Base
belongs_to :thread, class_name: "Forum::Thread"
end
This would allow you to use the following:
#config/routes.rb
scope path: ":forum_id", as: "forum" do
resources :threads do
resources :posts
end
end
#app/controllers/forum/threads_controller.rb
class Forum::ThreadsController < ApplicationController
def show
#forum = Forum.find params[:id]
#threads = #forum.threads
end
end
This is how I got it working.
#forum_posts = #forum_thread.forum_posts.paginate(:page => params[:page], :per_page => 2)
How do I add parameters to methods for rendering the current place in favorites?
I tried this:
class Place < ActiveRecord::Base
has_and_belongs_to_many :users
def in_fav(user)
if user.places.include?Place.find(id)
return true
else
return false
end
end
end
class User < ActiveRecord::Base
has_and_belongs_to_many :places
end
class PlacesController < ApplicationController
places = Place.all
user = User.first
render json: {desc:true, status:1; data: places}.to_json(:methods => :in_fav(user))
end
I find same problem here
attr_accessor :current_user
def is_favorited_by_user?(user=nil)
user ||= current_user
end
#drops.current_user = current_user
render :json => #drops.to_json(:methods => :is_favorited_by_user?)
I don't understand current_user - it's assocciations? and how to use method current_user for collection #drops
I have two models like that
class Plan < ActiveRecord::Base
belongs_to :profile
And
class Profile < ActiveRecord::Base
has_many :plans
And routes like: (I need to)
resources :profiles do
resources :plans
end
resources :plans
So, following up ruby-on-rails - Problem with Nested Resources, I've made my PLANS index controller like this, to works NESTED and UNESTED at same time (the only way I've found for now):
def index
if params.has_key? :profile_id
#profile = Profile.find(params[:profile_id])
#plans = #profile.plans
else
#plans = Plan.all
end
There is a cleaner approach to this?
I have another models in this situation, and putting all actions, in all controllers to behave like this is cumbersome.
You gave me an idea:
models/user.rb:
class User < ActiveRecord::Base
has_many :posts
attr_accessible :name
end
models/post.rb:
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :user_id
end
controllers/posts_controller.rb:
class PostsController < ApplicationController
belongs_to :user # creates belongs_to_user filter
# #posts = Post.all # managed by belongs_to_user filter
# GET /posts
# GET /posts.json
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
end
And now the substance:
controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
def self.belongs_to(model)
# Example: model == :user
filter_method_name = :"belongs_to_#{model}_index" # :belongs_to_user_index
foreign_key = "#{model}_id" # 'user_id'
model_class = model.to_s.classify # User
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{filter_method_name} # def belongs_to_user_index
if params.has_key? :'#{foreign_key}' # if params.has_key? :user_id
instance_variable_set :"##{model}", # instance_variable_set :"#user",
#{model_class}.find(params[:'#{foreign_key}']) # User.find(params[:user_id])
instance_variable_set :"#\#{controller_name}", # instance_variable_set :"##{controller_name}",
##{model}.send(controller_name.pluralize) # #user.send(controller_name.pluralize)
else # else
instance_variable_set :"#\#{controller_name}", # instance_variable_set :"##{controller_name}",
controller_name.classify.constantize.all # controller_name.classify.constantize.all
end # end
end # end
EOV
before_filter filter_method_name, only: :index # before_filter :belongs_to_user_index, only: :index
end
end
The code is not complex to understand if you have notions of Ruby metaprogramming: it declares a before_filter which declares the instance variables inferring the names from the controller name and from the association. It is implemented just for the index actions, which is the only action using the plural instance variable version, but it should be easy to write a filter version for the other actions.
In my Rails app, I have the following objects:
Group: has_many users through group_membership
GroupMembership: belongs_to user, belongs_to group
User: has_many groups through group_membership
Users can create groups. When this happens, I want to automatically add the user to the group. In my GroupsController, I have the following (extending InheritedResources):
super do |success, failure|
if success
GroupMembership.create(:user_id => current_user, :group_id => ???)
...
end
The problem is I cannot retrieve the object that super created. Is there a way to do this? Or better, is there a way to change the LockGroup model so that it always performs this association?
When the callback is fired, the controller already has the standard instance variable corresponding to the created group: #group !!
class GroupController < InheritedResources::Base
def create
super do |success, failure|
if success
GroupMembership.create(:user_id => current_user, :group_id => #group.id)
...
end
end
end
I assume the params key given for your group is :group. Then you can used the nested_attributes_for option in the model. Then you can set those in a before filter from the create action:
class User < ActiveRecord::Base
accept_nested_attributes_for :group_membership
end
# on your controller
before_filter :add_user, :on => [:create]
def add_user
params[:group][:group_membership_attributes] = {}
params[:group][:group_membership_attributes][:user] = current_user
end
or you build the group membership on user initialize:
class User < ActiveRecord::Base
def after_initialize
build_group_membership
end
end
# on your controller
before_filter :add_user, :on => [:create]
def add_user
params[:group][:user] = current_user
end
and it should automagically work.
I have the models User and StoredItem:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
end
class StoredItem < ActiveRecord::Base
belongs_to :user
named_scope :lookup, lambda { |id| { :conditions => ['qid = ?', id]}}
end
I need to have two methods to add and remove the items to StoredItem for current user. I put this code to User model:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
def save_item(params)
if(!self.stored_items.lookup(params[:qid]).exists?)
item = self.stored_items.new(:sid => params[:qid],
:name => params[:qti],
:url => params[:qur],
:group_id => params[:title],
:rating => Integer(params[:rating]))
item.save
end
end
def remove_item(qid)
item = self.stored_items.lookup(qid).first()
item.destroy
end
end
So here is the StoredItem controller:
def save_item
#user = UserData.find_by_login(session[:cuser])
#user.save_item(params)
# ...
end
Is it good architectural decision or it will be better to put this code to StoredItem model and pass the current user into it?
This is a good architectural decision. You need to keep it in the user since the User is the owner of the StoredItem. The user is responsible for its stored items, not the other way around.