domingo, 12 de julio de 2015

When STI met polymorphic associations

It was a very long time from the last time that I wrote a post. A lot of things happened but I continue working with Ruby!

The other day I worked in very complete case of polymorphic association with some subclasses in STI (Single Table Inheritance). For me was very interesting and I wanted to share it with you. The models I have are like the following:



There are two kind of users: Campaign Manager and Account User.

All kind of users have accounts, so we have also an Account model. The cardinality of the relationships are like the following:
  • A campaign manager can have multiple accounts, and an account can have multiple campaign managers
  • An account user can have only one account, and an account can have multiple account users
So we have a many-to-many relationship and a one-to-many relationship. That is why we need to model such relationships using the special Accounting join model. If we add into this that we have STI, then we end up with a pretty interesting model, isn't it?

To model the kind of user we will use inheritance in our Rails models. We want also to be able to access the accounts from both the CampaignManager and AccountUser. For that we need two relationships, one to connect the users to the Accounting join model, and the other connecting the later with the Account model. So the models should be like the following:

class CampaignManager < User
  has_many :accountings, as: :accountable, dependent: :destroy
  has_many :accounts, through: :accountings
end

class AccountUser < User
  has_one :accounting, as: :accountable, dependent: :destroy
  has_one :account, through: :accounting
end

Now we can access the accounts of an user with code like the following:

campaign_manager.accounts
account_user.account

Note that "account" for AccountUser is singular because of the cardinality.

The Account model itself:

class Account < ActiveRecord::Base
  has_many :campaign_managers, through: :accountings, source: :accountable, source_type: 'CampaignManager'
  has_many :account_users, through: :accountings, source: :accountable, source_type: 'AccountUser'
  has_many :accountings, dependent: :destroy
end

We have polymorphic association called "accountable". It is polymorphic because it can link to either an AccountUser or an CampaignManager object. So we need to tell the Account model the subclasses that we want to retrieve with source_type.

We need also to create the join model. As we said, the Accounting class is the join model that associates the account with the accountable objects. The code looks like:

class Accounting < ActiveRecord::Base
  belongs_to :account
  belongs_to :accountable, polymorphic: true

  validates :accountable_id, uniqueness: true,
                             if: Proc.new { |a| a.accountable.type == 'AccountUser' }
end

In the accountings table we will see the following fields:
  • account_id
  • accountable_id: It corresponds to the campaign_manager id or the account_user id
  • accountable_type:  It should be "User" for all the rows, and neither "CampaignManager" nor "AccountUser". Don't get confused with the STI type here! 
Very important! We need a validation of uniqueness in the case the accountable object is an AccountUser, because an AccountUser only can have a single Account.

And that's all!



lunes, 28 de abril de 2014

A road to HTML Mails in Rails

 If you need your Rails 4 application to send html emails, then perhaps this post can help you.
 Here there is a very good page about the topic.
 As you can see in the page, the email clients will strip the css classes out. So you should use inline styles.
 I followed the next steps:
 - I put the html email layout (with inline styles) in app/views/layouts
  <body style="your-style">
    <table>
      <tr>
        <td style="your-style">
          <p><%= yield(:email_content) %></p>
        </td>
      </tr>
     </table>
  </body>
 - For each mailer view file .text.erb write a corresponding file .html.erb in the same folder and with the same name, with the following code
<% content_for :email_content do %>
  <p>Text</p>
  <p>message</p>
<% end %>
 - In the application, you probably will have several mailer classes in the folder mailer. Mailers work very similarly to controllers. In my case these classes inherit from the class ApplicationMailer, so I wrote in this class:
layout 'layouts/email'
 - If you are using devise you need to configure it to use the layout. Write inside the class Application in config/application.rb:
config.to_prepare do
 Devise::Mailer.layout "layouts/email"
end
 - If you are testing your application with rspec and have code like the following:
