Showing posts with label ActiveRecord. Show all posts
Showing posts with label ActiveRecord. Show all posts

Wednesday, January 30, 2008

ActiveRecord callbacks execute sequence

before_validation
before_validation_on_create
after_validation
after_validation_on_create
before_save
before_create
insert operation
after_create
after_save

ActiveRecord callback instance methods execute ways on create will revok before_validation_on_create callback before ActiveRecord validation methods, and before_create callback will revoke after ActiveRecord validation methods and helpers, like validates_associated, validates_each.
If a new record has some error, and will not be validated by ActiveRecord validation methods and helpers, then after_validation and before_create callback will not be revoked.

Wednesday, October 17, 2007

Rails APP Error : wrong argument type Mysql (expected Data)

主要是ActiveRecord和Mysql连接的问题,更新一下ActiveRecord就可以。
$> gem update rails -v 1.2.3

Sunday, September 16, 2007

ActiveRecord style Javascript REST

Jester 1.2: Flexible REST

This release is about making Jester more flexible, and better supporting custom REST APIs. The flurry of activity in ActiveResource is a good reminder that REST isn’t just several default controller actions—it’s a guiding philosophy to defining your own API. REST is just about using simple URLs and HTTP status codes to carry all the metadata, so that the bodies of your requests and responses don’t have to.

New features this release:

find() will take a hash of query parameters to append to the URL.
Base.model() takes all parameters after the name as a hash, instead of a list.
Asynchronous calls can take a hash of callbacks and options, instead of just a callback.
Support for reading an object’s schema. (new.xml)
Dates are parsed into Date objects. (Thanks to Nicholas Barthelemy)
Objects are reloaded from XML if XML is returned after a create or update request.
Included ObjTree.js inline, and replaced prototype.js with an optional lighter form (prototype.jester.js).

You can pass arbitrary query parameters along with a find request, in a hash. It’s the second parameter, bumping the asynchronous callback/options parameter to third. This breaks backwards compatibility.

>>> User.find("all", {admin: true, toys: 5})
GET http://localhost:3000/users.xml?admin=true&toys=5

Arguments when defining a model are now taken as a hash instead of a plain ordered list. This breaks backwards compatibility.

>>> Base.model("User", {plural: "people", prefix: "http://www.thoughtbot.com"})
>>> User._plural_url()
"http://www.thoughtbot.com/people.xml"

ou can now supply a hash of options to be fed directly to Prototype’s Ajax.Request method, instead of just a callback. You can use this to specify different callbacks for success or failure conditions, or override the HTTP method used in the request, or anything specified here. If you supply only a callback, it will be treated as your “onComplete” callback. You can use these options with a synchronous request if you set “asynchronous” to false.

>>> User.find(1, {}, {onSuccess: success, on404: notFound, method: "post"})
POST http://localhost:3000/users/1.xml

>>> User.find(1, {}, successCallback)
GET http://localhost:3000/users/1.xml

>>> eric = User.find(1, {}, {asynchronous: false})
GET http://localhost:3000/users/1.xml
There is a longstanding problem in Jester, which is that when you create or build a new object, you have to specify all of its properties in the attributes hash. If you simply call eric = User.build(), and then later, eric.name = "Eric", the User model has no way of knowing this is a model attribute, and it won’t be included in any save() requests. ActiveResource gets around this by using Ruby’s method_missing, which isn’t an option for clients written in many languages.

After talking it over with my coworkers, we realized there was value in giving a REST client access to a model’s schema, much as ActiveRecord has database access to ascertain a model’s schema. So to I proposed that Rails introduce into their standard REST controllers the “new.xml” route, and a patch with it, which was accepted this week.

Jester supports this, but it is disabled by default, as it will incur an HTTP request when you may not expect it, and your code may work fine without it. It also currently only works synchronously. You can trigger it by passing an option to build in a second hash parameter.

>>> eric = User.build({}, {checkNew: true})
GET http://localhost:3000/users/new.xml

