Different behaviour of unscoped between rails 3 and rails 4 - ruby-on-rails-3

I have just upgraded an application from Rails 3.2.8 to Rails 4.0.2 and I've got an strange behaviour with a model that has a "default_scope" defined.
The questions is that it seems like "unscoped" (http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-unscoped) doesn't work in Rails 4 in the same way it used to do in Rails 3.
The problem raises when I try to chain "unscoped" with another "scope". In Rails 3 it worked fine by first removing default scope and then applying the second scope. In Rails 4, it adds the default scope always.
I have a Model defined this way:
class User < ActiveRecord::Base
......
# set a default scope to be aplied to EVERY query. to override this use "unscoped", for example: User.unscoped.all
def self.default_scope
where :active => true
end
# users that can be shown in the application
scope :not_hidden, where(:hidden => false)
.....
end
And here are some examples on how scope and unscoped are working on 3.2.8 and 4.0.2:
User.all (OK)
(3.2.8)
User.all
SELECT "users".* FROM "users" WHERE "users"."active" = 't'
(4.0.2)
User.all
SELECT "users".* FROM "users" WHERE "users"."active" = 't'
User.unscoped (OK)
(3.2.8)
User.unscoped
SELECT "users".* FROM "users"
(4.0.2)
User.unscoped
SELECT "users".* FROM "users"
User.unscoped.not_hidden (FAIL)
(3.2.8) (OK)
User.unscoped.not_hidden
SELECT "users".* FROM "users" WHERE "users"."hidden" = 'f'
(4.0.2) (FAIL)
User.unscoped.not_hidden
SELECT "users".* FROM "users" WHERE "users"."active" = 't' AND "users"."hidden" = 'f'
User.not_hidden.unscoped (OK)
(3.2.8) (OK)
User.not_hidden.unscoped
SELECT "users".* FROM "users"
(4.0.2) (OK)
User.not_hidden.unscoped
SELECT "users".* FROM "users"
Can anybody tell me if I'm missing something on how default scope and unscoped works in Rails 4?

That's desired behaviour (see Removing All Scoping in the guides).
Note that chaining unscoped with a scope does not work. In these
cases, it is recommended that you use the block form of unscoped:
Client.unscoped {
Client.created_before(Time.zone.now)
}

I have just found a solution.
I have changed not_hidden scope defintion from:
scope :not_hidden, where(:hidden => false)
To:
def self.not_hidden
where(:hidden => false)
end
And it works fine!!!!

Related

no implicit conversion of Symbol into Integer

I'm using rails 4.0.1 and ruby 2.0.0 with devise 3.2.2. I keep getting this issue when a user tries to edit his account. I have my strong parameters in application_controller.rb and a custom update controller which looks like this:
class RegistrationsController < Devise::RegistrationsController
def update
# For Rails 4
account_update_params = devise_parameter_sanitizer.for(:account_update)
#user = User.find(current_user.id)
#user.balanced_associate_token_with_customer(account_update_params)
if #user.update_with_password(account_update_params)
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
protected
def after_update_path_for(resource)
user_path(resource)
end
end
I have been pulling my hairs since yesterday...any assitance would be appreciated. Let me know if you need any more info.
Edited:
The only thing that changed before i noticed the error is that devise starting asking for config.secret_key in devise initialize file.
A portion of the full trace shows:
devise (3.2.2) lib/devise/models/database_authenticatable.rb:64:in `[]'
devise (3.2.2) lib/devise/models/database_authenticatable.rb:64:in `update_with_password'
app/controllers/registrations_controller.rb:12:in `update'
actionpack (4.0.2) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (4.0.2) lib/abstract_controller/base.rb:189:in `process_action'
Thanks!
I fixed the issue after a number of hours. Hopefully this helps someone out there. Seems Devise changed its documentation. Anyway I removed this line:
account_update_params = devise_parameter_sanitizer.for(:account_update)
and changed this line:
if #user.update_with_password(account_update_params)
to
if #user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
Everything works..now I need a drink.

Writing spec for devise RegistrationsController that will make sure users can't change their role - cryptic error message

