Ruby on Rails

could not route to support as settings are not loaded

This error isn’t blocking anything

it’s due to AR not being loaded prior to rails initialization

and using DB-based settings to determine routing

only happens on migration/initialization

Advertisements

year, month, and day from a string

Given: “03/17/2014”
Get: Year = “2014”, Month = “03”, Day = “17”

There are numerous ways to get them, I’ll show you some easy ones and my favorite one (:

1st way:

  date = "03/17/2014"
  m = date[0..1]    # => "03"
  d = date[3..4]    # => "17" 
  y = date[6..10]   # => "2014" 

note: several ways to substring as well, for more info: http://www.ruby-doc.org/core-1.9.3/String.html#method-i-5B-5D

2nd way:

  m = "03/17/2014"[/(\d{2})\/(\d{2})\/(\d{4})/,1]    # => "03"
  d = "03/17/2014"[/(\d{2})\/(\d{2})\/(\d{4})/,2]    # => "17"
  y = "03/17/2014"[/(\d{2})\/(\d{2})\/(\d{4})/,3]    # => "2014"

3rd way (my fav):

"03/17/2014".scan(/(\d{2})\/(\d{2})\/(\d{4})/).flatten
 # => ["03", "17", "2014"] 

4th way:

data = "03/17/2014".match(/(\d{2})\/(\d{2})\/(\d{4})/)
 # => #<MatchData "03/17/2014" 1:"03" 2:"17" 3:"2014"> 
data.captures
 # => ["03", "17", "2014"] 

testing cookies is almost as fun as eating them

Given: we have two different apps A and B
And: we GET data X from backend (Java) API
When: we pass “secret” data X from app A to app B
Then: we use a secure cookie to store data X

#controller

def index
    begin
      data = Data.find(@id)
      data_X = data['X']
      if data['X'] == "Keep"
        cookies[:X] = {
          :value => "true",
          :secure => true,
          :domain => '.www.cheriecodes.com',
          :httponly => true
        }
      elsif data['X'] == "Remove"
        cookies[:X] = {
          :value => "false",
          :secure => true,
          :domain => '.www.cheriecodes.com',
          :httponly => true
        }
      end

    # some other code here that directs to app B #
  
    rescue ActiveResource::ResourceNotFound => e
      Rails.logger.debug "#index: fails to find data"
    rescue => e
      Rails.logger.debug "#index error: #{e.message}"
    end
  end

note: yes, we should take out the cookie into its own method to keep it DRY, but that’s not my topic today d: so ahem… how do we test for cookies?

#rspec

context "when data X is equal to 'Keep'" do
    it "sets the cookie X to true" do
        Data.stub(:find).and_return({'X' => "Keep"})
        get :index
        cookies[:X].should == "true"
    end
end

but it fails miserably…

Failure/Error: cookies[:X].should == "true"
       expected: "true"
            got: nil (using ==)

What? How’s that possible?? Hmm… let’s get a better view of the whole spec

describe "#index" do
    before :each do
        @mocked_cookies = mock('cookie store').as_null_object
        controller.stub(:cookies).and_return(@mocked_cookies)
        @mocked_cookies.stub(:[]).with(:country_preference).and_return(nil)
    end

    # other specs #

    context "when data X is equal to 'Keep'" do
        it "sets the cookie X to true" do
          Data.stub(:find).and_return({'X' => "Keep"})
          get :index
          cookies[:X].should == "true"
        end
    end
end

Ahhhhhhhhhhh… somebody already stubbed the cookies to return nil

so to fix this, separate out the two different specs into its own describe block and move the mocked cookies into the appropriate describe block (not the one that tests for cookies[:X]) and the test passes (:

Side Question: can you think of any other way to pass “secret” (private) information between app A and app B?

rails, getJSON, and jasmine

Task:
When user selects a country from the country dropdown,
get the new terms and conditions links according to the selected country,
and update the current terms and conditions links with the new ones.

controller

def update_policy_links
    respond_to do |format|
      format.js {
        render :json => {:updated_terms_link => 
                          return_policy_link("terms", country_preference),
                         :updated_privacy_link => 
                          return_policy_link("privacy", country_preference),
                         :updated_electronic_link => 
                          return_policy_link("electronic", country_preference)}
      }
    end
end

country_preference: a cookie that stores the latest country user selected
return_policy_link: check out the previous post on default value & nil guard

routes

get '/update_policy_links/:country' => "enrollment/user#update_policy_links", :constraints => {:country => /[a-zA-Z]{2}/}

note: we’re passing in country as a param for other usage which I won’t go over here…

javascript

$("#new_vuser").on('blur', '#user_countryCode', function(){
    country_selected = $(this).val();

    $.getJSON("/update_policy_links/" + country_selected + "?locale=" + I18n.locale, function(data){
          $(".terms_link, #terms_link").attr('href', data.updated_terms_link);
          $(".privacy_link, #privacy_link").attr('href', data.updated_privacy_link);
          $(".electronic_link").attr('href', data.updated_electronic_link);
    });
});

jasmine

describe("on_select_change", function(){
    it("should update the terms and condition links", function(){

        var links ={
            updated_terms_link : "/pages/terms",
            updated_privacy_link : "/pages/privacy",
            updated_electronic_link : "/pages/electronic"
        };

        spyOn($, 'getJSON').andCallFake(function(url,data){ data(links); });
        $('#user_countryCode').blur();

        expect($.getJSON).wasCalled();
        expect($('.terms_link, #terms_link')).toHaveAttr('href',"/pages/terms");
        expect($('.privacy_link, #privacy_link')).toHaveAttr('href',"/pages/privacy");
        expect($('.electronic_link')).toHaveAttr('href',"/pages/electronic");
    });
})

Route Constraints

TASK:

  • constraint T&C policy routes according to its parameters.

EX:

  • /en/us/legal/terms.f0a6f2aa-8e68-45ae-a875-84444ef1130f

constraints:

  • languageCode – 2 alpha characters – EX: en/ fr/ sp
  • country – 2 alpha characters – EX: us/ ca/ uk
  • policy – 3 types – EX: Terms/ Privacy/ Electronic
  • guid – standard – 32 hexadecimal digits – EX: 21EC2020-3AEA-1069-A2DD-08002B30309D

—————————————————————————————————————–
rspec:
note: the guid is located after the “.”, which works as a separator for formatted routes,
hence the :format=>guid instead of :guid => guid.

describe "GET policies" do
    it "should route to us privacy page" do
      { :get => '/en/us/legal/privacy.f0a6f2aa-8e68-45ae-a875-84444ef1130f'}.should
      route_to(:controller=>"pages", :action=>"privacy")
    end

    it "should route to ca terms page" do
      { :get => '/en/ca/legal/terms.f0a6f2aa-8e68-45ae-a875-84444ef1130f'}.should
      route_to(:controller=>"pages", :action=>"terms_ca")
    end

    it "should route to 404 page when given invalid policy parameter" do
      { :get => '/en/ca/legal/invalid.f0a6f2aa-8e68-45ae-a875-84444ef1130f'}.should
      route_to(:controller=> "errors", :action => "routing",
               :path=>"en/ca/legal/invalid",
               :format=> "f0a6f2aa-8e68-45ae-a875-84444ef1130f")
    end

    it "should route to 404 page when given invalid country parameter" do
      { :get => '/en/invalid/legal/terms.f0a6f2aa-8e68-45ae-a875-84444ef1130f'}.should
      route_to(:controller=> "errors", :action => "routing",
               :path=>"en/invalid/legal/terms",
               :format=> "f0a6f2aa-8e68-45ae-a875-84444ef1130f")
    end

    it "should route to 404 page when given invalid language parameter" do
      { :get => '/invalid/us/legal/privacy.f0a6f2aa-8e68-45ae-a875-84444ef1130f'}.should
      route_to(:controller=> "errors", :action => "routing",
               :path=>"invalid/ca/legal/privacy",
               :format=> "f0a6f2aa-8e68-45ae-a875-84444ef1130f")
    end

    it "should route to 404 page when given invalid guid" do
      { :get => '/en/us/legal/privacy.invalid'}.should
      route_to(:controller=> "errors", :action => "routing",
               :path=>"en/ca/legal/privacy",
               :format=> "invalid")
    end

  end

# routes.rb

constraints(PolicyConstraint) do
   get ':language/:country/legal/:policy.:guid' => "pages#policy_by_type",
        :constraints => {:language => /[a-zA-Z]{2}/, :country => /[a-zA-Z]{2}/,
        :guid=>/[\h]{8}-[\h]{4}-[\h]{4}-[\h]{4}-[\h]{12}/}

end

An example of policy_by_type method that is used for filtering two countries: CA and US.
# pages_controller.rb

  def policy_by_type
    if params[:country].upcase == "CA"
      case params[:policy]
        when "terms"
          redirect_to terms_ca_url
        when "privacy"
          redirect_to privacy_ca_url
        when "electronic_communication"
          redirect_to electronic_communication_ca_url
        else
          render_404
      end
    else
      case params[:policy]
        when "terms"
          redirect_to terms_url
        when "privacy"
          redirect_to privacy_url
        when "electronic_communication"
          redirect_to electronic_communication_url
        else
          render_404
      end
    end
  end

now for constraining policy segment:
# constraints/policy_constraint.rb

class PolicyConstraint
   def self.matches?(request)
     policy_types = ['terms', 'privacy', 'electronic']
     policy_types.include?(request.parameters[:policy])
   end
end

and viola the route is now “constrained” (:

more info: http://guides.rubyonrails.org/routing.html