I use rolify gem with devise for AdminUser
my Roles table
class RolifyCreateRoles < ActiveRecord::Migration
def change
create_table(:roles) do |t|
t.string :name
t.references :resource, :polymorphic => true
t.timestamps
end
create_table(:admin_users_roles, :id => false) do |t|
t.references :admin_user
t.references :role
end
add_index(:roles, :name)
add_index(:roles, [ :name, :resource_type, :resource_id ])
add_index(:admin_users_roles, [ :admin_user_id, :role_id ])
end
end
model 'Role'
class Role < ActiveRecord::Base
has_and_belongs_to_many :admin_users, :join_table => :admin_users_roles
belongs_to :resource,
:polymorphic => true
validates :resource_type,
:inclusion => { :in => Rolify.resource_types },
:allow_nil => true
scopify
end
my issue is when i want to get users witch belong to role it gives empty array instead of my adminuser object
u = AdminUser.first
u.add_role(:admin)
u.roles => #<Role id: 1, name: "admin", admin_user_id: 1, resource_id: nil, resource_type: nil, created_at: "2016-06-16 15:03:33", updated_at: "2016-06-17 09:04:30">
and when i do
Role.first=> #<Role id: 1, name: "admin", admin_user_id: 1, resource_id: nil, resource_type: nil, created_at: "2016-06-16 15:03:33", updated_at: "2016-06-17 09:29:32">
Role.first.admin_users => []
To check if a user has a global role:
user = User.find(1)
user.add_role :admin # sets a global role
user.has_role? :admin
=> true
Or to check at instance level In your case
u = AdminUser.first
u.add_role(:admin)
> Role.first=> #<Role id: 1, name: "admin", admin_user_id: 1,
> resource_id: nil, resource_type: nil, created_at: "2016-06-16
> 15:03:33", updated_at: "2016-06-17 09:29:32">
Instead of doing this => Role.first.admin_users => []
Try
u = AdminUser.first
u.roles
Rolify has a pretty clear documentation
Rolify
oh i fixed it by changing relation in my admin_user model
from has_many to has_and_belongs_to_many :roles
Related
let say that I have two class
class User
attr_accessible :name
has_one :address
validates :name, :presence => true
validates_associated :address
end
class Address
attr_accessible :country, :user_id
belongs_to :user
validates :country, :presence => true
validates :user, :presence => true
end
Now when i try to create invalid Address then it fails(which is good)
a = Address.new
a.valid? #=> false
But when i build User with invalid Address then it pass(which is bad)
u = User.first
u.build_address
u.valid? #=> true
u.save #=> true
Due to this User has Address with country => nil.
How can i tell Rails to not save Address if its invalid?
FIXED: I fixed this by adding follow line to the code. Thank you everyone.
validates_associated :address, :if => :address
class User
attr_accessible :name
has_one :address, :validate => true
validates :name, :presence => true
validates_associated :address
end
You need to also validate that an Address is actually present for User:
class User < ActiveRecord::Base
validates :address, :associated => true, :presence => true
end
With that in place, I get:
>> u = User.first
=> #<User id: 1, name: "Bob", created_at: "2013-10-09 15:17:21", updated_at: "2013-10-09 15:17:21">
>> u.build_address
=> #<Address id: nil, user_id: 1, country: nil, created_at: nil, updated_at: nil>
>> u.valid?
=> false
>> u.errors
=> #<ActiveModel::Errors:0x007fe1d6919b18 #base=#<User id: 1, name: "Bob", created_at: "2013-10-09 15:17:21", updated_at: "2013-10-09 15:17:21">, #messages={:address=>["is invalid"]}>
>> u.address.errors
=> #<ActiveModel::Errors:0x007fe1d69197a8 #base=#<Address id: nil, user_id: 1, country: nil, created_at: nil, updated_at: nil>, #messages={:country=>["can't be blank"]}>
I have the two rails models Section & SectionRevision. A Section is mostly just a container that holds all the Revisions relating to itself. So most of the attributes for the Section are basically stored within the SectionRevision model so there's a history of Revisions that can be reverted back to at any time.
Sometimes I need to access the attributes for the latest revision from the Sections Model so I've created some Virtual Attributes to account for this.
Each model has the attributes as defined in these migrations:
Section:
class CreateSections < ActiveRecord::Migration
def change
create_table :sections do |t|
t.integer "page_id", :null => false
t.timestamps
t.datetime "deleted_at"
end
add_index("sections", "page_id")
add_index("sections", "current_revision_id")
end
end
SectionRevision:
class CreateSectionRevisions < ActiveRecord::Migration
def change
create_table :section_revisions do |t|
t.integer "section_id", :null => false
t.integer "parent_section_id"
t.integer "position"
t.string "title", :default => "", :null => false
t.text "body", :null => false
t.timestamps
end
add_index("section_revisions", "section_id")
add_index("section_revisions", "parent_section_id")
end
end
And the models:
SectionRevision:
class SectionRevision < ActiveRecord::Base
belongs_to :section, :class_name => 'Section', :foreign_key => 'section_id'
belongs_to :parent_section, :class_name => 'Section', :foreign_key => 'parent_section_id'
def parsed_json
return JSON.parse(self.body)
end
end
Section:
class Section < ActiveRecord::Base
belongs_to :page
has_many :revisions, :class_name => 'SectionRevision', :foreign_key => 'section_id'
has_many :references
def current_revision
self.revisions.order('created_at DESC').first
end
def position
self.current_revision.position
end
def parent_section
self.current_revision.parent_section
end
def children
Sections.where(:parent_section => self.id)
end
end
As you can see Section has a couple of virtual attributes like, parent_section,current_revision & position.
The problem being now I would like to create a virtual attribute, children that selects all sections where the virtual attribute parent_section.id is equal to self.id. Is this possible at all? I know the above code won't work as its doing a query for a column that doesn't exist - and I'm not sure how to access the Model instances from within the model 'Sections' doesn't appear to work.
Can a perform a selection based on virtual attributes?
I've updated the model based on ProGNOMmers answer and get the following:
class Section < ActiveRecord::Base
has_many :revisions, :class_name => 'SectionRevision',
:foreign_key => 'section_id'
#Need to somehow modify :child_revisions to only be selected if it is the section_id's current_revision?
has_many :child_revisions, :class_name => 'SectionRevision',
:foreign_key => 'parent_section_id'
has_many :children, :through => :child_revisions,
:source => :section
end
Circumstance 1: This works perfectly fine.
1.9.3p392 :040 > section
=> #<Section id: 3, page_id: 10, created_at: "2013-04-02 01:31:42", updated_at: "2013-04-02 01:31:42", deleted_at: nil>
1.9.3p392 :041 > sub_section
=> #<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>
1.9.3p392 :042 > revision1
=> #<SectionRevision id: 5, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 10:21:46", updated_at: "2013-04-04 21:55:10", position: 3, parent_section_id: nil>
1.9.3p392 :043 > revision2
=> #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 21:55:15", position: 3, parent_section_id: 3>
1.9.3p392 :044 > sub_section.current_revision
SectionRevision Load (0.6ms) SELECT `section_revisions`.* FROM `section_revisions` WHERE `section_revisions`.`section_id` = 4 ORDER BY created_at DESC LIMIT 1
=> #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 21:55:15", position: 3, parent_section_id: 3>
1.9.3p392 :045 > section.children
=> [#<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>]
Circumstance 2:
1.9.3p392 :021 > section
=> #<Section id: 3, page_id: 10, created_at: "2013-04-02 01:31:42", updated_at: "2013-04-02 01:31:42", deleted_at: nil>
1.9.3p392 :022 > sub_section
=> #<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>
1.9.3p392 :023 > revision1
=> #<SectionRevision id: 5, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 10:21:46", updated_at: "2013-04-04 10:24:22", position: 3, parent_section_id: 3>
1.9.3p392 :024 > revision2
=> #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 12:29:19", position: 3, parent_section_id: nil>
1.9.3p392 :025 > sub_section.current_revision
SectionRevision Load (0.7ms) SELECT `section_revisions`.* FROM `section_revisions` WHERE `section_revisions`.`section_id` = 4 ORDER BY created_at DESC LIMIT 1
=> #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 12:29:19", position: 3, parent_section_id: nil>
1.9.3p392 :026 > section.children
Section Load (0.6ms) SELECT `sections`.* FROM `sections` INNER JOIN `section_revisions` ON `sections`.`id` = `section_revisions`.`section_id` WHERE `section_revisions`.`parent_section_id` = 3
=> [#<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>]
In circumstance 2 I would like section.children to return => [] as sub_section.current_revision.parent_section_id = nil and not section.id.
In other words section.children should return all Sections where .current_revision.parent_section_id = section.id but I can't query that as .current_revision is a virtual attribute.
Is it possible to maybe turn Section.current_revision in to some sort of association? Or maybe the only way is to add a current_revision column to the sections table?
I think custom relationships are well suited for this cases:
class Section < ActiveRecord::Base
has_many :revisions, :class_name => 'SectionRevision',
:foreign_key => 'section_id'
has_many :child_revisions, :class_name => 'SectionRevision',
:foreign_key => 'parent_section_id'
has_many :children, :through => :child_revisions,
:source => :section
end
Section.find(42).children
#=> SELECT ... WHERE ... AND section_revisions.parent_section = 42
I didn't tried the code, there could be mistakes, but the idea should be correct.
I deleted the part about :conditions, since is not useful after the last edits
Looks like you should improve your model as ProGNOMmers stated; you could use some of following gems:
ancestry ( http://railscasts.com/episodes/262-trees-with-ancestry )
awesome_nested_set ( https://github.com/collectiveidea/awesome_nested_set )
But answering your question literally, you can try adding 'children' method to your SectionRevision model and delegate Section#children to current_revision.
class SectionRevision
def children
SectionRevision.where(:parent_section => self.id) # parent_section IS a column of SectionRevision
end
end
class Section
def children
current_revision.children
end
end
BTW you could use #delegate for delegating:
class Section
delegate :children, :position, :parent_section, to: :current_revision
def current_revision
Section.where(:parent_section => self.id)
end
end
http://apidock.com/rails/Module/delegate
This might be a case of me being blind…
Somehow I cannot get my layout_positions_attributes to arrive at layout_positions
l = Layout.new
…
[17] pry(main)> l.layout_positions
=> []
[18] pry(main)> l.layout_positions_attributes=[{:name=>'xxx'}]
=> [{:name=>"xxx"}]
[19] pry(main)> l.layout_positions
=> [] # WTF!
[20] pry(main)> l.layout_positions.build({:name=>'xxx'})
=> #<LayoutPosition id: nil, name: "xxx", active: true, columns: nil, rows: nil, layout_id: nil, pos: -1>
[21] pry(main)> l.layout_positions
=> [#<LayoutPosition id: nil, name: "xxx", active: true, columns: nil, rows: nil, layout_id: nil, pos: −1>]
The parent class:
class Layout < ActiveRecord::Base
attr_accessible :name, :active, :layout_positions_attributes
has_many :layout_positions
accepts_nested_attributes_for :layout_positions,
:reject_if => lambda { |a| a[:content].blank? },
:allow_destroy => true
validates_presence_of :name
end
And where the attributes should go…
class LayoutPosition < ActiveRecord::Base
attr_accessible :name, :active, :columns, :rows, :pos
belongs_to :layout
validates_presence_of :name
end
You're telling it to reject it if the content for the layout_position object is blank. In your example, above "WTF", you're not passing through any content. Therefore this is deleting the attributes.
Calling build doesn't rely on accepts_nested_attributes_for and so will work just fine.
I apologize if the question title is confusing. I have the following situation: I have a Person model which stores people in the standard way in the people table.
I need to add a has_many relationship for emergency_contacts on the Person model. I tried doing this in the following way:
Migrations:
create_table :people do |t|
t.string :first
t.string :middle
t.string :last
t.timestamps
end
create_table :emergency_contacts, :id => false do |t|
t.integer :person_id
t.integer :emergency_contact_id
t.timestamps
end
Models:
class Person < ActiveRecord::Base
has_many :emergency_contacts
validates :first, :presence => true
validates :last, :presence => true
end
class EmergencyContact < ActiveRecord::Base
belongs_to :person
has_one :person, :foreign_key => 'emergency_contact_id'
end
This allows me to do:
ruby-1.9.2-p180 :001 > p = Person.new(first: "John", last: "Doe")
=> #<Person id: nil, first: "John", middle: nil, last: "Doe", created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :002 > ec = EmergencyContact.new
=> #<EmergencyContact person_id: nil, emergency_contact_id: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :003 > ec.emergency_contact = Person.new(first: "Peter", last: "Griffin")
=> #<Person id: nil, first: "Peter", middle: nil, last: "Griffin", created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :004 > p.emergency_contacts << ec
=> [#<EmergencyContact person_id: nil, emergency_contact_id: nil, created_at: nil, updated_at: nil>]
ruby-1.9.2-p180 :005 > p.save!
=> true
However, I don't feel the EmergencyContact model should have to be there, since I am really just referencing a Person model anyway.
Is there a way to remove this "middle-man" model, so that I can just do something like:
p = Person.new(first: "John", last: "Doe")
p.emergency_contacts << Person.new(first: "Peter", last: "Griffin")
I'd really use self referential associations for this kind of purpose.
Se tutorial here: http://railscasts.com/episodes/163-self-referential-association
This is probably basic stuff, but I really made my homework looking it up. I still can´t find the solution.
I have a users db, a movies db, and a ratings db. Ratings has a movie_id, user_id and grade.
class Rating < ActiveRecord::Base
attr_accessible :grade
belongs_to :user
belongs_to :movie
end
class User < ActiveRecord::Base
has_many :ratings, :dependent => :destroy
has_many :movies, :through => :ratings
...
class Movie < ActiveRecord::Base
has_many :ratings, :dependent => :destroy
has_many :users, :through => :ratings
...
But I can´t create a rating with both movie_id and user_id
rails c test
Loading test environment (Rails 3.0.3)
#movie=Factory(:movie)
#<Movie id: 1, title: "Rocky 20", link: "#", created_at: "2011-01-22 21:04:42", updated_at: "2011-01-22 21:04:42">
#user=Factory(:user)
#<User id: 1, name: "lola", email: "lola#gmail.com", created_at: "2011-01-22 21:04:48", updated_at: "2011-01-22 21:04:48", encrypted_password: "c306a696138fa08c543ada3a3b4fd92067e9941743b7558e891...", salt: "f82c6abaccec17e2866d50150ad200181eb4bc8e0204249f171...", admin: false>
>> #user.ratings.create(:grade=> 4, :movie_id =>#movie.id)
=> #<Rating id: 1, grade: 4, user_id: nil, movie_id: nil, created_at: "2011-01-22 21:04:55", updated_at: "2011-01-22 21:04:55">
that "movie_id: nil" is what is killing me...
then of course me tests are not passing:
#user.rating.create(grade => 5, :movie_id => #movie.id) has no movie_id
#movie.rating.create(grade => 5, :user_id => #user.id) has no user_id
any hints?
Thanks!!
The code you're showing here should absolutely work IMO. Which leaves me guessing that it's something you aren't showing here. A couple of things I'd try:
Use the regular console for a change
Don't use Factories (maybe it's them?), create actual entries instead
I've tried your example and here's what I got:
> m = Movie.create :title => "Foo"
=> #<Movie id: 1, title: "Foo", created_at: …>
> u = User.create :name => "Marc"
=> #<User id: 1, name: "Marc", created_at: …>
> u.ratings.create :grade => 4, :movie_id => m.id
=> #<Rating id: 1, grade: 4, user_id: 1, movie_id: 1, created_at: …>
> m.ratings.create :grade => 3, :user_id => u.id
=> #<Rating id: 2, grade: 3, user_id: 1, movie_id: 1, created_at: …>
Which you can also use like this, as you might know:
> m.ratings.create :grade => 3, :user => u
=> #<Rating id: 3, grade: 3, user_id: 1, movie_id: 1, created_at: …>
Try defining your factories like this:
Factory.define :movie, :class => Movie do |f|
f.title "Rocky 20"
f.link "#"
end
Factory.define :user, :class => User do |f|
f.name "Lola"
f.email "lola#gmail.com"
end
Factory.define :rating, :class => Rating do |f|
f.movie { |m| m.association :movie}
f.user { |u| u.association :user}
f.grade 4
end
Then to test a rating, use the rating factory by itself:
it "creates a rating" do
rating = Factory.create(:rating)
rating.user.name.should == "Lola"
rating.movie.title.should == "Rocky 20"
end