I have a rails 3.1 app using Devise and CanCan to mange users and roles. I want to make sure that users can update their password, but not their roles. (So ordinary users can't give themselves an admin role, basically). I have overridden the Devise class "RegistrationsController" with some code like this:
def update
# this is my attempt to stop people from updating their roles
# and giving themselves "Admin" priveledges.
params.delete("role_ids")
super
end
I'm hoping this will prevent hackers from updating the "role_ids" field in the user to change their priviledges. (If there is a better way to achieve this, please say!) My problem is I can't seem to write a spec that will test that this code works. My spec looks like this:
require 'spec_helper'
describe RegistrationsController do
before (:each) do
#user = Factory(:user)
sign_in #user
end
it "should update the user attributes but not the roles" do
user_params = {"name" => "new_name", "role_ids" => ["2"],}
put :update, { :id => #user.id, :user => user_params}
#user = User.find(#user.id)
#user.name.should == "new_name"
#user.roles.should be_empty
end
end
The trouble is this test doesn't execute. I get an error message like this:
Failures:
1) RegistrationsController should update the user attributes but not the roles
Failure/Error: put :update, { :id => #user.id, :user => user_params}
AbstractController::ActionNotFound:
Could not find devise mapping for path "/user?id=29&user%5Bname%5D=new_name&user%5Brole_ids%5D%5B%5D=2".
Maybe you forgot to wrap your route inside the scope block? For example:
devise_scope :user do
match "/some/route" => "some_devise_controller"
end
# ./spec/controllers/registrations_controller_spec.rb:13:in `block (2 levels) in <top (required)>'
I don't understand what the error message is asking me to do. My routes seem fine and my application seems to work otherwise. Can anyone help?
Try this in your setup
#request.env["devise.mapping"] = Devise.mappings[:user]
For details see How To: Controllers and Views tests with Rails 3

Why using merge method with scopes isn't working anymore on Rails 3.1?

I stumbled upon a wonderful article about scopes on Rails 3+ : http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/index.html
You can read there (in 'Crazy Town' section) that it's possible to merge scopes from different models like this :
class User < ActiveRecord::Base
scope :published, lambda {
joins(:posts).group("users.id") & Post.published
}
end
which works just as expected, and allows you to do :
User.published.to_sql
#=> SELECT users.* FROM "users"
# INNER JOIN "posts" ON "posts"."author_id" = "users"."id"
# WHERE (posts.published_at IS NOT NULL AND posts.published_at <= '2010-02-27 02:55:45.063181')
# GROUP BY users.id
I tried this approach in my Rails 3.1 project and apparently it's not working anymore.
So I cloned the article's Rails 3.0.0-beta1 project, saw by my eyes that the guys are not lying and things are working the way they tell.
Then I 3.1'ed it up, and now I get :
ruby-1.9.2-p290 :003 > User.published.to_sql
User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."author_id" = "users"."id" GROUP BY users.id
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE (posts.published_at IS NOT NULL AND posts.published_at <= '2011-10-05 11:45:00.512231')
User Load (0.1ms) SELECT "users".* FROM "users"
NoMethodError: undefined method `to_sql' for []:Array
from (irb):3
from /home/jerefrer/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands/console.rb:45:in `start'
from /home/jerefrer/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands/console.rb:8:in `start'
from /home/jerefrer/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:9:in `require'
from script/rails:9:in `<main>'
==> Doesn't work anymore.
And that makes me sad, because scope merging was awesome and now I can't be as DRY as I want.
Do you know :
What happened between the two versions ?
Any other way to do the same ?
The & method doesn't look like it works anymore (too bad, I found the syntax was neat). You can replace it with ActiveRecord::Relation#merge:
class User < ActiveRecord::Base
scope :published, lambda {
joins(:posts).group("users.id").merge(Post.published)
}
end
Edit
And it looks like it won't be back, trying it in rails 3.0.10 gives a deprecation warning:
DEPRECATION WARNING: Using & to merge relations has been deprecated and will be removed in Rails 3.1. Please use the relation's merge method, instead.
Here's the commit deprecating it, in case someone's interested: https://github.com/rails/rails/commit/66003f596452aba927312c4218dfc8d408166d54

RefineryCMS: overriding Devise Omniauth Callbacks

I am working on an application that I am wanting to authenticate through a custom oauth provider I have setup on a different server.
I am trying to integrate this custom login using RefineryCMS. I have my routes setup, but for some reason it is still trying to follow the devise route built into RefineryCMS.
Routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
devise_scope :users do
match '/users/auth/:provider', :to => 'users/omniauth_callbacks#passthru'
end
Link for callback
<p><%= link_to "Sign in with Olympus", user_omniauth_callback_path(:olympus) %></p>
Olympus is the codename for the project for now.
Omniauth callback controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def olympus
auth = env["omniauth.auth"]
ap auth
end
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
end
The log via rails s once the link is clicked on:
Started GET "/users/auth/olympus/callback" for 127.0.0.1 at 2011-08-12 07:52:09 -0500
Processing by Devise::OmniauthCallbacksController#failure as
SQL (0.7ms) SHOW TABLES
SQL (0.6ms) SHOW TABLES
Page Load (0.3ms) SELECT `pages`.`id`, `pages`.`depth`, `pages`.`parent_id`, `pages`.`lft`, `pages`.`rgt`, `pages`.`link_url`, `pages`.`menu_match`, page_translations.title as page_title FROM `pages` INNER JOIN `page_translations` ON `page_translations`.`page_id` = `pages`.`id` WHERE `pages`.`draft` = 0 AND `pages`.`show_in_menu` = 1 AND `page_translations`.`locale` = 'en' ORDER BY lft ASC
Slug Load (0.1ms) SELECT `slugs`.* FROM `slugs` WHERE (`slugs`.`sluggable_id` IN (1,3) and `slugs`.`sluggable_type` = 'Page' AND (`slugs`.`locale` = 'en')) ORDER BY id DESC
Slug Load (0.2ms) SELECT `slugs`.* FROM `slugs` WHERE (`slugs`.sluggable_id = 3 AND `slugs`.sluggable_type = 'Page') ORDER BY id DESC
SQL (1.3ms) describe `roles_users`
Role Load (0.1ms) SELECT `roles`.* FROM `roles` WHERE `roles`.`title` = 'Refinery' LIMIT 1
SQL (1.4ms) describe `roles_users`
User Load (0.2ms) SELECT * FROM `users` INNER JOIN `roles_users` ON `users`.id = `roles_users`.user_id WHERE (`roles_users`.role_id = 1 )
Redirected to http://localhost:3000/users/login
So its still trying to go through the Devise::OmniauthCallbacksController through RefineryCMS... Does anyone know how to get around this or maybe even override this devise controller?
I also would like to say that I am completely new to working with oauth providers/clients. I have worked with Facebook and Twitter but doing them from scratch is something I am new at. As well as working with Devise/Omniauth. So excuse my ignorance. Thanks!
Fixed routing issue by reading this article: http://groups.google.com/group/refinery-cms/browse_thread/thread/37917e227b26f5ca
The issue I am having now, is that it appears to not be connecting to my oauth provider to do the authenticating :(

How can I see the SQL that will be generated by a given ActiveRecord query in Ruby on Rails

I would like to see the SQL statement that a given ActiveRecord Query will generate. I recognize I can get this information from the log after the query has been issued, but I'm wondering if there is a method that can be called on and ActiveRecord Query.
For example:
SampleModel.find(:all, :select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
I would like to open the irb console and tack a method on the end that would show the SQL that this query will generate, but not necessarily execute the query.
Similar to penger's, but works anytime in the console even after classes have been loaded and the logger has been cached:
For Rails 2:
ActiveRecord::Base.connection.instance_variable_set :#logger, Logger.new(STDOUT)
For Rails 3.0.x:
ActiveRecord::Base.logger = Logger.new(STDOUT)
For Rails >= 3.1.0 this is already done by default in consoles. In case it's too noisy and you want to turn it off you can do:
ActiveRecord::Base.logger = nil
Stick a puts query_object.class somewhere to see what type of object your working with, then lookup the docs.
For example, in Rails 3.0, scopes use ActiveRecord::Relation which has a #to_sql method. For example:
class Contact < ActiveRecord::Base
scope :frequently_contacted, where('messages_count > 10000')
end
Then, somewhere you can do:
puts Contact.frequently_contacted.to_sql
just use to_sql method and it'll output the sql query that will be run. it works on an active record relation.
irb(main):033:0> User.limit(10).where(:username => 'banana').to_sql
=> "SELECT "users".* FROM "users" WHERE "users"."username" = 'banana'
LIMIT 10"
when doing find, it won't work, so you'll need to add that id manually to the query or run it using where.
irb(main):037:0* User.where(id: 1).to_sql
=> "SELECT "users".* FROM "users" WHERE "users"."id" = 1"
This may be an old question but I use:
SampleModel.find(:all,
:select => "DISTINCT(*)",
:conditions => ["`date` > #{self.date}"],
:limit=> 1,
:order => '`date`',
:group => "`date`"
).explain
The explain method will give quite a detailed SQL statement on what its going to do
This is what I usually do to get SQL generated in console
-> script/console
Loading development environment (Rails 2.1.2)
>> ActiveRecord::Base.logger = Logger.new STDOUT
>> Event.first
You have to do this when you first start the console, if you do this after you have typed some code, it doesn't seem to work
Can't really take credit for this, found it long time ago from someone's blog and can't remember whose it is.
When last I tried to do this there was no official way to do it. I resorted to using the function that find and its friends use to generate their queries directly. It is private API so there is a huge risk that Rails 3 will totally break it, but for debugging, it is an ok solution.
The method is construct_finder_sql(options) (lib/active_record/base.rb:1681) you will have to use send because it is private.
Edit: construct_finder_sql was removed in Rails 5.1.0.beta1.
Create a .irbrc file in your home directory and paste this in:
if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
require 'logger'
RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
end
That will output SQL statements into your irb session as you go.
EDIT: Sorry that will execute the query still, but it's closest I know of.
EDIT: Now with arel, you can build up scopes/methods as long as the object returns ActiveRecord::Relation and call .to_sql on it and it will out put the sql that is going to be executed.
My typical way to see what sql it uses is to introduce a "bug" in the sql, then you'll get an error messages spit out to the normal logger (and web screen) that has the sql in question. No need to find where stdout is going...
Try the show_sql plugin. The plugin enables you to print the SQL without running it
SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
You could change the connection's log method to raise an exception, preventing the query from being run.
It's a total hack, but it seems to work for me (Rails 2.2.2, MySQL):
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
def log_with_raise(sql, name, &block)
puts sql
raise 'aborting select' if caller.any? { |l| l =~ /`select'/ }
log_without_raise(sql, name, &block)
end
alias_method_chain :log, :raise
end
end
end
You can simply use to_sql() function with the active record
Form.where(status:"Active").to_sql
In Rails 3 you can add this line to the config/environments/development.rb
config.active_record.logger = Logger.new(STDOUT)
It will however execute the query. But half got answered :