Rails Rest Service GET - ruby-on-rails-3

I have set up a Rails REST Service and I am having a problem showing a single record. Here is the URL that I am trying to hit:
http://website:3000/users/2/timesheets/21
Controller code:
def show
puts "SHOW"
puts params.inspect
#timesheets = User.find(params[:user_id]).timesheets(params[:id])
respond_to do |format|
format.json { render json: #timesheets }
end
end
I know the params are getting to the controller, but it is not using the :timesheet_id. Here is the console output to show what I mean:
Started GET "/users/2/timesheets/21" for **.**.***.** at 2013-03-19 06:12:11 -0400
Processing by TimesheetsController#show as */*
Parameters: {"user_id"=>"2", "id"=>"21"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "2"]]
Timesheet Load (0.5ms) SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."user_id" = 2
Completed 200 OK in 120ms (Views: 36.5ms | ActiveRecord: 2.9ms)
I see the timesheet id of 21 in the parameters hash. A query is then made to get the user, but then all of the timesheets for that user are grabbed. Any help would be appreciated. Thanks.

What Prakash suggests works, but executes two queries: one to get the user and one the get the timesheet. There does not seem to be any reason to do User.find(...) first. Might as well query the timesheets table only, which will only execute one query and is thus faster:
#timesheet = Timesheet.where('user_id = ? and id = ?', params[:user_id], params[:id]).first

Try this:
#user = User.find(params[:user_id])
#timesheet = #user.timesheets.find(params[:id])
This should run a query as follows:
SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."id" = 21 AND ("timesheets"."user_id" = 2) LIMIT 1
The corresponding view should be referring to #timesheet variable and not #timesheets.

Related

Rails 5 - How to use postgresql query?

In rails 5, I am using pg(postgresql) for a back-end database. Now I want to query through rails and get the data. How can I use IN and ORDER(:created_at, :desc) conditions in a query.
In controller,
PAGE_LIMIT = 5
posts = Post.where("user_id IN (?)", [1,2,3]).order(created_at: :desc)
posts = posts.paginate(params[:page], PAGE_LIMIT)
I am writing a custom method like,
def paginate(page, limit = 5)
page = page ? page.to_i : 1
limit = limit.to_i
offset = (page - 1) * limit
self.offset(offset).limit(limit)
end
I am new to postgresql. Please help me to solve this issue?
suppose you have User model and you want to get user which has id in [1,2,3]
User.where("id IN (?)", [1,2,3]).order(created_at: :desc)
for more dynamic
x = [1,2,3]
name = "testing123"
User.where("name = ? AND id IN (?)", name, x).order(created_at: :desc)
for more details you can see Active Record Query
to make it working with pagination for array changes to be done here
modified answer
PAGE_LIMIT = 5
posts_arr = Post.where("user_id IN (?)", [1,2,3]).order(created_at: :desc)
posts = Post.where(id: posts_arr.map(&:id)) #here to get active records of posts to make it working for custom pagination
posts = posts.paginate(params[:page], PAGE_LIMIT)

Rails and SQL straight request

In rails we have Model.find(id_number) request. What I want to ask is there any possibility to create straight request to DB, for example: SELECT * FROM users WHERE id = 1 and replace rails basic Model.where(id: 1)?
EDIT
In my model.rb I have:
scope :search_import, -> {includes(:translations)}
default_scope -> {self.search_import}
And request (in rails console) looks like:
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User::Translation Load (0.6ms) SELECT `users_translations`.* FROM `users_translations` WHERE `users_translations`.`category_id` IN (1)
But I don't want to User::Translation was executed (with unscoped I have the same result)
Sure, something like
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = 1")

Check if a record is present in a has-many association

I have a rails-api application in which users can follow other users.
To check if an user already follows another user, I need to include a query in the attributes and because of that, I have always a N+1 query problem.
Here is my code:
Index action in user controller:
def index
#users = ::User.all.paginate(page: params[:page])
end
The followers will always be included by a default_scope in the User model.
index.json.jbuilder:
json.partial! 'attributes', collection: #users, as: :user
_attributes.json.jbuilder:
json.extract! user, :id, :firstname, :lastname, :username, :follower_count
is_follower = user.follower.find_by(id: current_user.id).present? if current_user
json.following is_follower
And as a result:
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 14]]
Rendered v1/user/users/_attributes.json.jbuilder (1.3ms)
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 9]]
Rendered v1/user/users/_attributes.json.jbuilder (1.4ms)
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1 [["followed_id", 13]]
Is there some workaround or is it somehow possible to generate a dynamic attribute in the SQL query which includes the boolean value if the user follows the other user?
Thank you very much in advance.
My first thought would be to eager load the followers using the .includes method when you get the list of users like this #users = ::User.all.includes(:followers).paginate(page: params[:page]). But perhaps, I'm not understanding your question correctly? Let me know if that works or if I should focus my answer on a different subject. Thanks!
EDIT: Correct answer from the comments below:
Perhaps you can try user.followers.include?(current_user) to make use of the pre-loaded followers association.

How to pass dynamic database connection in Sidekiq worker?

