Activeadmin semantic input partial and f.inputs togather

in activeadmin form when I do:

  form(:html => { :multipart => true })  do |f|
    f.inputs
  end

it shows all the fields nicely, and when it comes to belongs_to field it shows them as a collection but I want to replace this collection with a template of my belongs_to field.
Now if I want to use custom belongs_to field I can’t use the power of f.inputs because it will cause repetition. So what can we do now?

Well, currently using this as a solution:
at my “helper/active_admin_helper”:

	def form_inputs_for(m)
		columns=m.columns.map{|c| c.name}
		columns=columns.select{|s| !(s.end_with?"_id" or s.end_with?"_at" or s=="id") }
		columns.each do  |a|
        	input a
      	end
    end

and at form:

ActiveAdmin.register ModelClass do
  require "helper/active_admin_helper"
      form(:html => { :multipart => true })  do |f| 
        f.inputs do
          render "admin/product", f:f
          form_inputs_for(ModelClass)
        end
      end
end

Let me know if you have better solution, in fact I am still looking for one…

Testing rails app with RSpec, FactoryGirl, Capybara, Selenium & Headless

In this blog I don’t have any intention to argue or discuss the necessity of Test Driven Development (TDD), but indeed without any doubt I believe testing is necessary. The other day I was watching a talk by Kent Beck where he said “if you don’t find Testing necessary for your code, then don’t do that. Whats the point in arguing about it?” I am also believer of that, as he said testing is all about gaining confidence. Now in this blog I am putting it together how did I use Rspec , Capybara , Selenium to gain this “confidence”.

