ActiveRecord Delete...? - sql

I'm just starting to learn ActiveRecord, and I am just trying out little things to figure out how everything works. I just tried the following code on the following sqlite3 database.
Ruby:
class Balances < ActiveRecord::Base
def initialize
#balance = 50
update_attribute(:balance, #balance)
end
def withdraw amount
update_attribute(:balance, #balance-amount)
end
end
SQL:
CREATE TABLE balance(
balance 50
);
When I write:
balance = Balances.new
I Get:
NoMethodError: undefined method `delete' for nil:NilClass
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/write.rb:28:in `write_attribute'
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/dirty.rb:67:in `write_attribute'
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/write.rb:14:in `balance='
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/persistence.rb:180:in `update_attribute'
Why is this? Am I doing something wrong?

I notice several things:
The class name should be Balance (capitalized, singular). The table name in the database will be lower case, plural. Eg, balances
Don't define an initialize method for an ActiveRecord model. Instead use after_initialize callback. A post. Also, the Rails docs.
Added Also, the file name for the model should be balance.rb (lower case, singular)
Added some more You probably don't want to change the balance of the record back to 50 every time an instance of the record is initialized. -- That's what your example is currently doing. If you want to set the opening balance of new records in the database to be 50, then use the "before_create" callback.
Remember that ActiveRecord model classes are associated with, but different from, the records in the underlying database. For example, you can create an instance of an ActiveRecord model, and then not create a matching record in the database. -- The database record will only be created when and if you call the save or create methods.

Related

Rails Active Records using maximum and minimum simultaneously

Below both the query work fines
Event.joins(:visit).where(:page_id => 3).group(:visit_id).minimum('events.time')
Event.joins(:visit).where(:page_id => 3).group(:visit_id).maximum('events.time')
I want to do find the diff maximum('events.time') - minimum('events.time') and group by visit id
For this I am writing below query
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(maximum('events.time') - minimum('events.time'))
I am getting error, (undefined method maximum for main:Object)
can anyone help me out for this active records query
That's because you're trying to call maximum and minimum as though they were helper or instance methods. You have to call them on an instance of an ActiveRecord object. In your case I'm guessing that you have class Event < ActiveRecord::Base at the top of your model file.
Even if you change your code to use the ActiveRecord methods, you still have malformed Ruby code at (:visit_id).(maximum. So if the calculation performs and you get a date your code would look like this:
Event.joins(:visit).where(:page_id => 3).group(:visit_id).(SomeTimeStamp), which will throw a different error.
You'll have to rework your query either way, but the important thing to note is that you can't call minimum or maximum as helper methods.

In a Rails ActiveRecord model, is using after_initialize callbacks a very bad idea?

Let's suppose we have this model
class Account < ActiveRecord::Base
after_initialize :set_name
def set_name
self.name = ‘My Account’
end
end
Now I want run a query that returns only some attributes of the model but not all of them, in particular is not returning the "name" attribute that it is used in after_initialize callback
Account.group(:name).select("count(*), id").first
And then this execution raises the following error because the set_name callback uses an attribute that has not been "loaded" or selected into the records returned by the query.
ActiveModel::MissingAttributeError: missing attribute: name
Fortunately for some particular cases I can execute the same sql query without using the Account model at all to get the desired result
sql = Account.group(:name).select("count(*), id").to_sql
ActiveRecord::Base.connection.execute(sql).first
=> #<Mysql2::Result:0x00000106eddbc0>
But the point is, what if I want to get Account objects instead of a Mysql2::Result one? Should the .select method return "complete" objects with all their attributes (e.g. filling the missing columns with Nil's)? Or is just a very bad idea to use after_initialize callbacks for our ActiveRecord models? Of course we can also add some code in the callback to check if the property exists or not but, in my opinion, this is unnatural or sounds weird working in an OO language.
Most uses of after_initialize can be (and SHOULD be) replaced with defaults on the corresponding database columns. If you're setting the property to a constant value, you may want to look into this as an alternative.
EDIT: if the value isn't constant, a call to has_attribute?(:name) will guard against this error - ActiveModel::MissingAttributeError occurs after deploying and then goes away after a while
No, it is not a bad idea, in fact I use it very often at work. The valid use case for this would be when you want code to run before you try and do anything with the object. Here is a breakdown of some of the filters offered.
# Before you intend to do anything with the object
after_initialize
# Before you intend to save the object
before_save
# After you've saved the object
after_save
# Before you save a new record
before_create
# After you create a new object
after_create

Scope staticly reference another model resulting table not found

I use in-memory database for testing. The schema is reloaded in every test.
Recently my rspec complains that a table is not found. The reason is that a scope is referencing another model at load time.
class Item
scope :public, where(:store_id => Store.public_store_ids())
class Store
def self.public_store_ids
self.public.pluck(:id)
The problem is that, during the initializing when item model is loaded in the memory, the schema for store table has not been loaded yet, but my scope will try to query the public store ids, which results in the "table not found" error.
How can I make my item scope to evaluate dynamically at runtime? I didn't want to use join because it can slow down my query, but would it be my only way?
I realized that I can just make it a class method so it is evaluated at run time
def self.public
store_ids = BeautyStreet::Store.public_store_ids()
where(:store_id => store_ids)
end

Finding Records using Rails without a Match (Fault without Fault Cleared)

I'm attempting to write a site in Rails where a user in a manufacturing plant can see what devices are failing. The program storing the alarm data stores one entry when a device faults, and then stores another entry when the device gets fixed. The entries are linked only by having the same value in the EventAssociationID column. How might I write a named scope in Rails to check which faults have been fixed and which ones haven't?
I wasn't able to do it in a named scope, however, I was able to define a method for the model that solved the problem:
def inAlarm
return ConditionEvent.count(:all, :conditions => ['EventAssociationID = ?', self.EventAssociationID]) == 1
end

Rails 3 - how do you use methods like where and sum on proxy_targets in association extension methods?

in my app, 2 peope can be friends via a friendships model and they can create records of how much money they lent to the other friend.
I want to create something like this
#friendship.receipts.balance_for(#user) which will return how much #user owes to or is owed by their friend in this friendship. i.e. if it returns -50 they owe $50 or if its positive they are owed $50
Id also like to be able further scope this for use in live filtering (by dates, tags etc) i.e. would like this
#friendship.receipts.where(:tag => "groceries).balance_for(#user)
My first attempt was to create an association extension on the friendship model
class Friendship < ...
has_many :receipts do
def balance_for(user)
user_total_spent = proxy_target.sum(:value, :payer_id => user.id)
friend_total_spent = proxy_target.sum(:value, :payer_id => friend.id)
return user_balance = user_total_spent - friend_total_spent
end
end
end
but unfortunately the method sum cant be used on proxy_target because it returns a plain array and not the Activerecord::Relation class sum expects.
Whats a good workaround this??? Id like to create a method that can be called on scoped results.
You might want to try simply calling sum without an explicit receiver. I'm looking for the solution to a similar problem, and found what looks like the answer to your question here: http://withoutscope.com/2008/8/22/don-t-use-proxy_target-in-ar-association-extensions
Unfortunately, it doesn't solve my problem. I'm trying to figure out how to extend models retrieved through my association extension with a module where the module name is a member of the proxy's owner.