How to set an attribute to Array in Model? - ruby-on-rails

Can you teach me how to set an attribute to Array in Model?.
I have tried it but when I use array's method like push, each, I got the error undefined method `push' for nil:NilClass
My migrate look like this:
class CreateContacts < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :name
t.string :email
t.string :email_confirmation
t.integer :city_ids, array: true
t.text :description
t.timestamps
end
end
end
I would like to set attribute city_ids to Array.

One important thing to note when interacting with array (or other mutable values) on a model. ActiveRecord does not currently track "destructive", or in place changes. These include array pushing and poping, advance-ing DateTime objects.
Example
john = User.create(:first_name => 'John', :last_name => 'Doe',
:nicknames => ['Jack', 'Johnny'])
john = User.first
john.nicknames += ['Jackie boy']
# or
john.nicknames = john.nicknames.push('Jackie boy')
# Any time an attribute is set via `=`, ActiveRecord tracks the change
john.save
Referrence - link

You need to set a default. Otherwise, the attribute is nil, until you give it a value:
t.integer :city_ids, array: true, default: []
Or, you need to give it a value befor eyou attempt to use it:
c = City.find(...)
c.city_ids ||= []
c.city_ids.push(...)

Related

Rails create new record with draft status

I've got a portfolio model with following fields:
name: string (required)
status: string (required) one of: draft, active, funded
One of the requirement is that a newly created portfolio should have a status draft. I could set a default value inside of migration something like:
create_table :portfolios do |t|
t.string :name, null: false
t.string :status, null: false, default: 'draft'
t.timestamps
end
But I don't think it will easy to maintain. Of course I could set this status inside create method like:
Portfolio.create!(
name: params[:name],
status: 'draft'
)
Is there a better way to create such record? maybe some method inside of model?
class Portfolio < ApplicationRecord
after_initialize do
self.name = "draft"
end
end
I think it's better to do it using after_initialize because this callback will guarantee that the default value will be there from the very beginning of the life cycle of the object
Portfolio.new.name
#shoudl give you draft

Get the most common column value between all records

I'm trying to return the most common text value (country) from the database.
Whilst my first attempt is not producing any errors, I suspect it's not returning the most common value:
#mostpopularroast = Roast.group(:id).order("count(country) DESC").first
Interesting it gives me the exact same result should I use ASC instead.
I'm therefore now trying a solution from this similar question:
#mostpopularroast = Roast.group('country').order('count(*)').limit(1).pluck(:country).first
But this is giving me the error undefined method 'country' for "Country 1":String. Country 1 being the value in the db.
my model
class CreateRoasts < ActiveRecord::Migration[5.1]
def change
create_table :roasts do |t|
t.string :roaster
t.string :name
t.string :country
t.string :region
t.string :bestfor
t.string :beans
t.string :roast
t.string :slug
t.timestamps
end
end
end
You should apply descending ordering to get the most popular country:
Roast.group(:country).select(:country).order("count(*) desc").first.country
Your initial error is not related to this, it is just that you are using pluck, which returns Array object, then you are calling first method on it and getting String object, which is the object that is already the name of the most popular roast country, but then you are trying to call country on it, which results in the exception.

Querying the database passing multiple parameters rails

I have a user table and an activity table. The user has many activities. This is what i have in my users table:
class SorceryCore < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :surname
t.integer :previous_award
t.integer :chosen_award
t.string :email, :null => false
t.string :crypted_password
t.string :salt
t.timestamps
end
add_index :users, :email, unique: true
end
This is what I have in my activities table:
class CreateActivities < ActiveRecord::Migration
def change
create_table :activities do |t|
t.integer :activity_type
t.string :activity_name
t.string :approver_email
t.references :users, index: true, foreign_key: true
t.timestamps null: false
end
end
In my view I want to show the activity_name, where user_id = the current user's id, and where the the activity_type = 1. I'm not sure where to write this method or how to call it either. I have read through the following link but can't seem to get anything working. http://guides.rubyonrails.org/active_record_querying.html
I think I need to use something along the lines of this:
Activity.where("activity_type <= ?", 1).where("user_id <= ?", current_user.id)
But I'm not sure if this is supposed to go into a method in the controller or the model, and I'm not sure which controller or model it's supposed to go into
In the User model:
# user.rb
def activity_by_type(type = 1)
self.activities.where(activity_type: type).first
end
and then, you can call current_user.activity_by_type(<specify the type here>)
You can use the above method to get any of the activity type for the specified user, by specifying the type in the method call.
One advice I'll give though is to try and use the concept of enums to categorize your activity_type column in the activities table. An example on how, can be found here.
You simply have to query on the current_user.activities association:
#activities = current_user.activites.where(activity_type: "1")
You could also use a scope (which is what SunnyK recommended):
#app/models/user.rb
class User < ActiveRecord::Base
has_many :activities
scope :by_type, ->(type = 1) { activities.where(activity_type: type) }
end
--
If you only wanted to return a single record, you'd have to replace .where with .find_by:
#activity = current_user.activities.find_by activity_type: 1
--
Since you're using an enum, you may wish to use the built-in class method that you'd be able to call:
enum activity_type: [:sports, :photography]
#activity = current_user.activities.sports