>>> eric._properties
["active", "email", "name"]

>>> eric = User.build({lasers: 1000}, {checkNew: true})
GET http://localhost:3000/users/new.xml

>>> eric._properties
["active", "email", "name", "lasers"]
Dates are now parsed into actual JavaScript Date objects when a model is loaded from XML, thanks to code contributed by Nicholas Barthelemy. (SVN)

>>> post = Post.find(1)
GET http://localhost:3000/posts/1.xml
>>> post.created_at
Sat Mar 31 2007 03:01:56 GMT-0500 (Eastern Standard Time)
If a create or update request results in an XML response body, the model will reload itself using this XML. So, if your app changes an object on create or on update, this will be reflected in the client, as long as your controller renders the object in XML after saving it. Props to Nicholas Barthelemy for pointing out this was important before DHH suddenly committed changes in ActiveResource to do the exact same thing.

Client:

>>> eric = User.create({email: "emill@thoughtbot.com", name: "Eric Mill"})
POST http://localhost:3000/users.xml

>>> eric.unique_key
"frederick"
Controller:


def create
@user = User.new(params[:user])
@user.unique_key = "frederick"

respond_to do |format|
if @user.save
headers["Location"] = user_url(@user)
format.xml { render :xml => @user.to_xml, :status => 201}
end
end

You no longer need to include ObjTree.js in your own HTML. I took the parts of ObjTree I used, packed them using Dean Edwards’ packer script, and appended them to jester.js. In the same vein, I removed prototype.js from the repository, and replaced it with a smaller form of prototype, prototype.jester.js. Use this if you aren’t using Prototype for anything besides Jester, and want quicker loading times.

There were also some little fixes. If a resource is found by its ID, the ID will definitely be set in the object’s properties, even if the returned XML didn’t include it. Also, the ID is set more correctly when parsed out of a Location header, though I doubt this issue affected anyone, as it still worked fine in practice in most cases. There was also a bug in ObjTree where empty attributes (i.e. ””) weren’t even counted.


There’s still some significant work left for Jester, and I’m sure even more great ideas will come out of you guys, and out of the ActiveResource team. My targets for the next release, in order of importance:

Allowing “prefix options”, so that you can specify comments at /users/:user_id/comments.xml, instead of /comments.xml. Options would be passed inside the same hash as the query parameters, and separated by Jester. An example might look like:

// find all approved comments by user #1
>>> Comment.find("all", {user_id: 1, approved: true})
GET http://localhost:3000/users/1/comments.xml?approved=true
Calls to new.xml will only happen once per model, when the model is first declared, not on every call to build.
Support for “custom methods”, as specified in ActiveResource here
Support for manually specified URLs, as specified in ActiveResource here
Provide an option for a model so that Jester will submit POST and PUT request parameters as an XML body, instead of as form parameters.

Friday, September 14, 2007

ActiveRecord find_or_create methods


def create
company = params[:person].delete(:company)
@company = Company.find_or_create_by_name(company[:name])
@person = @company.people.create(params[:person])

respond_to do |format|
format.html { redirect_to(person_list_url) }
format.js
format.xml { render :xml => @person.to_xml(:include => @company) }
end
end

Added find_or_initialize_by_X which works like find_or_create_by_X but doesn’t save the newly instantiated record. [Sam Stephenson]
Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:

Comment.constrain(:creation => { :post_id => 5 }) do
# Associated with :post_id
Comment.create :body => "Hello world"
end

This is rarely used directly, but allows for find_or_create on associations. So you can do:

# If the tag doesn't exist, a new one is created that's associated with the person
person.tags.find_or_create_by_name("Summer")

Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:

class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, *last_name = name.split
last_name = last_name.join " "

find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end

person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"

Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).

Sunday, August 26, 2007

Rails ActiveRecord Observer usage

要使Observer工作,第一种做法是在controller里声明, 这样unit test就无法加载此observer到console中


# app/models/flower_observer.rb
class FlowerObserver < ActiveRecord::Observer
observe Flower