RSpec has a different repository for rails (https://github.com/rspec/rspec-rails) and a different gem. To put RSpec in project we need to put gem rspec-rails in Gemfilelike this:

group :development,:test do gem
'rspec-rails', '~> 3.0'
end

then do a:

bundler install

Now we need to run a generator command:

rails generate rspec:install

Which basically generates following files:

spec/spec_helper.rb
spec/rails_helper.rb

Later in this blog we will configure it. Now we will write some RSpec tests. RSpec tests can be for many things and of many type. It can be for models, it can be for controllers, it can be for features and so on. There are generator commands to help you creating files.

When I run:

rails generate rspec:features home_page

It creates spec/features/home_page_spec.rb. As you see, every rspec files end with . So when we run bundle exec rspec _spec.rb it finds all these files and test them, we can also run:
bundle exec rspec spec/features

Which will only run feature test. We can also do:

bundle exec rspec spec/features/home_page_spec.rb:2

which will run test of that line until it ends. Now time to write some tests. Before writing test one thing we always need to keep in mind, in every test case we will test only one feature and we will focus only on that feature. (Maybe in later blog I will describe mock and stubs two cool features). If we don’t focus on one feature then it will take more time to debug our test case then writing the patch.

In our RSpec block we will be using describe and it methods. its are the functionality tests and a describe can contain a lot of its. A describe block can hold bunch of it blocks. In another word given/let calls are used at the top of a feature/describe/contextblock and apply to all contained feature/describe/context or scenario/it blocks.

in

spec/features/home_page_spec.rb
require 'rails_helper'
RSpec.feature "home page", type: :feature do
describe "navigation" do
it " shows me menu" do
get "/"
#do testing
assert_select ".navbar", :text => "home"
end
end
it "shows title" # it shows pending because it does not have do block
end

There are alias methods, and other way to write, for an instance feature is an alias for describe …, :type => :feature , background is an alias for before , scenario for it , and given/given! aliases for let/let! , respectively and so on.

We can see that RSpec by default deals with requests like get , post , it is not a browser a user will use see the product. But the main point is it does not have js support and modern website is full of javascripts. So what can I do? We will be using Capybara to mimic browser. We will add following gem to our Gemfile before doing

bundler install

:

gem 'capybara'

Now it is time to re write our test code. We need to add require ‘capybara/rails’ in our spec file.

require 'capybara/rails'
require 'rails_helper'
RSpec.feature "home page", type: :feature do
describe "navigation" do
it " shows me menu" do
visit "/"
expect(page.find('.nav-bar')).to have_content("brand") # page find search for css path
end
end
end

A Capybara cheat sheet which I found to be very useful can be found and here : https://gist.github.com/zhengjia/428105

Now we will integrate:

gem "FactoryGirl"

FactoryGirl is awesome tool to automate model entry creation. Suppose we have a BlogPost model which has many Catogories and one Blogger . In real life this is just a simple model will look like this when we write test case:

blog_post=BlogPost.new({title:"this is title",body:"this is body"})
blog_post.category<<Category.create(name:"technology")
blog_post.blogger=User.create(username="sadaf",password="password",rewritepassword="password")
blog_post.save()

For each post we will need to write this many line of code, and every post will need to have hand written title and body which is tiresome. FactoryGirl solves this problem. We will define Factories in support/factories.rb :

FactoryGirl.define do #active admin factory :admin_user, :class => AdminUser do
sequence(:email){|n| "email#{n}@example.com"}
password "password"
password_confirmation "password"
end
factory :category, :class => Category do
name "category name"
end
factory :blog_post, :class => BlogPost do
association :blogger, factory: :admin_user
sequence(:title){|n| "this is super cool title #{n}"}
sequence(:title){|n| "this is" + " super duper " * n + " body."}
description "static description"

factory :blog_post_with_category do
after(:create) do |post|
create(:category, blog_post: post)
end
end
end
end

so to create AdminUser , using factory_girl we will do:

FactoryGirl.create(:admin_user)
1
FactoryGirl.create(:admin_user)
Every time we call above code it creates different email address, email1@example.com, email2@example.com .., but password remains the same for all cases as it is not in sequence block. BlogPost has a association. and when we will do FactoryGirl.create(:blog_post) it will also create admin_user to satisfy its need, so we don’t need to write it separately. And to create blog_post with category we will need to do following:

blog_post = create(:blog_post_with_category)

Life is pretty easy right now.

....
it " shows me sub-menu on parent-menu click" do
visit "/"
blog_post = create(:blog_post_with_category
expect(page).to have_content(blog_post.title)
end
...

Now we may like to test our beautiful nested navbar is working or not, which is beautiful because it is manipulated by javascript. To do that we need to tag our it or describe block with “js”, which is very simple just add js:true as it or describes parameter and it is done. But before we write our test we will need to add another gem for our webdriver.

gem 'selenium-webdriver'

Now we can write:

describe "navigation" do
....
it " shows me sub-menu on parent-menu click", js:true do
visit "/"
click("parent menu") #text of button
expect(page).not_to have_selector('.submenu',visible:true) # if visible or not
expect(page.find('.submenu')).to have_content("submenu")
end
...
end

So far we run bundle exec rspec we did not see any browser, but now we will see a browser popping up and popping down. Which can be annoying sometime. To solve this either we can use webkit instead of seleniumbut in this blog I will use another gem called headless which turns selenium into a ui less browser which is super cool.

gem 'headless'

then we need to install Xvfb which is as easy as typing

sudo apt-get install Xvfb

Then we need to modify my spec_helper.rb . We need to add following lines at the beginning of the file:

if ENV['HEADLESS'] == 'true'
# require 'rubygems'
require 'headless' # if face load error read below
headless = Headless.new
headless.start
at_exit do
exit_status = $!.status if $!.is_a?(SystemExit)
headless.destroy
exit exit_status if exit_status
end
end

This code is responsible to instantiate headless when we type: ENV=HEADLESS bundle exec rspec and to destroy it. Then in between configure block we need to put before and after blocks which changes browser to selenium for js:

RSpec.configure do |config|
....
config.before(:each) do |example|
if example.metadata[:type] == :request and example.metadata[:js]
Capybara.current_driver = :selenium
end
end
config.after(:each) do |example|
Capybara.use_default_driver
end
....
end

If you see problem loading headless gem.

LoadError: cannot load such file -- headless
from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'

Then you can make headless gem directory executable to all user using chmod:

gem which headless

for me:

var/lib/gems/2.1.0/gems/headless-2.2.0/lib/headless.rb

And then copy the directory of gem before lib and chmod it to 655:

Ghost methods and meta programming can make your life much much much easier

I have been cleaning up some of my codes today at my a ruby on rails app, I had 10 similar with minor difference methods which kept me thinking why would I write 10 separate methods rather than 1 using ghost methods of ruby? But things are a little bit different when we work on rails that is basically the motivation behind this blog.

Suppose my client started to take orders from its customer to build a custom car, so imagine how many of this following methods we will need to write!!

def add_tire
    @current_build = get_current_build
    Tire = Tire.find(params[:tire_id])
    # todo do stuff
    redirect_to current_build_url
end

def add_engine
    @current_build = get_current_build
    engine = Engine.find(params[:engine_id])
    # todo add methods
    redirect_to current_build_url
end

#....more similar looking add_items methods

Probably a hundred or even the method can reach thousand. But don’t surprise, ruby has its own elegant way to handle this type of repeating. Using ruby support of ghost methods, we can solve this problem. Usually a ghost methods are being handled using method_missing, if you already are not aware of the power of method_missing, then let me tell you in short, method_missing is a method that is being called when a method is being called but it is not defined in the object. So basically when a method is being called in check within it, if not found it checks its parent if that method exists there or not, if not found then it checks its parent too, this check goes all the way to ruby object class as object is the parent of all ruby class.

Now let me show you an example:

class Test
    def say_hi(to)
        "Saying hi to "+to
    end
    def method_missing(name, *args)
        say_hi(*args)
    end
end

irb(main):022:0> Test.new.say_hi("mike")
=> "saying hi to mike"
irb(main):023:0> Test.new.say_hello("mike")
=> "saying hi to mike"

 

 

This hello, or hi method does not exist in Test class, but they are acting as if they exist, thats why they are called ghost methods.

Every ruby class has a send method, that takes method name as string argument and capable to run that method.

class Test
    def say_hi(to)
        "Saying hi to "+to
    end
    def say_bye(to)
        "Saying bye to "+to
    end
    def method_missing(method, *args)
        self.send("#{method}",*args)
    end
end

irb(main):023:0> Test.new.say_hi("mike")
=> "saying hi to mike"
irb(main):022:0> Test.new.hi("mike")
=> "saying hi to mike"
irb(main):023:0> Test.new.say_bye("mike")
=> "saying bye to mike"
irb(main):022:0> Test.new.bye("mike")
=> "saying bye to mike"

 

 

We have seen how to call a method dynamically, but we may also need to call another class name dynamically, for that Object.const_get takes string as parameter and call that class. (Object.const_get "Test").new.send("hi","mike") is the same as Test.new.hi("mike").

irb(main):023:0> (Object.const_get "Test").new.send("hi","mike")
=> "saying hi to mike"

 

In ruby meta programming there is a method called eval which exicutes a string as ruby code.

irb(main):025:0> eval("Test.new.hi('mike')")
=> "saying hi to mike"

 

Now I think we know everything to attempt to write our ghost method at controller, but writing ghost method with method_missing does not help much in rails controller because rails does not always require methods to be present in controller if corresponding template exists. So when action is missing it starts to attempt rendering templates, thats why method_missing does not invokes. But fear not, we have action_missing which is being called when ruby failed to find action. Here is an example of our car order case:

  def action_missing(m, *args, &block)
    if m.starts_with? "add_"
      k=(m.split "_", 2) [1]

      @current_build = get_current_build
      qty=params[:qty]      

      build= (Object.const_get "#{k.camelize}Build").create({(k+"_id").to_sym => params[(k+"_id").to_sym], :market_status_id => params[:market_status]})
      eval("@current_build.#{k}_builds << build")
      redirect_to current_build_url
    else
      super
    end
  end

Here we go, we have saved at least 100 lines of code, not only that, now we need to change only one line of code on change of methods, where previously, we had to change 100 lines on change.

Preparing DataCleaner with RSpec, Capybara, Selenium for testing Rails app

“data_cleaner” is a beautiful gem that cleans database. We can use it for cleaning our database for each test case. I have been banging my head for hours to make it work, even though I had followed the right documentation. So I thought may be I would look “COOL” if I could write a blog describing steps, at the same time it could be helpful to other.

I would add at my Gemfile:

gem 'database_cleaner'

Then I would like to do a

bundler install

Now when database_cleaner gem is install I want to add DatabaseCleaner to my RSpec configuration, it is always a good idea to keep this code separated, so I have created a different file ‘support/database_cleaner.rb’. We need to set DataBase cleanup strategy, by default RSpec’s get/post request driven testing uses  Transaction but Capybara, Selenium which is being used for testing js uses Truncation as strategy so we will need to write at least two different strategies. It is also good practice to clean up everything before we run the test so even if database had any data by mistake, it can not mess with my test cases. So my ‘support/database_cleaner.rb’ looks like:

RSpec.configure do |config|
  #It runs before the entire test suite runs and it clears the test database. 
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end
  
  # itsets the default database cleaning strategy to be transactions. 
  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end
  
  #Tests which is flagged as :js => true by default, these tests using Capybara 
  # using Capybaras test server and firing an actual browser window via the Selenium 
  # backend. For these types of tests, our previous transactions won’t work, so we need to override
  # the setting and chooses the “truncation” strategy instead.
  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end


# Now we need to start and end database cleaner.
  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end

 

Now we need to fix few things at  `rails_helper.rb`.
We will add require ‘support/database_cleaner’.

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'spec_helper'
require 'support/database_cleaner'

But still it won’t work unless we, change `rails_helper.rb` from:
config.use_transactional_fixtures = true
To:
config.use_transactional_fixtures = false

If you try to put `config.use_transactional_fixtures = false` at `spec_helper.rb` then you may need to change the order of rails and spec_helper at `rails_helper.rb`

require 'rspec/rails'
require 'spec_helper'

or else you may get:

spec_helper.rb:53:in `block in <top (requir
ed)>': undefined method `use_transactional_fixtures=' for #<RSpec::Core::Configuration:0x00000001d971b8>