How do I declare model attributes in Rails 4? - ruby

I am trying to understand Rails and I dont understand how I declare the model attributes correctly. For now my user class is looking like this:
class User < ActiveRecord::Base
has_many :users # Friends
end
By Googling I have understand that before Rails 4 one could determine attributes with the attr_accessible, like this:
attr_accessible :firstname, :lastname, :age, :sex
But this seems to be deprecated, how can I do that same thing in Rails 4?

has_many :users is not a model attribute, its model association. It means model User can have many User objects, which is incorrect. (also does not make sense even literally)
What attr_accessible does?
Specifies a white list of model attributes that can be set via
mass-assignmen
To add attributes to a model, you need to generate migrations.
Example, lets add name attribute to users:
rails generate migration AddNameToUsers #creates a migration file to add `name` column to `users` table
followed by:
rake db:migrate # executes migration file creating `name` column in `users` table
Now you can access these attributes simply as:
user = User.new
user.name
Again, if you want to mass-assign this attribute at some point of your code, you will need to specify this in your class with attr_accessible, as in your original example.

Related

Sinatra with existing database (that doesn't abide by naming conventions)

I have an existing legacy Firebird database with nonstandard table and field names.
I would like to write a Sinatra app that can access it and display information. I've seen stuff like dm-is-reflective that appears to work when a database has proper naming conventions, but how do I use DataMapper (or ActiveRecord whichever is the easiest) to access those tables?
For example, assuming I had these two tables:
Bookshelfs
shelf_id: integer
level: integer
created: timestamp
Book
id: integer
id_of_shelf: integer
title: string
pages: integer
Something like with odd naming conventions that don't follow any set pattern and where one table's record might "own" multiple entries in another table even though there is not foreign_key assigned.
How would you set up datamapper (or activerecord) to communicate with it?
Look in to this gem to get setup with ActiveRecord on Sinatra:
https://github.com/bmizerany/sinatra-activerecord
As for how to define the relations, activerecord can do this easily.
class Book < ActiveRecord::Base
belongs_to :bookshelf, :class_name => 'Bookshelf', :foreign_key => 'id_of_shelf'
end
class Bookshelf < ActiveRecord::Base
has_many :books, :class_name => 'Book', :foreign_key => 'id_of_shelf'
end
Assuming you figured out how to connect to your legacy database using ActiveRecord's Firebird adapter, the next thing I would do is define a view on top of each table, e.g.
CREATE VIEW books AS SELECT * FROM Book;
CREATE VIEW bookshelves AS SELECT * FROM Bookshelfs;
This way you can simply define models Book and Bookshelf in ActiveRecord as per usual and it will find everything in the right place inside the database.

Rails has_many association results in a "no such column" error

I am following the guide at http://guides.rubyonrails.org/association_basics.html
and I have created
class Customer < ActiveRecord::Base
has_many :orders, :dependent => :destroy
end
class Order < ActiveRecord::Base
belongs_to :customer
end
but executing #order = #customer.orders.create() results in
unknown attribute: customer_id
Do you know why does this error occurs? And more importantly Is there a hidden reason for all the guides for has_many to drive you insane with showing this example, but non of them to be actually working :)
You need to add customer_id column to orders table.
For that you have to run the migration -
rails g migration add_customer_id_to_orders customer_id:integer
then
rake db:migrate
You're going to have to add a customer_id column to your orders table.
ActiveRecord doesn't know which customer to fetch for the relating order.
Try rails g migration AddCustomerIdToOrders customer_id:integer (don't forget db:migrate).
Sounds like you forgot to run your migrations. From the console and within the root directory of your rails project, run rake db:migrate to ensure rails has generated all the backing tables and columns for your associations and models.

How do you add new column named "unique_id", and associate mailboxer with devise?

I have a question.
Devise and mailboxer are installed on my rails app without any problem.
The problem is that you have to use "email" or "name" column to associate devise user with mailboxer.
Obviously, devise doesn't have column called "name" in Users table.
So, if you use "email" then users will see everyone else's email address that they wanna hide.
I want twitter-like registration.
They've got unique_id(account name) that never will be changed.
To achieve that, How can I?
I. Add column named "unique_id" to Users table?
command: rails g migration AddUniqueIdToUsers unique_id:string
to create migration file, and open and edit like this
class AddUniqueIdToUsers < ActiveRecord::Migration
def change
add_column :users, :unique_id, :string
add_index :users, :unique_id, :unique => true
end
end
II. How do I associate devise with mailboxer by using "unique_id" column?
Thanks in advance
Obviously, devise doesn't have column called "name" in Users table
That is entirely up to you, all Devise does (or wants you to do) is add a few records that tell it how to function. If you look at the devise generator you can see that all it does is add a couple of columns to your migration.
I want twitter-like registration. They've got unique_id(account name) that never will be changed. To achieve that, How can I?
First of all, a unique ID is always given to you by free in Rails (indeed, in most typical web applications using a database backend, each row has a unique ID).
But, if you also want users to select a username, and have that be unique as well, the you could do as the mailboxer readme states and simply override the usage of name with your own database column like username, like so:
Mailboxer.setup do |config|
# ...
#Configures the methods needed by mailboxer
config.email_method = :email
config.name_method = :username
# ...
end
Or, if you want to stay out of the mailboxer config file, you can simply use alias_method and do this (given that you have a username column):
class User < ActiveRecord::Base
alias_method :name, :username
acts_as_messageable
end
Either way, Devise doesn't restrict you in which columns you use on your User model, and it seems that Mailboxer also doesn't restrict you in which columns you use to attach the gem to the User model. So you have as much flexibility as you want without having to built this functionality by yourself.

Create a reference column for 2 existing models in rails 3

I am doing Rails 3.2.3 and have an issue:
I have 2 models Product and User already. The User table was created when I setup devise. I want to make product belong to user and here is what I did:
rails g migration AddUserToProduct user_id:integer
In product model, I add:
belongs_to :user
In User model, I add:
has_many :products
I ran
rake db:migrate
and i looks fine. To test that, I go to the web interface, log in and make a new product then using Rails console, I type in
Product.last
I am confused since user_id:nil which means there's no reference between the user and the product.
Is there anything wrong with my code ? What is the proper way to create a reference column between two existing Models ?
Thanks for your helps
Could you show your Products controller? Most likely, you have something like
def create
#product = Product.new(params[:product])
...
end
if so, change it to
def create
#product = current_user.products.build(params[:product])
...
end

DataMapper - why "has" and "belongs_to"?

I'm just getting started with DataMapper and I'm trying to figure out why you need to specify a has and a belongs_to.
For instance, look at the example on the DataMapper website. Isn't this redundant? If Post has n comments, then doesn't Comment automatically belongs_to post? Why do I have to specify this?
class Post
include DataMapper::Resource
property :id, Serial
has n, :comments
end
class Comment
include DataMapper::Resource
property :id, Serial
property :rating, Integer
belongs_to :post # defaults to :required => true
def self.popular
all(:rating.gt => 3)
end
end
You specify both sides of the relationship only when you want to use the methods generated by the extra specification. It's completely optional: If you never need to get to the Post from the Comment (e.g. #comment.post), you won't have to specify the belongs_to relation in Comment.
One advantage is that your instances are a bit cleaner because in Comment the additional methods are not autogenerated. On the other hand, if you need them, those extra methods would not bother you.
See also the documentation about associations in ActiveRecord.
That gives you the methods to access the relational object easily. Such as #post.comments #comment.post. I see what you mean, applying has_many could imply the belongs_to. Though given the developer overhead to add the belongs_to, is probably better than adding more system overhead to dynamically add the methods to the right class.
Another thing would be when using the has_many relation ship through another has_many relationship. That would cause odd belong_to relationships, and would probably cause issues with the SQL.
For example:
class User < ActiveRecord::Base
has_many :roles, :through => :roles_users
has_many :roles_users
end
The RolesUser being a join table that has belongs_to for the both the user and the role models. Implying the belongs to in this case would then add a belongs_to to the role model to a user. Nor does this not make sense, it also wouldnt work due to the lack of the database column being there. Of course this could be adjusted when the through option is there, but again this would highly raise the complexity of the code when it's not needed. As Daan said in his answer, you do not need both for it to work, it's optional.
I'd like to add to these good answers that if you have required dm-constraints (directly or via data_mapper) and use auto_migrate!, then belongs_to will automatically add foreign key constraints at the database level, whereas has alone will not do this.
e.g.:
require 'data_mapper'
class Post
include DataMapper::Resource
property :id, Serial
end
class Comment
include DataMapper::Resource
property :id, Serial
belongs_to :post
end
Produces this (via MySQL adapter):
~ (0.140343) CREATE TABLE comments (id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
post_id INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB
CHARACTER SET utf8 COLLATE utf8_general_ci
~ (0.234774) CREATE INDEX index_comments_post ON comments (post_id)
~ (0.433988) ALTER TABLE comments ADD CONSTRAINT comments_post_fk
FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE NO ACTION ON UPDATE NO ACTION
If you use has n, :comments within Post but choose not to include belongs_to :post within Comment, the post_id column in the comments table will still be created and indexed, but no foreign key constraint will be added.

Resources