def after_create(model)
# model.do_something!
end
end

# controller(s)
class FlowerController < ApplicationController
observer :flower_observer
end

第二种做法可以加载observer到console中:

# app/models/foo_bar.rb
class FooBar < ActiveRecord::Base
end

FooBarObserver.instance

最后一个方法是在config/environment.rb中加载此observer:

config.active_record.observers = :flower_observer


参考RobbyOnRails

model callbacks of Rails ActiveRecord


class Encrypter
def before_save(model)
model.name.tr! 'a-z', 'b-za'
end

def after_save(model)
model.name.tr! 'b-za', 'a-z'
end

# alias_method :after_find, :after_save
# alias_method
# Makes new_id a new copy of the method old_id. This can be used to retain access to
# methods that are overridden.

alias after_find after_save
# Aliasing
# alias new_name old_name
# creates a new name that refers to an existing method, operator, global variable, or reg-
# ular expression backreference ($&, $`, $', and $+). Local variables, instance variables,
# class variables, and constants may not be aliased. The parameters to alias may be
# names or symbols.

end

class ActiveRecord::Base
def self.encrypted
encrypt = Encrypter.new
before_save encrypt
after_save encrypt
after_find encrypt
module_eval <<-"end_eval"
def after_find
# Unlike all the other callbacks, after_find and after_initialize will only be run
# if an explicit implementation is defined (def after_find).
end
end_eval
end
end

class User < ActiveRecord::Base
encrypted
protected
def validate_on_create()
puts 'existed user name' if self.class.exists? :name => name
end
end

Saturday, May 12, 2007

SQL Transaction in Rails

http://snippets.dzone.com:80/posts/show/3995


def fetch_value
sql = ActiveRecord::Base.connection();
sql.execute "SET autocommit=0";
sql.begin_db_transaction
id, value =
sql.execute("SELECT id, value FROM sometable WHERE used=0 LIMIT 1 FOR UPDATE").fetch_row;
sql.update "UPDATE sometable SET used=1 WHERE id=#{id}";
sql.commit_db_transaction

value;
end

Thursday, April 12, 2007

Faking cursors in ActiveRecord

Faking cursors in ActiveRecord
from the { buckblogs :here } - Home by Jamis

There are times (like, in a migration, or a cron job) where I want to operate on large numbers of rows in the database, such as for billing, where you want to select all accounts who are due for automatic renewal, or when adding a new column to a table that you need to prepopulate with computed data.

One way to do that is just to brute force it:
Account.find(:all).each do |account|
# ...
end
The drawback here is obvious: when you’re dealing with hundreds of thousands or even millions of rows, selecting them all into memory at once is brutal. And since ActiveRecord doesn’t support cursor-based operations, you can’t just ask ActiveRecord to return the rows as it reads them.

Here’s a trick I’ve been using recently to query large result sets while being friendly to the computer:


class < def each(limit=1000)
rows = find(:all, :conditions => ["id > ?", 0], :limit => limit)
while rows.any?
rows.each { |record| yield record }
rows = find(:all, :conditions => ["id > ?", rows.last.id], :limit => limit)
end
self
end
end

Account.each do |account|
# ...
end

Sadly, this won’t work on every DBMS, or with every query; it exploits several idiosyncrasies of MySQL which might not be present on other DBMSs:

* MySQL sorts indexes.
* The primary key is an index.
* Queries which MySQL determines can be best satisfied by the primary key, then, will be returned in sorted order.

This means that if you try to add additional conditions to the query, you’ll also need to add an :order clause to sort by the id…and this will more than likely cause the performance of the query to go down the tubes. But for those queries where you just want to select every row anyway, it works quite well. You could use OFFSET and LIMIT, but OFFSET begins to be really, really slow when the OFFSET is in the tens of thousands or higher because it has to count through that many rows before finding where to begin returning data. Basing the query on id, like this, has the advantage of speed, because the database can use indexes like it was meant to.