ActiveRecords returning model object from a join table

I have the following DB for a simple flash cards example i'm building:
create_table "card_associations", :force => true do |t|
t.integer "card_id"
t.integer "deck_id"
end
create_table "cards", :force => true do |t|
t.string "question"
t.string "answer"
end
create_table "decks", :force => true do |t|
t.string "name"
t.string "description"
end
I've setup the has_many through relationships in all my models.
Now I want to be able to return a list of all cards from the join table, given the deck id.
If I run the query:
CardAssociation.find_by_deck_id(3).card
It retruns the first card with the deck_id of 3. But when I try.
CardAssociation.find_all_by_deck_id(3).card
I get the error
NoMethodError: undefined method `card' for #
Can someone help me with this? I feel like i'm making a very simple mistake.
Thanks for the help
The find_all_* methods always return an Array (which could be empty)!
CardAssociation.find_all_by_deck_id(3) # => Array of results
CardAssociation.find_all_by_deck_id(3).first # => first result of the Array or nil if no result
I advise you to first read the Ruby on Rails Style Guide, and then use the Rails3 way of finding object with ActiveRecord:
CardAssociation.where(:deck_id => 3) # => Array of results
CardAssociation.where(:deck_id => 3).first # => first result of the Array if exists
In your case, a scope can be set up on the Card model:
You said: "Now I want to be able to return a list of all cards from the join table, given the deck id"
class Card < ActiveRecord::Base
scope :for_deck, lambda { |deck| joins(:card_associations).where('card_associations.deck_id = ?', deck.try(:id) || deck) }
end
This scope can be used like following:
Card.for_deck(deck) # returns an Array of Card objects matching the deck.id
As defined in the scope, the parameter of Card.for_deck(deck) can be a deck object or a deck_id (type Integer)
Hope this helped!

Can't get a non-mass-assignment value to set or save

frustrating simple problem here. I'm using Rails 3.1 and have the following class:
class Job < ActiveRecord::Base
attr_accessor :active
attr_accessible :roleTagline, :projectTagline, :projectStartDate, :projectDuration, :postedStartDate,
:postedEndDate, :skillsRequired, :skillsPro, :experiencedRequired, :description, :active
scope :is_active, :conditions => {:active => 1}
validates :roleTagline, :presence => true,
:length => { :minimum => 5 }
validates :projectTagline, :presence => true,
:length => { :minimum => 5 }
belongs_to :job_provider
# def active=(act)
# #active = act
# end
end
In my controller, I'm trying to create a Job using mass-assignment (one of the ActiveRecord build helpers), then afterwards set the "active" attribute to 1 and then save the Job. Here's the controller code:
def create
#job_provider = current_user.job_provider
#job = #job_provider.jobs.build(params[:job])
#job.active= 1 # debug logging #job.active here gives an empty string
if #job.save # etc.
I've tried all combinations of removing attr_accessor and writing my own setter instead, but whatever I do I can't seem to find the correct combination to get the attribute to stay on the model. It's not active record I don't think because even before the #job.save the attribute is gone (from using debug logging). I've googled around but can't figure out what I'm doing wrong. Can anyone help please?
Edit: schema.rb from rake:
create_table "jobs", :force => true do |t|
t.string "roleTagline"
t.string "projectTagline"
t.date "projectStartDate"
t.integer "projectDuration"
t.date "postedStartDate"
t.date "postedEndDate"
t.string "skillsRequired"
t.string "skillsPro"
t.string "experiencedRequired"
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "active"
t.integer "job_provider_id"
end
Another edit:
Ok after even more Googling I finally found the answer to this here:
How can I override the attribute assignment in an active record object?
If you're modifying an ActiveRecord attribute and not a class instance, you need to do:
self[:fieldname] = value
Remove attr_accessor :active from the model. It is causing the value to be saved in an instance variable rather than to the database via the attributes hash. You don't have to write an accessor, because ActiveRecord does that automatically for you, based on the active column.
It's showing up blank in the debug log because it is initialized to nil. If you remove the attr_accessor line and change the active column in the database to NOT NULL DEFAULT 0, it will be initialized to 0. i.e. :null => false, :default => 0 in an ActiveRecord migration or schema.rb.
I think you didn't add the job_provider_id to the att_accessible. Try the following
attr_accessible :roleTagline, :projectTagline, :projectStartDate, :projectDuration, :postedStartDate, :postedEndDate, :skillsRequired, :skillsPro, :experiencedRequired, :description, :active, :job_provider_id

Resources