How does rolify keep track of roles? - ruby-on-rails

Rolify has a join table called user_roles that keeps track of all the relationships:
user_id role_id
But here's what's weird. When I empty this table of all data each user still has its role remembered.
I'm trying to manipulate the database in my specs, and it's impossible because of this spookiness.
Look at this from the console:
user = User.save
irb(main):022:0> user.roles # nothing weird, I have a before filter to make every new user a guest
=> #<ActiveRecord::Associations::CollectionProxy [#<Role id: 1, name: "guest", resource_id: nil, resource_type: nil, created_at: "2014-08-17 10:04:57", updated_at: "2014-08-17 10:04:57">]>
irb(main):023:0> UsersRole.all.each do |role| puts role.inspect end
=> [#<UsersRole user_id: 2, role_id: 1, id: 4>, #<UsersRole user_id: 2, role_id: 1, id: 5>]
irb(main):024:0> UsersRole.all.each do |role| role.delete end
=> [] # completely empty!
irb(main):026:0> user.roles # no way should this get anything
=> #<ActiveRecord::Associations::CollectionProxy [#<Role id: 1, name: "guest", resource_id: nil, resource_type: nil, created_at: "2014-08-17 10:04:57", updated_at: "2014-08-17 10:04:57">]>
I'd really appreciate some help into why this is happening.

you should reload your object after deleting the roles. rails caches association.
user = User.save
irb(main):022:0> user.roles
=> #<ActiveRecord::Associations::CollectionProxy [#<Role id: 1, name: "guest", resource_id: nil, resource_type: nil, created_at: "2014-08-17 10:04:57", updated_at: "2014-08-17 10:04:57">]>
irb(main):023:0> UsersRole.all.each do |role| puts role.inspect end
=> [#<UsersRole user_id: 2, role_id: 1, id: 4>, #<UsersRole user_id: 2, role_id: 1, id: 5>]
irb(main):024:0> UsersRole.all.each do |role| role.delete end
=> [] # completely empty!
irb(main):026:0> user.reload.roles

Related

Getting column value in Rails

I have a table named students.
I need to get email field from specific students
for example, I get the following output in IRB
Student.all
Student Load (0.1ms) SELECT "students".* FROM "students"
=> [#<Student id: 1, name: "Bob", grade: 1, email_address: "bob#school.com", created_at: "2014-03-27 08:55:51", updated_at: "2014-03-27 08:55:51">, #<Student id: 2, name: "Neo", grade: 1, email_address: "robert#neo.com", created_at: "2014-03-27 08:56:05", updated_at: "2014-03-27 08:56:05">, #<Student id: 3, name: "Phil", grade: 3, email_address: "phil#school.com", created_at: "2014-03-27 08:56:21", updated_at: "2014-03-27 08:56:21">]
now I need to get email addresses of grade 1 students.
How could I get it?
I solved it,
it can be done by using pluck function:
mail_addresses = Student.where(grade: 1).pluck(:email_address)
mail_addresses.each do|a|
puts a
end
Output :
bob#school.com
robert#neo.com
=> ["bob#school.com", "robert#neo.com"]
Voila!
I think that it will help you.
There are many way to write queries to get email addresses of student with grade 1.
Student.where(:grade => 1).map(&:email)
or
Student.where(:grade => 1).collect(&:email)
or
Student.where("grade = ?", 1).map(&:email)
or
Student.find(:all, :conditions => ["grade = ?", 1]).map(&:email)
you can use select also.
Student.where("grade = ?", 1).select("email").map(&:email)
always use rails query with where instead of find. rails query with `where' is working fast instead of find.

Why can't I destroy this variable in Ruby?

In the rails console, I do this:
input = Input.create :name => "foo"
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
Input.all
=> [#<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">]
input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
input.destroy
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
> Input.all
=> []
> input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
> input.reload
ActiveRecord::RecordNotFound: Couldn't find Input with id=8
> input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
What I'd really expect to see is something like:
> input
=> nil
The object is deleted from the database but the variable still exists and is still trying to point to it. What's going on?
The input variable stores a reference to the instance in memory. Destroying the record will remove the row from the database. Calling input.reload (docs) raises an exception when attempting to find the record but doesn't set the value of your variable to nil on your behalf.
This behavior can be useful in the span of a DELETE request in which you want to display information about the object you removed. For example:
class WidgetsController < ApplicationController
def destroy
#widget = Widget.find(params[:id])
#widget.destroy
respond_with #widget, notice: "You successfully removed #{#widget.name}"
end
end
The destroy method makes the SQL call to the database and destroys the row in the table that contains it. It does still allow you to manipulate the object in the application as long as it’s still in scope (i.e) the callbacks and
filters are allowed even after destroying the object.
It is better to use "delete" if we don't want the callbacks to be triggered or if we want better performance
you can use input.delete

Monitoring process interference Factory's instance initialization

In my project, the user will have many items, which have an onshelf_at attribute default to DateTime.now at its creation.
# item.rb
class Item < ActiveRecord::Base
before_create :calculate_onshelf_time
default_scope :order => 'items.onshelf_at DESC'
def calculate_onshelf_time
self.onshelf_at = DateTime.now
end
end
In the user model test, I tried to convince myself the retrieved order is indeed items.onshelf_at DESC. So I made the following snippet, but the result turned out to be reverse. (namely [#item1, #item2])
# spec/models/user_spec.rb
before :each do
#user = User.create(#attr)
#item1 = Factory(:item, :owner=>#user, :onshelf_at => 2.days.ago, :created_at => 2.days.ago)
#item2 = Factory(:item, :owner=>#user, :onshelf_at => 1.day.ago, :created_at => 1.day.ago)
end
it "should have the right items in the right order" do
#user.items.should == [#item2, #item1]
end
I checked the console, and found that onshelf_at wasn't listening to the instance initialization of Factory Girl. In its stead, it followed before_create rule, and valued to the time when test was run!
Failure/Error: #user.items.should == [#item2, #item1]
expected: [#<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>]
got: [#<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>] (using ==)
How can I fix this?
A quick fix would be to add a condition to your calculate_onshelf_time method:
self.onshelf_at = DateTime.now if self.onshelf_at.nil?
But - you don't even need all this. If you can (that is, if you have control over the schema), replace the onshelf_at attribute with a created_at column, which will automatically be set by Rails at the time of creation. If you're using migrations:
create_table :foo do |t|
# ...
t.timestamps
end
will add created_at and updated_at timestamps to the model.

Rails ActiveRecord :counter_cache not updated if assocation not set up before create. Intentional?

I've implemented a belongs_to relation with :counter_cache => true and I notice that the counter cache does not get updated if the relation was not set up before the initial save.
For instance, say a company has_many employees. If I do
company.employees << Employee.new(:name => "Joe")
The counter gets updated correctly but if I do
company.employees << Employee.create(:name => "Joe")
The counter remains unchanged.
For more details, here are the models:
class Employee < ActiveRecord::Base
belongs_to :company, :counter_cache => true
end
class Company < ActiveRecord::Base
has_many :employees
end
And here's a Rails Console session that demonstrates this:
Loading development environment (Rails 3.0.5)
ruby-1.9.2-p180 :001 > company_a = Company.create(:name => "ACME")
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 0>
ruby-1.9.2-p180 :002 > company_a.employees << Employee.new(:name => "Bob")
=> [#<Employee id: 1, company_id: 1, name: "Bob", created_at: "2011-07-22 01:31:59", updated_at: "2011-07-22 01:31:59">]
ruby-1.9.2-p180 :003 > company_a.reload
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 1>
ruby-1.9.2-p180 :004 > company_a.employees << Employee.create(:name => "Joe")
=> [#<Employee id: 1, company_id: 1, name: "Bob", created_at: "2011-07-22 01:31:59", updated_at: "2011-07-22 01:31:59">, #<Employee id: 2, company_id: 1, name: "Joe", created_at: "2011-07-22 01:32:28", updated_at: "2011-07-22 01:32:28">]
ruby-1.9.2-p180 :005 > company_a.reload
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 1>
The documentation does say that the counter is incremented/decremented when the object is created/destroyed but I was thinking it should monitor updates as well to be useful. Otherwise, say, moving employees between companies would quickly result in counters that are totally off.
Is this the expected behavior? If so, what's the rationale? And if not, am I doing something wrong? I tried this in Rails 3.0.5 and Ruby 1.9.2
Thanks!

weird behavior with acts_as_taggable_on

Edit
The instructions on Github instruct you to use the gemcutter source for the gem. Currently, this installs version 2.0.5 which includes the bug I've detailed below.
#Vlad Zloteanu demonstrates that 1.0.5 does not include the bug. I have also tried with 1.0.5 and confirm that the bug does not exist in this version. People struggling with acts_as_taggable_on and owned tags on 2.x, rollback and wait for a fix..
For some reason, tags aren't showing up on a taggable object when an tagger is specified.
testing the post
class Post < ActiveRecord::Base
acts_as_taggable_on :tags
belongs_to :user
end
>> p = Post.first
=> #<Post id: 1, ...>
>> p.is_taggable?
=> true
>> p.tag_list = "foo, bar"
=> "foo, bar"
>> p.save
=> true
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">]
testing the user
class User < ActiveRecord::Base
acts_as_tagger
has_many :posts
end
>> u = User.first
=> #<User id: 1, ...>
>> u.is_tagger?
=> true
>> u.tag(p, :with => "hello, world", :on => :tags)
=> true
>> u.owned_tags
=> [#<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
refresh the post
>> p = Post.first
=> #<Post id: 1 ...>
>> p.tags
=> [#<Tag id: 2, name: "bar">, #<Tag id: 1, name: "foo">]
Where's the hello and world tags? Miraculously, if I modify the database directly to set tagger_id and tagger_type to NULL, the two missing tags will show up. I suspect there's something wrong with my User model? What gives?
EDIT
Even stranger:
Post.tagged_with("hello")
#=> #<Post id: 1, ...>
It finds the post! So it can read the tag from the database! How come it's not showing up with Post#tags or Post#tag_list?
I recreated your project, using exactly the same classes.
This is my result:
>> Post.create
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p = Post.first
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p.is_taggable?
=> true
>> p.tag_list = "foo, bar"
=> "foo, bar"
>> p.save
=> true
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">]
>> User.create
=> #<User id: 1, created_at: "2010-05-18 09:17:02", updated_at: "2010-05-18 09:17:02">
>> u = User.first
=> #<User id: 1, created_at: "2010-05-18 09:17:02", updated_at: "2010-05-18 09:17:02">
>> u.is_tagger?
=> true
>> u.tag(p, :with => "hello, world", :on => :tags)
=> true
>> u.owned_tags
=> [#<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
>> p = Post.first
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">, #<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
Therefore, I can not replicate your bug. I've tried it with both mysql and sqlite.
This is from my env file:
config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"
This is my gem version:
gem list | grep taggable
mbleigh-acts-as-taggable-on (1.0.5)
Can you post your gem version? Can you try to upgrade your gem? What DB are you using?
If it doesn't work, can you also post the output from tail -f log/development.log ?
EDIT: I'm using Rails 2.3.5
mbleigh released a commit that resolve this problem.
To obtain ALL the tags you can use owner_tags_on :
p = Post.first
p.owner_tags_on(nil, :tags )
This is the commit: http://github.com/mbleigh/acts-as-taggable-on/commit/3d707c25d45b5cc680cf3623d15ff59856457ea9
bye,
Alessandro DS

Resources