ruby on rails public_activity gem fetch followed activity - mysql

i have used public_activity gem in my app and also the act_as_follower gem
where a user can follow other user
the logic i am using to fetch all the following activities is
#follow_activities = PublicActivity::Activity.where(trackable_type: 'follow', key: 'follow.create')
here #follow_activities is fetching all the records where the following has been done but i want to limit this query,
It should fetch the follow activities only of those users which the current_user has followed.
fetching all queries is a bad idea.
for elaboration i am showing you the models i have included
class User< ActiveRecord::Base
acts_as_follower
acts_as_followable
end
and the follow model is as
class Follow < ActiveRecord::Base
include PublicActivity::Model
tracked owner: ->(controller, model) { controller && controller.current_user }
extend ActsAsFollower::FollowerLib
extend ActsAsFollower::FollowScopes
# NOTE: Follows belong to the "followable" interface, and also to followers
belongs_to :followable, :polymorphic => true
belongs_to :follower, :polymorphic => true
def block!
self.update_attribute(:blocked, true)
end
end
Please tell me how can i limit the records fetching. Thankx in advance

I finally figured it out i need to do this
follow_activities = PublicActivity::Activity.where(trackable_type: 'follow', key: 'follow.create', owner_id: current_user.all_following)
now this will fetch the records of only those users which the current_user has followed

Related

How to use the results of one table found using form params to query another table - Ruby on Rails