expect(inbox_for(email_account)).to include_email(subject: 'Text Subject', body_text: /Text message/)
 After the change, the test won't work because now the email isn't plain text any more. Now is a multipart email. So you could write:
last_email = inbox_for(email_account).last
expect(last_email.subject.to_s).to match('Text Subject')
expect(last_email.text_part.body.raw_source.to_s).to match(/Text message/)
expect(last_email.html_part.body.raw_source.to_s).to match(/Text</p>\n\n  <p>message/)
 Instead of use inline styles you could use gems as premailer or roadie. I didn't use them because I only have an html.
 I hope that this information helped you. I am sure that you have better ideas to implement this, so share it with us!

viernes, 24 de enero de 2014

Mysterious scroll

Today I'd like to speak about a problem that we had with CSSModal and Autofocus.

What do we use in our application?
 - Rails
 - For modal windows CSS Modal. For example, we have at the top of the index page a link for login. When we click this link, a modal with a form for the login is opened.

 The problem showed up when we used the autofocus attribute in our forms. The result was that when we opened the index page, it was scrolled to the middle of the page, without clicking the login link.

 I didn't know that the autofocus could cause this behaviour, so I created a JS to test what it was happening:

console.log(document.activeElement);
console.log($(window));
console.log('$(window).scrollTop() ' + $(window).scrollTop());
console.log('$(window).height() '+ $(window).height());
console.log('$(document).height() ' + $(document).height());
console.log('window.pageYOffset ' + window.pageYOffset);
console.log('document.body.scrollTop ' + document.body.scrollTop);

I saw that the activeElement was always the first field of the login form. How could it be? I didn't click the login link.

Thilo, told me that perhaps the autofocus of the login form was changing the pageYOffset. He removed the link and tested that the page worked properly.

So, I removed the autofocus in the modal forms and the pages worked.

But I needed the focus in the first field of each form!!!

How did we resolve? With JS.

In the input or text fields of our forms with the needed autofocus, we used a html class called "should_have_focus"

Then, in a js file:

$(document).on('cssmodal:show', function () { 
     $('.is-active') 
          .find('input.should_have_focus, textarea.should_have_focus')
          .focus();
});

When the modal is active, the focus is transferred to the input or textarea field with the html class "should_have_focus"

And that's all!!

jueves, 28 de noviembre de 2013

Internet Explorer Bomb

Today I deserve some chocolate.

If you are working with SASS you have probably a time bomb in your code base and you don't know.
I was totally happy on Monday when the customer told us that the application did't work in Internet Explorer.
We are working with Rails and Sass.

The problem seemed to be that the stylesheets weren't found.
Well, I have a Mac and I don't have Internet Explorer. So I used VirtualBox with the virtual machines that provides www.modern.ie to try to debug the issue.

I could see two things:
- The application worked in IE version >= 10
- In IE version < 10, the stylesheets were accessed (no error 404) but the site was showing as if there weren't any styles.

The designer gave us a hint: Perhaps we could use http://blesscss.com/.

What is it?

The problem appears here:
Microsoft's IE support documentation explains that in Internet Explorer 6-9:
  1. All style tags after the first 31 style tags are not applied.
  2. All style rules after the first 4,095 rules are not applied.
  3. On pages that uses the @import rule to continously import external style sheets that import other style sheets, style sheets that are more than three levels deep are ignored.

There are three possible solutions:

      1. Use Bless.
  • It splits a big css in various css. The first css make an "@import url" to the other css file.
  • You need Node to use Bless.
  • It isn't integrated in the assets pipeline of Rails.
      2. Use gem css_splitter
  • It is integrated in the assets pipeline of Rails.
      3. Split files manually.

For every solution, you need to know which css has more than 4095 rules. My question was, how do I know that?

First I obtained a list of css files with a command like that:

  find . -name "*css" > css.txt

Then I loaded the css_splitter gem in my project, and in "rails c" I defined the following method:

def count                                                  
  IO.foreach("css.txt") do |line|                          
    puts line.strip                                        
    puts CssSplitter::Splitter.count_selectors(line.strip!)
  end                                                      
end                                                        

Run the method and.....

The winners are!!!!
  • ./public/assets/application-c5f01626ddcd1f522b8a7f16cecf3355.css with 11,575 selectors
  • ./vendor/assets/stylesheets/libs/<util>/<util>.css with 9,759 selectors
The files in the project are structured like this:

app
  assets
    stylesheets
      application.css.scss
        @import "screen";
vendor
  assets
    stylesheets
      screen.css
        @import "libs/<util>/<util>";
      libs
        <util>
          <util>.css

The assets pipeline of Rails precompiles application.css.scss with all the @import and create a file with all the selectors:
./public/assets/application-c5f01626ddcd1f522b8a7f16cecf3355.css

But, which css should I split? The one in public/assets or <util>.css?

I tried to use the gem css_splitter but I wasn't able to make it work. So I used other approach.

I installed node and npm on my machine. I executed blessc -f <util>.css. This command overwrites the original file. At the end I had three files:


vendor
  assets
    stylesheets
      libs
        <util>
          <util>.css
            @import url('libs/<util>/<util>-blessed2.css');
            @import url('libs/<util>/<util>-blessed1.css');
          <util>-blessed1.css
          <util>-blessed2.css

Now the number of selectors were:
./public/assets/application-d618a047f651d19af56d0e3e9132fba3.css ------> 3,387
./vendor/assets/stylesheets/libs/<util>/<util>-blessed1.css -------------------> 4,095
./vendor/assets/stylesheets/libs/<util>/<util>-blessed2.css -------------------> 4,093
./vendor/assets/stylesheets/libs/<util>/<util>.css -------------------------------> 1,571

Ok, but where is the magic? The key is that inside <util>.css we are using @import url. We aren't composing a big file, the file is calling others files using the URL.

But, we have still a problem. Rails creates dynamic names for assets. It concatenates a digest to the asset name, for example, ./public/assets/application-d618a047f651d19af56d0e3e9132fba3.css. So we can not call simply to libs/<util>/<util>-blessed2.css.

No problem, we have solution for almost everything.

We are using sass-rails, so we can use the helper asset_path inside the css file. The steps are:
  • Rename ./vendor/assets/stylesheets/libs/<util>/<util>.css to ./vendor/assets/stylesheets/libs/<util>/<util>.css.erb
  • Inside ./vendor/assets/stylesheets/libs/<util>/<util>.css.erb, we wrote:
       @import url(<%= asset_path 'libs/<util>/<util>-blessed2.css' %>);
       @import url(<%= asset_path 'libs/<util>/<util>-blessed1.css' %>);
  • In the file config/application.rb I added the new files in the property config.assets.precompile: 
config.assets.precompile += %w{ libs/<util>/<util>.css libs/<util>/<util>-blessed2.css libs/<util>/<util>-blessed1.css }
Remember, I only used the gem css_splitter for the selector count, don't leave it in your Gemfile!

I hope that this post helps you.

I am enjoying this wonderful chocolate with salt, I deserve it!
















domingo, 10 de noviembre de 2013

Devise with Rails 4: Redirect back to current page after changing the password

Suppose that you are visiting a site, you are in an interesting page in this site and you need to sign up to continue doing things.

  This site is made in Rails 4 and use Devise for the authentication. If you want to continue in the same page that you were before the sign up, you need to implement in your application controller something similar to what is described here: How To: Redirect back to current page after sign in, sign out, sign up, update

  But imagine that your application has a "Forgot the password" feature. So you have the following URIs:

   - /users/password/new to ask for a new password
   - /users/password/edit in the email confirmation to change the password

  You'll have errors when you submit the password because the application will try to resend to the /users/password with a GET. But the URI /users/password is designed to be accessed only by PUT, so you'll get an error saying that the path /users/password with the method GET doesn't exist.

  To solve this you can use the solution proposed in "problems when used along with confirmable module", but I prefer the following solution because you don't store any request with POST or PUT.

