Gemfile · I codes and I blogs

Using Date - Ruby vs. Rails

This little investigation was prompted by an interesting quirk whereby Date.today == Date.tomorrow returned true.

Date.today is implemented in Ruby and uses system’s local time & timezone.

In Rails, timezone can be set in config/application.rb but defaults to UTC. Thus, it can be different that local system time used by Ruby.

Date.tomorrow & Date.yesterday are implemented in Rails and use Date.current internally.

Date.current is implemented in Rails thus:

::Time.zone ? ::Time.zone.today : ::Date.today

i.e. if Time.zone is set (which in Rails it is, as stated above), Date.current, Date.tomorrow & Date.yesterday will use Time.zone.today. If it’s not set, which never happens in Rails, then the methods fall back to the timezone-unaware Ruby implementation.

The solution to avoiding issues related to all this in Rails is to use the Rails implementations based on Date.current and avoid using Ruby’s Date.today.

block_given?

A method of the Kernel module, and thus available everywhere in Ruby, block_given? allows you to check whether a block was passed as an argument to a function.

def new_func
  if block_given?
    yield
  else
    'No block!'
  end
end

new_func    
# => "No block!"
new_func { 'In the block!' }
# => "In the block!"

Here’s a less contrived example, which came out of my solution to a problem on RubyMonk:

class MyArray
  attr_reader :array

  def initialize(array)
    @array = array
  end

  def sum(initial_value = 0)
    # your code here
    @array.inject(initial_value){ |sum, n| sum += (block_given? ? yield(n) : n) }
  end
end

my_array = MyArray.new([1, 2, 3])
my_array.sum(10)
# => 16 (10 + 1 + 2 + 3)
my_array.sum(0) {|n| n ** 2 }
# => 14 (1**2 + 2**2 + 3**2)

Without a block argument, the method simply adds array elements to a specified initial value. When a block is passed, each array element is first yielded to the block (which squares the element in the example) and then summed.

RSpec 3.0.1 and RubyMine

After the recent RSpec update, warnings started showing up in RubyMine:

Deprecation Warnings: Treating metadata[:execution_result] as a hash is deprecated. Use the attributes methods to access the data instead. Called from …/teamcity/rspec3_formatter.rb: line#

(So, RSpec is complaining that RubyMine’s formatter is not accessing its data properly?) The warnings were overwhelming to the point of obscuring the results of running the test. Simple fix:

  1. open /Applications/RubyMine.app/rb/testing/patch/bdd/teamcity/spec/runner/formatter/teamcity/rspec3_formatter.rb by clicking on one of the links in the warning message

  2. this takes you right to the offending lines (#135, 333 & 340 in my version), wherein, as per the message’s instructions, the hash format

started_at_ms = get_time_in_ms(example.execution_result[:started_at])

needs to be changed to an attribute accessor

started_at_ms = get_time_in_ms(example.execution_result.started_at)

No more warnings :)

(In the process, I also learned that while File > Open Recent works to reopen projects, View > Recent Files (or ⌘-E) is for recently closed files.)

The Rails Machine

I’ve been working through - and enjoying - the EdX/Berkeley CS169 course for the past couple of months. With a bit of a break between the two halves, I thought I’d start a small Rails project of my own. After including rspec-rails in the Gemfile, I ran bundle install and then

$ rails g rspec:install
Could not find generator rspec:install

This was bad. I naturally blamed the gem first, and spent an awful lot of time digging for an answer. At one point, as a control, I added the cucumber-rails gem

$ rails g cucumber:install
Could not find generator cucumber:install

This was much worse. The gems were installed and bundled, no errors, no missing dependencies. I went as far as installing the latest Ruby, creating a new gemset, reinstalling Rails, …

Could not find generator cucumber:install

Running rails g only showed the built-in Rails generators: model, controller, etc. None of the gem generators would show up. This meant no devise, no foundation, no bootstrap, no hundreds of other gems that I haven’t used or that have not even been written yet. I dug deeper, hitting pages 3 and 4 of my Google searches, looking at questions and answers from 2, 3, 4 years ago. Nothing.

It was late. I turned off the computer, which I rarely do, and went to bed. In the morning, everything worked.

I couldn’t help but remember Tom Knight and the Lisp Machine:

A novice was trying to fix a broken Lisp machine by turning the power off and on.

Knight, seeing what the student was doing, spoke sternly: “You cannot fix a machine by just power-cycling it with no understanding of what is going wrong.”

Knight turned the machine off and on.

The machine worked.

Removing Paperclip attachments

Paperclip’s GitHub page includes the necessary steps to delete an attachment - the model’s attribute that refers to the attachment simply gets set to nil. Here is the full implementation of deleting a user’s profile picture in a Rails app. Start with tests (right?!):

spec/features/editing_users_spec.rb:

require 'spec_helper'
feature 'Editing Users' do
  let(:user) { FactoryGirl.create(:user) }
  let(:admin) { FactoryGirl.create(:admin) }
  scenario 'removing user profile photo' do
    sign_in_as admin # of course
    visit edit_user_path(user)
    click_link 'Remove profile photo'
    expect(page).to have_content 'User profile photo has been removed.'
    expect(page).not_to have_css("img[src*='user.png']") # file name of the user photo defined in the factory
    expect(page).to have_css("img[src*='paperclip_default.png']") # specified in the User model
  end
end

(This post covers creation of an attachment in a FactoryGirl factory.)

Read more