I need to use the result of a query, found using form parameters to query another table and I am having difficulties.
The first query is:
#criteria_event = CriteriaEvent.joins(:events).where("events.horse_id = ? AND events.task_group_id = ? AND events.start_date = ?", criteria_event_params[:horse_id], criteria_event_params[:task_group_id], criteria_event_params[:start_date])
This query returns the correct information as follows:
[#<CriteriaEvent id: 57, task_group_id: 3, start_time: "2000-01-01 10:00:00", created_at: "2016-02-12 01:36:54", updated_at: "2016-02-12 01:36:54", start_date: "2016-02-23", horse_id: 13>]>
I have been trying to return the related events using the CriteriaEvent id: found above (id:57) in the following query. This query follows the above query immediately:
#events = Event.where(criteria_event_id: #criteria_event.id)
I get the following error:
NoMethodError (undefined method `id' for #<CriteriaEvent::ActiveRecord_Relation:
The relevant models have the following:
class CriteriaEvent < ActiveRecord::Base
has_many :events
accepts_nested_attributes_for :events
end
class Event < ActiveRecord::Base
belongs_to :criteria_event
end
The controller has the following permits:
def criteria_event_params
params.require(:criteria_event).permit(:horse_id, :task_group_id, :start_date, :start_time, events_attributes: [:id, :criteria_event_id])
end
def event_params
params.require(:event).permit(:title, :start_date, :start_time, :end_date, :end_time, :location, :description, :notification_type, :notification_span_before, :notification_span_type, :horse_id, :task_group_id, :criteria_event_id, criteria_events_attributes: [:criteria_event_id, :id])
end
What am I missing?
Thanking you in advance for your help
Hey you are trying to select id on active record relation you can use map for array of id as,
#events = Event.where(criteria_event_id: #criteria_event.map(&:id))
Instead of using separate query you can find Events in this way,
#events= Event.joins(:criteria_event).where("events.horse_id = ? AND events.task_group_id = ? AND events.start_date = ?", criteria_event_params[:horse_id], criteria_event_params[:task_group_id], criteria_event_params[:start_date])
NoMethodError (undefined method `id' for #
The issue is that #criteria_event contains multiple records (inside an ActiveRecord::Relation object). This works like an array, and as such you don't have access to id on its own.
Instead, you'll need to either use pluck(:id) to get an array of ids, or just find_by (only returning a single result):
#pluck
#criteria_event = CriteriaEvent.joins(:events).where(events: { horse_id: criteria_event_params[:horse_id], task_group_id: criteria_event_params[:task_group_id], start_date criteria_event_params[:start_date]}).pluck(:id)
#find_by
#criteria_event = CriteriaEvent.joins(:events).find_by(events: { horse_id: criteria_event_params[:horse_id], task_group_id: criteria_event_params[:task_group_id], start_date: criteria_event_params[:start_date] }).id
Both of these will then allow you to use:
#events = Event.where(criteria_event_id: #criteria_event)

Ruby on rails database interface

I'm used to MySQL but trying to use Ruby on Rails right now. In MySQL, I would have two tables, with one containing a reference to another ("posts" referring to "topic"). A MySQL query doing what I want would be similar to "SELECT * FROM Posts WHERE posts.topic="topic" ("topic" here is a variable).
However, trying to work with the Ruby model stuff has me confused. The variables being passed between the controller and view are null because they are empty tables.
In my controller:
def topic
#topic = Topic.where(params[:topic])
#posts = Post.where(topic: #topic.object_id)
end
I don't know how to select the posts which have the topic defined by the "topic" variable.
In the view:
<% #posts.each do |post| %>
<p><%= post.title %></p>
<% end %>
The migration files:
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.string :text
t.references :topic
t.timestamps
end
end
end
class CreateTopics < ActiveRecord::Migration
def change
create_table :topics do |t|
t.string :topic
t.timestamps
end
end
end
Given that Post and Topic are related, according to your migrations at least, in the models you should be stating"
class Topic
has_many :posts
and
class Post
belongs_to :topic
Given that you then have an instance of Topic, #topic, you can retrieve all the related records with:
#posts = #topic.posts
I think those methods you put in your controller are fine where they are, but keep in mind that the Rails way is "fat models, skinny controllers." If you put that logic in the model as a method, it's much easier to read in the controller. Also, you should look into scopes, as they'll help you with queries like this down the line too.
In any case, you should stick the following in your Topic model:
scope :by_name, ->(name) { where(topic: name) }
That's essentially the same as doing the following:
def self.by_name(name)
where(topic: name)
end
On your posts model, you'd be able to do the following:
scope :by_topic, ->(topic) { where(topic_id: topic) }
The other problem with what you've stuck in your controller is that when you use scopes, or a "where", it returns an array that contains all of the different records that match your query terms. So, when you call #topic = Topic.where(params[:topic]), you're getting back an array of objects. Therefore, when you do a #topic.id, you're trying to get back the id of an array instead of one object.
Based off of what I showed you before, it makes much more sense for you to do this:
def topic
#topic = Topic.by_name(params[:topic]).first #this returns the first record
#post = Post.by_topic(#topic.id)
end
That will return an array of posts that match the first topic name that you query for.
Alright, first a primer on how database design and how Rails (really, ActiveRecord) works. Basically, you should be connecting posts.topic_id = topic.id, not posts.topic = topic.topic.
Your migration is correct as is, create_table automatically includes an :id PRIMARY KEY column. That said you should know that these are all equivalent:
t.references :topic
t.belongs_to :topic
t.integer :topic_id
In your view, instead of embedding topic.topic and passing that to the controller when the form is submitted, embed topic.id (the documentation for the select helper has a good example of this) and in your controller:
#topic = Topic.find params[:id]
#posts = #topic.posts

Rails 4: ActiveRecord or MySQL query where no related models have attribute

Having a tough time with this one. I have a Job model, and a JobStatus model. A job has many statuses, each with different names (slugs in this case). I need an 'active' method I can call to find all jobs where none of the associated statuses has a slug of 'dropped-off'.
class Job < ActiveRecord::Base
belongs_to :agent
has_many :statuses, :class_name => "JobStatus"
validates :agent_id,
:pickup_lat,
:pickup_lng,
:dropoff_lat,
:dropoff_lng,
:description,
presence: true
class << self
def by_agent agent_id
where(agent_id: agent_id)
end
def active
#
# this should select all items where no related job status
# has the slug 'dropped-off'
#
end
end
end
Job Status:
class JobStatus < ActiveRecord::Base
belongs_to :job
validates :job_id,
:slug,
presence: true
end
The closest I've gotten so far is:
def active
joins(:statuses).where.not('job_statuses.slug = ?', 'dropped-off')
end
But it's still selecting the Job that has a dropped-off status because there are previous statuses that are not 'dropped-off'. If i knew the raw sql, I could probably work it into activerecord speak but I can't quite wrap my head around it.
Also not married to using activerecord, if the solution is raw SQL that's fine too.
Job.where.not(id: JobStatus.where(slug: 'dropped-off').select(:job_id))
will generate a nested subquery for you.
Not the cleanest method, but you could use two queries.
# Getting the ID of all the Jobs which have 'dropped-off' JobStatuses
dropped_off_ids = JobStatus.where(slug: 'dropped-off').pluck(:job_id)
# Using the previous array to filter the Jobs
Job.where.not(id: dropped_off_ids)
Try this:
def active
Job.joins(:statuses).where.not('job_statuses.slug' => 'dropped-off')
end
or this:
def active
Job.joins(:statuses).where('job_statuses.slug != ?', 'dropped-off')
end
I think you may want to reevaluate your data model somewhat. If the problem is that you're turning up old statuses when asking about Job, you likely need to have column identifying the current status for any job, i.e. job.statuses.where(current_status: true)
Then you can very easily grab only the rows which represent the current status for all jobs and are not "dropped-off".
Alternatively, if I'm misunderstanding your use case and you're just looking for any job that has ever had that status, you can just go backwards and search for the status slugs first, i.e.
JobStatus.where.not(slug: "dropped-off").map(&:job)

Nested strong parameters in rails - AssociationTypeMismatch MYMODEL expected, got ActionController::Parameters()

I'm rendering a model and it's children Books in JSON like so:
{"id":2,"complete":false,"private":false, "books" [{ "id":2,"name":"Some Book"},.....
I then come to update this model by passing the same JSON back to my controller and I get the following error:
ActiveRecord::AssociationTypeMismatch (Book (#2245089560) expected, got ActionController::Parameters(#2153445460))
In my controller I'm using the following to update:
#project.update_attributes!(project_params)
private
def project_params
params.permit(:id, { books: [:id] } )
end
No matter which attributes I whitelist in permit I can't seem to save the child model.
Am I missing something obvious?
Update - another example:
Controller:
def create
#model = Model.new(model_params)
end
def model_params
params.fetch(:model, {}).permit(:child_model => [:name, :other])
end
Request:
post 'api.address/model', :model => { :child_model => { :name => "some name" } }
Model:
accepts_nested_attributes_for :child_model
Error:
expected ChildModel, got ActionController::Parameters
Tried this method to no avail: http://www.rubyexperiments.com/using-strong-parameters-with-nested-forms/
Are you using accepts_nested_attributes_for :books on your project model? If so, instead of "books", the key should be "books_attributes".
def project_params
params.permit(:id, :complete, :false, :private, books_attributes: [:id, :name])
end
I'm using Angular.js & Rails & Rails serializer, and this worked for me:
Model:
has_many :features
accepts_nested_attributes_for :features
ModelSerializer:
has_many :features, root: :features_attributes
Controller:
params.permit features_attributes: [:id, :enabled]
AngularJS:
ng-repeat="feature in model.features_attributes track by feature.id
My solution to this using ember.js was setting the books_attributes mannualy.
In controller:
def project_params
params[:project][:books_attributes] = params[:project][:books_or_whatever_name_relationships_have] if params[:project][:books_or_whatever_name_relationships_have]
params.require(:project).permit(:attr1, :attr2,...., books_attributes: [:book_attr1, :book_attr2, ....])
end
So rails checks and filters the nested attributes as it expected them to come
This worked for me. My parent model was an Artist and the child model was a Url.
class ArtistsController < ApplicationController
def update
artist = Artist.find(params[:id].to_i)
artist.update_attributes(artist_params)
render json: artist
end
private
def artist_params
remap_urls(params.permit(:name, :description, urls: [:id, :url, :title, :_destroy]))
end
def remap_urls(hash)
urls = hash[:urls]
return hash unless urls
hash.reject{|k,v| k == 'urls' }.merge(:urls_attributes => urls)
end
end
class Artist < ActiveRecord::Base
has_many :urls, dependent: :destroy
accepts_nested_attributes_for :urls, allow_destroy: true
end
class Url < ActiveRecord::Base
belongs_to :artist
end
... and in coffeescript (to handle deletions):
#ArtistCtrl = ($scope, $routeParams, $location, API) ->
$scope.destroyUrls = []
$scope.update = (artist) ->
artist.urls.push({id: id, _destroy: true}) for id in $scope.destroyUrls
artist.$update(redirectToShow, artistError)
$scope.deleteURL = (artist,url) ->
artist.urls.splice(artist.urls.indexOf(url),1)
$scope.destroyUrls.push(url.id)
Something is missing from all of the answers, which is the inputs for fields_for in the form.
The form works if you do this:
f.fields_for #model.submodel do ..
However, the form is sent as model[submodel], but that's what causes the error others have mentioned in their answers. If you try to do model.update(model_params), Rails will raise an error that it's expecting a Submodel type.
To fix this, make sure you follow the :name, value format:
f.fields_for :submodel, #model.submodel do ...
Then in the controller, make sure you put _attributes on your params:
def model_params
params.require(:model).permit(submodel_attributes: [:field])
end
Now the save, update, etc. will work fine.
Wasted several days trying to figure out how to use accepts_nested_attributes with Angular, and the issue is always the same: Rails whitelist will not allow the variables into the params hash. I've tried every single different whitelisting syntax that everyone said on SO and other blogs, tried using :inverse, tried using habtm and mas_many_through, tried manually rolling my own solution but that wont work if the whitelist wont allow params through, tried doing what http://guides.rubyonrails.org says about 'Outside the Scope of Strong Parameters', tried removing whitelisting all together which isnt really an option but it causes other problems anyways. Not sure why rails 4 strong parameter whitelisting wont allow arbitrary data thru, thats a huge problem especially if accepts_nested_attributes doesn't work either.... I guess we are left to just create/delete all associations on a separate page/form/controller and look like an idiot making my end users use several forms/pages to do something that should be easily doable on 1 page with 1 form. Ya know, usually I expect Angular to screw me, but this time Angular worked quite well and it was actually Rails 4 that screwed me twice on 1 issue that should be very straightforward.

Able to add myself as friend in rails console

I have built a user and friend relationship model but the problem is that with those associations I can friend myself. I have successfully suppressed it in my views and controller, but logically it should be suppressed in the model because I could still create the friendship from the console which I want to avoid.
User model
has_many :user_friendships
has_many :friends, through: :user_friendships,
conditions: { user_friendships: { state: 'accepted' } }
User_friendship model
belongs_to :user
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
Everything else is working perfectly like adding, blocking, deleting, requesting a friend the only problem with my model is that I can also friend myself which I want to avoid.
Add a validation to UserFriendship:
validate :cannot_friend_self
def cannot_friend_self
errors.add(:friend_id, "cannot friend self") unless user_id != friend_id
end
This issue is a little problematic because we want to remain RESTful, separate the different tasks (MVC,) and take into account of weird race conditions (Thread Safety.)
Try using validations#exclusions (http://guides.rubyonrails.org/active_record_validations_callbacks.html#exclusion)
class ApplicationController < ActionController::Base
...
before_filter do |c|
User.current_user = User.find(c.session[:user]) unless c.session[:user].nil?
end
...
end
class User < ActiveRecord::Base
...
cattr_accessor :current_user
...
end
class Friends < ActiveRecord::Base
...
validates :friend_id, :exclusion => { :in => %w(User.current_user.id),
:message => "I don't think you really want to friend yourself" }
...
end
If you want to be safe, please refer to (http://nhw.pl/wp/2011/11/30/passing-current-user-id-to-rails-models)
Disclaimer:
I wrote this possible solution without testing it (aka pulled it out of the thin air with little reference)
I have not thread with Ruby on Rails.
You probably want to throw in a validation
Such as
validate :cannot_friend_self
def cannot_friend_self
current_user.id != friend.id
end
This code may not be exactly what you want, but should point you in the right direction.
Full guide here http://guides.rubyonrails.org/active_record_validations_callbacks.html#custom-methods