This is the 7th of 8 presentations given at University of Texas during my Beginner to Builder Rails 3 Class. For more info and the whole series including video presentations at my blog:
http://schneems.com/tagged/Rails-3-beginner-to-builder-2011
2. Rails - Week 7
• SQL Crash Course
• where & find
• method_missing
• scopes and class methods
• Pagination
• full text search
• Rake vs. Script/Runner
@Schneems
Sunday, July 31, 11
3. SQL
• Structured Query Language
• Used to manage data in RDBMS
(relational database management system)
• Active Record hides our SQL
User.where(:username => "foo").to_sql
=> SELECT "users".*
FROM "users" WHERE
"users"."username" = 'foo'
@Schneems
Sunday, July 31, 11
4. Find
• find by uniqe id, or ids
• find_by_#{:column_name}
User.find(1)
User.find(1,2,3)
User.find_by_username('schneems')
@Schneems
Sunday, July 31, 11
5. Method Missing
User.foo_foo
NoMethodError: undefined method `foo_foo' for #<Class:
0x105187be8>
@Schneems
Sunday, July 31, 11
6. Method Missing
class User
def self.method_missing(method_name, *args, &block)
puts "You just called #{method_name}"
end
end
User.foo_foo
>> "You just called foo_foo"
@Schneems
Sunday, July 31, 11
7. Method Missing
class User < ActiveRecord::Base
def self.method_missing(method_name, *args, &blk)
if method_name.to_s =~ /^foo_foo_find_by_.*/
column = method_name.to_s.gsub("foo_foo_find_by_", "")
User.where(column.to_sym => args.first)
else
super
end
end
end
User.foo_foo_find_by_username("schneems")
@Schneems
Sunday, July 31, 11
8. Active Record
• Active Record’s Query API
find
where (:conditions)
includes (:include)
group
order
limit
offset
joins
having
http://m.onkey.org/active-record-query-interface
@Schneems
Sunday, July 31, 11
11. Group
• Group
# Group
User.group(:hometown).count(:hometown)
#<OrderedHash {
"Austin, Texas"=>28,
"Alexandria, MN"=>1,
"Warsaw, Poland"=>1,
# ...
}
@Schneems
Sunday, July 31, 11
12. Joins
# Joins
BlogPost.joins(:user).where(:user_id => 2).first
=> #<BlogPost id: 13023, name: The best of times>
@Schneems
Sunday, July 31, 11
13. Include
# Include pre-fetches data
User.where(:id => 3).includes(:posts => :comments)
user = User.find(3)
post = user.posts.all.first
post.comments
@Schneems
Sunday, July 31, 11
14. Having
# having
User.group(:hometown).having("COUNT(hometown) > 22").count
#<OrderedHash
{"Austin, Texas"=>28,
"Austin, TX"=>102,
"San Francisco, CA"=>92,
"Dallas, TX"=>56}>
@Schneems
Sunday, July 31, 11
15. Where
• can take raw sql statement
User.where("username = foo").to_sql
=> "SELECT "users".* FROM "users"
WHERE (username = foo)"
@Schneems
Sunday, July 31, 11
16. Where
• Like
# 'Like' does a search using % as wildcard
User.where("username like '%foo%'")
# will match '...zfoo', 'fooz...', etc.
@Schneems
Sunday, July 31, 11
17. Where
• Null
# 'NULL' checks blank entries
User.where("username IS NULL")
# 'NOT' negates query
User.where("username IS NOT NULL")
@Schneems
Sunday, July 31, 11
18. Where
• Equality Operators
# '<>' is SQL not equal
User.where("username <> 'foo'")
# "greater than"
User.where("popularity > 5")
# "less than"
User.where("popularity < 5")
@Schneems
Sunday, July 31, 11
19. Where
• SQL Functions
# DATE() is a SQL function
User.where("DATE(created_at) > DATE('#{5.days.ago}')")
# Other Functions:
# AVG, SUM, COUNT, MIN, MAX
@Schneems
Sunday, July 31, 11
20. Where
• (?) passes data into SQL safely
User.where("DATE(created_at) > DATE(?)", 5.days.ago)
@Schneems
Sunday, July 31, 11
21. SQL Security
• SQL injection attacks
name = "'' or 1 DROP TABLE USERS"
# Bad
User.where("name = #{name}")
# Good
User.where("name = ?", name)
User.where(:name => name)
@Schneems
Sunday, July 31, 11
22. ActiveRecord
• Lazy Loading
# stores Query, doesn’t hit the DB
cars = Car.where(:colour => 'black')
# .each Fires "select * from cars where ..."
cars.each {|c| puts c.name }
# Now We can Chain
Item.limit(10).order('created_at DESC')
@Schneems
Sunday, July 31, 11
23. Scopes
• Re-use your queries
class Product < ActiveRecord::Base
scope :discontinued, where(:discontinued => true)
scope :cheap, where("price < 5")
end
Product.discontinued.first
Product.discontinued.cheap.first
@Schneems
Sunday, July 31, 11
24. Scopes
• Re-use your queries
class Product < ActiveRecord::Base
scope :cheaper_than,
lambda { |price| where("price < ?", price) }
end
Product.cheaper_than(5).first
@Schneems
Sunday, July 31, 11
25. Class Methods
• Re-use your queries
• Better than scopes
class Product < ActiveRecord::Base
def self.cheaper_than(price)
where("price < ?", price)
end
end
Product.cheaper_than(5).first
Product.cheaper_than(5).order.limit(5).first
@Schneems
Sunday, July 31, 11
26. Rake
# /lib/tasks/users.rake
namespace :users do
desc "Create dummy users"
task :populate => :environment do
5.times do
User.build(:name => Faker.first_name)
end
end
end
# execute with
> rake users:populate
@Schneems
Sunday, July 31, 11
27. Rake
• Can be used independently from Rails
# /lib/tasks/foo.rake
namespace :foo do
desc "foo bars"
task :bar do
# ...
end
end
@Schneems
Sunday, July 31, 11
28. Script/Runner
# /lib/user_populate.rb
5.times do
User.build(:name => Faker.first_name)
end
# execute with
> script/runner /lib/user_populate.rb
@Schneems
Sunday, July 31, 11
29. Rake Wins
• Versatile
• Useful outside of rails
• favored over script/runner
@Schneems
Sunday, July 31, 11
30. Pagination
• Split up large data into chunks
• will_paginate gem
post = BlogPost.paginate :page => params[:page], :per_page => 20
post.total_count # => 200
post.count # => 20
@Schneems
Sunday, July 31, 11
31. Full Text Search
# SQL Like
BlogPost.where("title like '%?%", params[:title])
# slow
# doesn't deal with
# punctuation
# pluralization
# etc.
@Schneems
Sunday, July 31, 11
32. SOLR
• Open Source Search Server
• Based on Lucene
• Fast
• Feature rich
• Easily Accessed ruby API wrappers
@Schneems
Sunday, July 31, 11
33. Sunspot GEM
class BlogPost < ActiveRecord::Base
searchable do
text :title, :body
text :comments do
comments.map { |comment| comment.body }
end
time :published_at
string :sort_title do
title.downcase.gsub(/^(an?|the)/, '')
end
end
end
@Schneems
Sunday, July 31, 11
38. Sphinx
• Flying Sphinx add-on in heroku
@Schneems
Sunday, July 31, 11
39. 3rd Parties/Black Box
• Index Tank
# Connect to Service
api = IndexTank::Client.new "<YOUR API URL HERE>"
index = api.indexes "<YOUR INDEX NAME>"
# add documents
docid = "<YOUR DOCUMENT ID>"
text = "<THE TEXTUAL CONTENT>"
index.document(docid).add({ :text => text })
# search documents
results = index.search "foo"
@Schneems
Sunday, July 31, 11
40. Questions?
http://guides.rubyonrails.org
http://stackoverflow.com
http://peepcode.com
@Schneems
Sunday, July 31, 11