def store_location
    if (request.path != '/users/sign_in' &&
        request.path != '/users/sign_up' &&
        request.path != '/users/password/new' &&
        request.path != '/users/password/edit' &&
        !request.xhr? && !request.post? && !request.put?
        )
      session[:previous_url] = request.fullpath
    end
  end

  def after_sign_in_path_for(resource)
    session[:previous_url] || root_path
  end

martes, 5 de noviembre de 2013

Long time ago

It was a long time ago since my last post. I have to say that I made a lot of things.
Since October I have been doing an Internship in Upstream. Here I work in a real project for an startup. Thilo teaches me. Upstream is located in the coworking space co-up, where I can meet a lot of software engineers and also go to very interesting meetings.
In the project I have been working with Ruby, Rails, Rspec, Capybara and Git (very dangerous ;) ). I have used webmock to test the service that uses the API of Mailchimp, the gem gibbon for Mailchimp, the gem icalendar and also I modified the task precompile for assets.
While I go to co-up I read a book that Thilo gave me: Practical Object-Oriented Design in Ruby by Sandi Metz.
After working I practice Rails in the project buzonciudadano with some friends ( elmendalerenda, pedrogimenez and semurat ), or I continue reading the Rspec book.

I also study about other lenguajes, for example, I installed today Pyenv in my local machine. Pyenv is a Python version management as rvm is for Ruby.
Today I tried to create my first application in Heroku but it doesn't get the Ruby version 1.9.3 that I have in my project although I modified my Gemfile with this ruby version. I'll solve that another day.

See you!







martes, 15 de octubre de 2013

Types of Test Double

I am reading the book The RSpec Book: Behaviour-Driven Development with RSpec, Cucumber, and Friends and writing the code that appears in it in the repository code-breaker.
I was in this point. This test in RSpec produces these fails:

Failures:

  1) Codebreaker::Game#start sends a welcome message
     Failure/Error: game.start
       Double "output" received :puts with unexpected arguments
         expected: ("Welcome to Codebreaker!")
              got: ("Enter guess:")
     # ./lib/codebreaker/game.rb:8:in `start'
     # ./spec/codebreaker/game_spec.rb:10:in `block (3 levels) in <module:Codebreaker>'

  2) Codebreaker::Game#start prompts for the first guess
     Failure/Error: game.start
       Double "output" received :puts with unexpected arguments
         expected: ("Enter guess:")
              got: ("Welcome to Codebreaker!")
     # ./lib/codebreaker/game.rb:7:in `start'
     # ./spec/codebreaker/game_spec.rb:16:in `block (3 levels) in <module:Codebreaker>'

The problem is solved here.

Why? I didn't understand. So I asked to . His answer:

There are three types of Test Double:

  • Stub
    • Pasive
    • It doesn't have memory
    • You can configure it to return data
    • They are used normally in the set up of the test
  • Spy
    • Pasive
    • It records what happens with it: Which methods were called and which with parameters
    • They are used in asserts. You ask to it: What happend?
  • Mocks
    • You must say in the setup of the test what things are going to happen.
    • Active: There is a Check of what is happening
    • Two sub types:
      • Strict
        • They fail if it doesn't happen exactly what we said in the setup
      • Lenient
        • They fail if it doesn't happen at least what we said in the setup.


In the test they are using Strict Mocks, and then in the following version they are converting this tests to use Lenient Mocks.
You can see that they aren't calling a should or an expect at the end of the test. The mock already knows what is going to happen.

In the frameworks, these names are mixed, so in some cases, tests that are called stubs aren't really stubs, but a mix of stub and spy, for example.

Wow, what an answer! It was a lot of wisdom, so I wanted to share with you.