So I have these two sidekiq workers, both should connect to and save data to a database. The specific database can change based on the Account.
Here is the one that works: (there are other methods in the worker)
class GaDataloadProcessor < GaWorker
attr_reader :profile, :dataload, :connection, :results
def perform(id)
#dataload = Dataload::GoogleAnalytics.find(id)
connector = dataload.google_analytics_connector
with_session(connector.username, connector.password) do
load_profile(connector.account, dataload.profile)
run_report
setup_db_connection
import_results
end
end
private
def setup_db_connection
#connection = ActiveRecord::RdsDb.get_connection(dataload.account)
end
I start this worker in the console with a valid ID and I get:
irb(main):004:0> ga_worker.perform(2)
Dataload::GoogleAnalytics Load (3.0ms) SELECT "dataload_google_analytics".* FROM "dataload_google_analytics" WHERE "dataload_google_analytics"."id" = $1 LIMIT 1 [["id", 2]]
Connector::GoogleAnalytics Load (0.7ms) SELECT "connector_google_analytics".* FROM "connector_google_analytics" WHERE "connector_google_analytics"."id" = 7 LIMIT 1
Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = 16 LIMIT 1
(76.1ms) insert into google_analytics_test2 (visitors,date) values ('4','20131018')
Awesome, works as expected. This worker does not work (bad worker): (and there are other methods in the worker)
class CmpUpdateWorker < BaseWorker
attr_reader :dataload, :connection
def perform(dataload_id)
#dataload = DataloadMailchimp.find(dataload_id)
#gibbon = Gibbon.new(#dataload.api_key)
setup_db_connection
run_report
import_results
end
private
def setup_db_connection
#connection = ActiveRecord::RdsDb.get_connection(dataload.account)
end
And when I execute this worker from console I get this:
irb(main):002:0> cmp_worker.perform(5)
DataloadMailchimp Load (68.0ms) SELECT "dataload_mailchimps".* FROM "dataload_mailchimps" WHERE "dataload_mailchimps"."id" = $1 LIMIT 1 [["id", 5]]
NoMethodError: undefined method `rds_username' for 16:Fixnum
16 is the correct account_id and it is in the database. That record also has the correct rds_username and other db connection values.
Both models referenced in the workers have belongs_to :account. I have been testing and trying to narrow this down but am stuck. I know there is a lot more code than I have posted here. I am happy to add anything that may help in solving this to a gist or access to the repo.
I appreciate any advice or direction. Thanks.
I got the CmpUpdateWorker to run by updating setup_db_connection to:
def setup_db_connection
#connection = ActiveRecord::RdsDb.get_connection(#dataload.account)
end

Rspec controller test hanging

Here is my controller test ("spec/controllers/api/tasks_controller_spec.rb")
require 'spec_helper'
describe Api::TasksController do
before :each do
#quadros = create :cat
#task = Task.new(:content => "Example task for #api")
#quadros.add_task(#task)
end
describe "GET index" do
it "returns a list of all user's tasks" do
get :index, :format => 'json'
expect(response).to eq(User.first.all_tasks)
end
end
end
Here is my Api::BaseController
class Api::BaseController < ApplicationController
respond_to :json
skip_before_filter :authenticate_user!
end
And Api::TasksController
class Api::TasksController < Api::BaseController
def index
#tasks = User.first.all_tasks
respond_with #tasks.to_json
end
end
My other test run fine.
When I run the api test, it executes the before block, makes the request as json, and then hangs on this query:
Processing by Api::TasksController#index as JSON
User Load (0.3ms) SELECT `users`.* FROM `users` LIMIT 1
Tag Load (0.3ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NOT NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE `tags`.`id` IN (NULL)
Tag Load (0.3ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NULL)
Task Load (0.7ms) SELECT tasks.* FROM `tasks` JOIN tag_tasks on tasks.id = tag_tasks.task_id WHERE (tag_tasks.tag_id IN (301) OR creator_id = 418) GROUP BY tasks.id ORDER BY tasks.created_at DESC
Completed 200 OK in 99ms (Views: 0.1ms | ActiveRecord: 0.0ms)
User Load (0.3ms) SELECT `users`.* FROM `users` LIMIT 1
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NOT NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE `tags`.`id` IN (NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NULL)
Task Load (0.7ms) SELECT tasks.* FROM `tasks` JOIN tag_tasks on tasks.id = tag_tasks.task_id WHERE (tag_tasks.tag_id IN (301) OR creator_id = 418) GROUP BY tasks.id ORDER BY tasks.created_at DESC
Where it will just sit forever.
Any ideas why this might be happening?
I've come across a similar issue, and it appears the problem is with your expectation line:
expect(response).to eq(User.first.all_tasks)
This is not how RSpec wants you to test the response body. Notice that in the docs, specialized matchers are used instead of the equality matcher:
expect(response).to render_template("index")
So the response object, which is an ActionController::TestResponse, is meant to be queried about what happened, not what the response body was. So your test should be something like:
expect(JSON.parse(response.body)).to eq(User.first.all_tasks)
(Note that the response body is a string.)
As for the explanation of why the test hangs—instead of outright failing—it appears that this block of code (lib/rspec/expectations/fail_with.rb:22 in the rspec-expectations gem version 2.14.0) is the culprit:
if actual && expected
if all_strings?(actual, expected)
if any_multiline_strings?(actual, expected)
message << "\nDiff:" << differ.diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
end
elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
message << "\nDiff:" << differ.diff_as_object(actual, expected)
end
end
The all_strings?, any_multiline_strings?, no_procs?, and no_numbers? methods (defined in the same file) all invoke args.flatten on [actual, expected]. In this case, from what I can tell the problem is that actual is a TestResponse, which causes the flatten method itself to hang without raising a runtime error. I didn't have time to investigate this further, but the source for Array.flatten is here if anyone is interested.