Keeping your specs DRY
I often see long before
blocks with lots of should_receive ... and_return
inside.
Remember that before
blocks are about setting up the "stage" (the context of your test), not declaring your expectations!
Also, they get run for every spec (every it
block).
So :
- convert your
should_receive
s tostub
calls inbefore
blocks; - have one or more
it
blocks withshould_receive
, if you actually care that the calls are made (watch out not to expect calls that are just an implementation details, only things that you need to see ca...
Rebase your feature branches
Regularly, but at least before merging your feature branches, rebase them to get rid of "fix typo" and "wip" commits.
Getting rid of unnecessary commits
Let's say you do a git rebase -i HEAD~~~~
and have this commit history:
pick 1d1e1f My Feature
pick 2d2e2f My Feature - wip
pick 3d3e3f My Feature - fix typo
pick 4d4e4f My Other feature
Change it to use fixup
for those commits that should be merged into the one before ...
How to run long scripts on production/staging server
In a nutshell, use screen
Why?
Sometimes we are concerned that if our connection gets closed, the process we are running will close too.
The processes or login sessions you establish through screen don't go away. You can resume your screen sessions.
How?
-
run screen command
screen
or you can give that screen a name (will help afterwards)
screen -S some_name
-
run your time consuming command
script/runner path_to_some_long_script.rb
-
detatch from your screen with the following commands
- `Crt...
Writing raw SQL queries
If you really, really have a good reason to write raw SQL (generally a bad practice), make sure you use the right call.
Avoid ActiveRecord::Base.connection.execute
in general, in particular because Octopus relies on this to determine which queries go to replicas. execute
would always go to the master.
A number of other methods are available: select_value
, select_rows
, of update
being the most usual.
Database: Scopes, migrations, and indices
Wether you modify an existing named scope or add a new one, or when you write a new query, make sure you have the proper indices.
This particularly applies if you're going to run non-trivial queries of course (admin backends, analytics, etc).
Compound indices
A chain of scopes results in (usually) one query. You should take into account all attributes (columns) that are used in :conditions
, :join
, :group
, :having
, and :order
, as all those result in filtering and sorting–slow operations without indices.
Take the list of all ...
When you set date attributes, you should not pass times
As you know, time zones make stuff a bit more difficult but are necessary.
A time-zoned record is converted to UTC using to_s(:db)
to be stored, and put back into the correct time zone when loaded. So when you are not on UTC, the following will happen:
>> Time.current
=> Fri, 15 Mar 2013 11:56:03 CET +01:00
>> Time.current.to_s(:db)
=> "2013-03-15 10:56:03" # This is now UTC
That will blow up in your face when you sen...
Find out branches containing a commit
If you have a commit and you want to see in what branches is is included, you have to write this:
git branch -r --contains [COMMIT-SHA]
-r is for remote
Respect time zones: Use Time.current and Time.zone.parse
Reading the current time
Don't use Time.now
as it's broken when using time zones. \
Instead, use Time.current
, DateTime.current
or Date.current
(the latter actually not being different, but just use it for good measure).
Parsing strings
Don't use Time.parse
as it only gives you a Time
object.
Use Time.zone.parse
instead for a TimeWithZone
.
Add indexes on foreign keys when you create a migration (with foreign key)
Whenever you make a migration to add a foreign key, you should also add an index for it
def self.up
add_column :comments, :user_id, :integer
add_index :comments, :user_id
end
Odd referrer issue
The request referrer is the URL you come from. It's set by the browser when you click a link (all browsers) and in Javascript click events too (all but the Internet Explorer family).
If you click a link on a page:
request.referer == request.referrer == request.env['HTTP_REFERER']
So far so good. But if you write the URL straight into the browser:
request.referer == request.env['HTTP_REFERER'] == nil
however
request.referrer == '/'
instead of nil.
This seems to happen in our current version of Rails, although not in Rails >= 3.x...
Bundle install in parallel
Gave a shot to the new Bundler 1.4.0RC1 during the weekend and found out it now supports gem installation in parallel. To invoke you need to pass --jobs
parameter with number of threads you want it to run – for me the best performance was achieved by specifying the number physical CPU cores.
I've tested on the AppFab app. In my case (CPU with 2-core i7 with HT) the speedup was 50%. I've also tried with a number greater than the number of physical cores but the performance was 15%-20% worse.
Bundler 1.3...
Avoid png file uploads with RSpec + Paperclip + Imagemagick
File uploads with .png are +10 times slower than using .jpeg or .gif.
# Bad
post :create, {
:property_id => property.id,
:photo => {
:image => File.open("#{Rails.root}/public/images/foo.png")
}
}
# Good
post :create, {
:property_id => property.id,
:photo => {
:image => File.open("#{Rails.root}/public/images/foo.jpeg")
}
}
jQuery ajax success/done will not run callbacks if request is json but the response is empty (typical 200)
When using
var onDone = function() { ... }
var onFail = function() { ... }
var params = { ... }
var url = ...
$.ajax({
type: 'put',
url: url,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(params),
})
.done(onDone)
.fail(onFail);
Always make sure your rails controller returns something:
if @property.update_attributes(params[:property])
render :json => { :ok => true }, :status => :ok
else
render :json => { :ok => false }, :status => :unpro...
Error: unable to read font `/usr/local/share/ghostscript/fonts/n019003l.pfb' @ error/annotate.c/RenderFreetype/1123: `(null)'
This happens when your machine does not have ghostscript installed, to fix it simply do
brew install ghostscript
Ruby: Don't a add return in ensure
This method won't throw an error:
def a_method
raise
ensure
return :something
end
it will in fact return :something
So please proceed with care :)
If you GROUP BY, make sure you ORDER BY NULL
TL;DR: If using :group => :some_field
you might want to :order => 'NULL'
.
According to the man
By default, MySQL sorts all GROUP BY col1, col2, ... queries as if you specified ORDER BY col1, col2, ... in the query as well. If you include an ORDER BY clause explicitly that contains the same column list, MySQL optimizes it away without any speed penalty, although the sorting still occurs. If a query includes GROUP BY but you want to avoid the overhead of sorting ...
DateTimes are Dates, beware
Given:
datetime = DateTime.now
date = datetime.to_date #or Date.today
will assert:
datetime.is_a? Date == true
datetime.is_a? DateTime == true
datetime.instance_of? Date == false
datetime.instance_of? DateTime == true
In case you have a table and a model like:
create_table :event do |t|
t.date :day
t.string :description
end
class Event < ActiveRecord::Base; end
And you say:
event = Event.new(:day => '2013-03-22')
Rails will convert the supplied value for day to the type of the database field,...
Profiling Ruby with ruby-prof
require 'ruby-prof'
# you don't need this if you have ruby-prof in your Gemfile
You can set one of the things you want to measure by using (default is PROCESS_TIME
most useful ones are PROCESS_TIME, MEMORY, CPU_TIME):
RubyProf.measure_mode = RubyProf::PROCESS_TIME
RubyProf.measure_mode = RubyProf::WALL_TIME
RubyProf.measure_mode = RubyProf::CPU_TIME
RubyProf.measure_mode = RubyProf::ALLOCATIONS
RubyProf.measure_mode = RubyProf::MEMORY
RubyProf.measure_mode = RubyProf::GC_RUNS
RubyProf.measure_mode = ...
has_defaults issues
What
The object returned by has_defaults apparently is the same between multiple object creations.
Consider this scenario:
class Order
has_defaults :items => []
end
o1 = Order.new
o1.items #=>> []
o1.items << item
o1.items #=>> [item]
o2 = Order.new
o2.items #=>> [item]
So, now o2.items is not empty by default because we modified the same object in has_defaults
How
When using has_defaults
on a model, consider using it in the following way:
has_defaults :items => proc {[] }
When
Consider do...
Killing wkhtmltopdf during cucumber
wkhtmltopdf
hangs on mac during cucumber unless we click on it. The main reason is with the version we use which is 0.11.0_rc1 and in out app/bin we have another version and it is a known issue with these versions. The fix is to go to 0.9.9, to downgrade the version we installed earlier using brew:
* brew uninstall wkhtmltopdf
* brew update
* brew versions wkhtmltopdf
* if you see output like
* `0.9.9 git checkout 6e2d550 /usr/local/Library/Formula/wkhtmltopdf.rb`
then `cd /usr/local`
* g...
To avoid using bundle exec or creating rvm gemsets
- Add to the end your .bash_profile
export PATH="./vendor/bundle/bin:$PATH"
- Also add
alias bi="bundle install --path vendor/bundle --binstubs=vendor/bundle/bin"
- Then to bundle install next time just use
bi
Now no more bundle exec
before any rake, cap, spec or anything else :)
Cleaner Rspec
When simply checking equality or truthiness then
Instead of:
it "should have role set to admin" do
@user.role.should eql('admin')
end
it "should be valid" do
@user.valid?.should be_true
end
Do:
it { @user.role.should eql('admin') }
it { @user.valid?.should be_true}
Try to stick to one expectation per test block, diverge in exceptional circumstrances, so instead of:
describe "#some_method" do
before(:each) do
@object = Class.new
end
it "should have attributes set" do
...
Method return value should always be of same type
One of the main source of bugs and complexity in the code is when a functional method (that we expect to return a value) return different values under different circumstances.
For example, we ruby programmers have a bad habit of returning nil from a method when certain condition is not fulfilled else return an Array or Hash. That just makes the calling code unnecessary complex and error prone because then it has to do different checks.
Bad Practice:
def bad_method(param)
return unless param == 'something'
[1,2,3]
end
...
Caching: don't use content_for, it won't work
Do not use content_for
inside a cached view fragment. It won't work because Memcache will just output whatever is in the cache, and not execute such commands.