Ruby on Rails

angularjs rails simple_form name

when using rails simple_form and angularjs for form validation

= simple_form_for :address, url: account_path(account.id), 
                            method: :patch, wrapper: :custom,
                            defaults: { input_html: { required: true } },
                            html: { name: 'addressForm',
                                    novalidate: true,
                                    'ng-submit' => "addressCtrl.submitForm(addressForm, $event)" } do |f|

  %fieldset.form-fieldset
    = f.input :address_1, input_html: { name: 'contact[street_address_1]', 
                                        'ng-model' => 'addressCtrl.address.street_address_1', 
                                        'ng-init' => "addressCtrl.address.street_address_1='#{account.street_address_1}'" }, 
                          wrapper_html: { 'ng-class' => "{'form-field--error': addressForm['contact[street_address_1]'].$invalid && addressForm['contact[street_address_1]'].$touched}" },
                          error_wrapper_html: { 'translate' => 'address.error.required.street_address_1', 
                                                'ng-show' => "addressForm['contact[street_address_1]'].$error.required && addressForm['contact[street_address_1]'].$touched" }

...

given input name

  name: 'contact[street_address_1]'

angularjs calls it by:

  addressForm['contact[street_address_1]']
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"] 

activeresource model rspec

Given: certain API to obtain merchant information: “/service/merchant/:merchantId”
When: it is called with the merchant id,
Then: it returns a json response like the following:

{"store": {
  "preferences" : [{
    "settings" : [
      {
        "key": "logo",
        "value": "logo.png"
      },
      {
        "key": "text",
        "value": "Hi, welcome to our store"
      },
      {
        "key": "url",
        "value": "http://somestore.com"
      }]
    }]
}}

Then: turn the store settings into a hash like the following:

{:logo => "logo.png", :text =>"Hi, welcome to our store", :url => "http://somestore.com"}

Assuming that we already have a method called get_merchant_info that performs the above task – turning the json response into a hash containing the information.

rspec

describe "#get_merchant_info" do
  context "when the service returns a 200 - data found" do
    it "returns a Hash containing the merchant's information" do
      api_data = {:logo => "logo.png", :text =>"Hi, welcome to our store", :url => "http://somestore.com"}
      
      # settings = mock('settings', <TODO: hash that resembles the json response>  )
      store = mock('store', {:preferences =>[settings]})
      MerchantService.any_instance.should_receive(:store).at_least(1).times.and_return(store)

      merchant = MerchantService.new
      merchant.get_merchant_info.should be_kind_of(Hash)
      merchant.get_merchant_info.should eq(api_data)
    end
  end
end

note: MerchantService is the model that integrates with the API and receives the json response.

The reason I set up api_data at the beginning is so I can reuse it for both json response and the hash result.

Now how do we turn api_data into a hash that resembles the json response (for line 6 above)?

After taking a look at Enumerable – http://apidock.com/ruby/Enumerable, I decided to use collect

So I test it out via the console/irb:

api_data.collect{|k,v| {"key" => k.to_s, "value" => v}}
 => [{"key"=>"logo", "value"=>"logo.png"}, {"key"=>"text", "value"=>"Hi, welcome to our store"}, {"key"=>"url", "value"=>"http://somesite.com"}] 

yayyy! Just what we’re looking for.

But is our test efficient? Personally, I don’t like how we’re mocking twice and stubbing the response, why don’t we set up the response like so…

describe "#get_merchant_info" do
  context "when the service returns a 200 - data found" do
    it "returns a Hash containing the merchant's information" do
      api_data = {:logo => "logo.png", :text =>"Hi, welcome to our store", :url => "http://somestore.com"}
      merchant = MerchantService.new({:store =>
                                          {:preferences => [{
                                                :settings =>
                                                    api_data.collect { |k, v| {"key" => k.to_s, "value" => v} }
                                           }]} })
      merchant.get_merchant_info.should be_kind_of(Hash)
      merchant.get_merchant_info.should eq(api_data)
    end
  end
end

which way do you think is better? hmm? or maybe you can think of a way that’s even better? let me know (;

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");
    });
})

Jasmine – how to mock window.location

I got some javascript code that I wanna test using Jasmine:

Cherie.Controllers.Help = function () {
    var $question = $('.question'),
        $help_section = $('#help_section');

    var event_handlers = {
        hide_event:{
            highlight_questions:function () {
                if (window.location.pathname == "/help") {
                    $question.each(function () {
                        if ($(this).attr('class').indexOf('highlight') == -1)
                            $(this).hide();
                    })
                }

            }
        },

        click_event:{
            question_list:function () {
                $help_section.on('click', '.question', function () {
                    $(this).next('.answer').toggle();
                    $this.find('.toggle').toggleClass('hide');
                });
            }
        }
    }

    $.each(event_handlers, function(i,handler_type){ $.each(handler_type, function(i,handler){ handler(); }) });
}

$(function() {
    Cherie.Controllers.Help();

});

I am gonna start with testing the hide_event, so I got to mock window.location.pathname… window.location can be written without the window prefix, and location is an object, which makes pathname a property (location.property), so we can’t “spyon” it, since spyon is used for mocking/stubbing functions, so let’s work around it so that we can use spyOn.

make a helper function:

var get_current_location = {
    get_pathname: function () {
        return window.location.pathname;
    }
};

and now the new code looks like this:

        hide_event:{
            highlight_questions:function () {
                if (get_current_location.get_pathname() == "/help") {
                    $question.each(function () {
                        if ($(this).attr('class').indexOf('highlight') == -1)
                            $(this).hide();
                    })
                }

            }
        }

great, on to jasmine…

describe("help.js", function () {
    beforeEach(function () {
        loadFixtures('help.html');
    });

    describe("hide_event", function (){
        it("should hide non-highlighted questions", function () {
            spyOn(get_current_location, 'get_pathname').andReturn("/help");
            Cherie.Controllers.Help();
            expect($('.highlight')).toBeVisible();
            expect($('.question:not(.highlight)')).toBeHidden();
        });

    });

    describe("click_event", function(){
        beforeEach(function(){
            Cherie.Controllers.Help();
            $('.question').first().click();
        });

        it("should show the corresponding answer", function(){
            expect($('.answer').first()).toBeVisible();
        });

        it("should show a '-' image with .hide", function(){
            expect($('.toggle').first()).toHaveClass('hide');
        });

        it("should show a '+' image without .hide on 2nd click", function(){
            $('.question').first().click();
            expect($('.toggle').first()).not.toHaveClass('hide');
        })
    });


});

& it’s alllll good (:

sidenote: The reason we have it as javascript is because we use the same partial across several different pages (not all questions are hidden); another way to implement it w/o javascript is to use conditionals along with css such as .hidden{display:none;}

hide_question = request.path == "/help" ? "hidden" : "" 

and then add the following to certain question list items.

{:class => "#{hide_question}"} 

There are a couple different ways to do this but you get the gist (; which one do you like better? javascript or rails way?