Rails 5 adds another base class Application Job for jobs

This blog is part of our Rails 5 series.

Rails 5 has added another base class ApplicationJob which inherits from ActiveJob::Base. Now by default all new Rails 5 applications will have application_job.rb.

# app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
end

In Rails 4.x if we want to use ActiveJob then first we need to generate a job and all the generated jobs directly inherit from ActiveJob::Base.

# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  def perform(*guests)
    # Do something later
  end
end

Rails 5 adds explicit base class ApplicationJob for ActiveJob. As you can see this is not a big change but it is a good change in terms of being consistent with how controllers have ApplicationController and models have ApplicationRecord.

Now ApplicationJob will be a single place to apply all kind of customizations and extensions needed for an application, instead of applying patch on ActiveJob::Base.

Upgrading from Rails 4.x

When upgrading from Rails 4.x to Rails 5 we need to create application_job.rb file in app/jobs/ and add the following content.

# app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
end

We also need to change all the existing job classes to inherit from ApplicationJob instead of ActiveJob::Base.

Here is the revised code of GuestCleanupJob class.

# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
  queue_as :default

  def perform(*guests)
    # Do something later
  end
end

Rails 5 allows to update records in AR Relation with callbacks and validations

This blog is part of our Rails 5 series.

The update_all method when called on an ActiveRecord::Relation object updates all the records without invoking any callbacks and validations on the records being updated.

Rails 5 supports update method on an ActiveRecord::Relation object that runs callbacks and validations on all the records in the relation.

people = Person.where(country: 'US')
people.update(language: 'English', currency: 'USD')

Internally, the above code runs update method on each Person record whose country is 'US'.

Let’s see what happens when update is called on a relation in which validations fail on few records.

We have a Note model. For simplicity let’s add a validation that note_text field cannot be blank for first three records.

class Note < ApplicationRecord
  validate :valid_note

  def valid_note
   errors.add(:note_text, "note_text is blank") if id <= 3 && note_text.blank?
  end
end

Now let’s try and update all the records with blank note_text.

 > Note.all.update(note_text: '')
  Note Load (0.3ms)  SELECT `notes`.* FROM `notes`
   (0.1ms)  BEGIN
   (0.1ms)  ROLLBACK
   (0.1ms)  BEGIN
   (0.1ms)  ROLLBACK
   (0.1ms)  BEGIN
   (0.1ms)  ROLLBACK
   (0.1ms)  BEGIN
  SQL (2.9ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 3
   (0.7ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 4
   (1.2ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 5
   (0.3ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (3.4ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 6
   (0.2ms)  COMMIT

 => [#<Note id: 1, user_id: 1, note_text: "", created_at: "2016-06-03 10:02:54", updated_at: "2016-06-16 19:42:21">,
 #<Note id: 2, user_id: 1, note_text: "", created_at: "2016-06-03 10:03:54", updated_at: "2016-06-16 19:42:21">,
 #<Note id: 3, user_id: 1, note_text: "", created_at: "2016-06-03 12:35:20", updated_at: "2016-06-03 12:35:20">,
 #<Note id: 4, user_id: 1, note_text: "", created_at: "2016-06-03 14:15:15", updated_at: "2016-06-16 19:14:20">,
 #<Note id: 5, user_id: 1, note_text: "", created_at: "2016-06-03 14:15:41", updated_at: "2016-06-16 19:42:21">,
 #<Note id: 6, user_id: 1, note_text: "", created_at: "2016-06-03 14:16:20", updated_at: "2016-06-16 19:42:21">]

We can see that failure of validations on records in the relation does not stop us from updating the valid records.

Also the return value of update on AR Relation is an array of records in the relation. We can see that the attributes in these records hold the values that we wanted to have after the update.

For example in the above mentioned case, we can see that in the returned array, the records with ids 1, 2 and 3 have blank note_text values even though those records weren’t updated.

Hence we may not be able to rely on the return value to know if the update is successful on any particular record.

For scenarios where running validations and callbacks is not important and/or where performance is a concern it is advisable to use update_all method instead of update method.

Rails 5 prevents destructive action on production database

This blog is part of our Rails 5 series.

Sometimes while debugging production issue mistakenly developers execute commands like RAILS_ENV=production rake db:schema:load. This wipes out data in production.

Users of heroku download all the config variables to local machine to debug production problem and sometimes developers mistakenly execute commands which wipes out production data. This has happened enough number of times to heroku users that Richard Schneeman of heroku decided to do something about this issue.

Rails 5 prevents destructive action on production database

Rails 5 has added a new table ar_internal_metadata to store environment version which is used at the time of migrating the database.

When the first time rake db:migrate is executed then new table stores the value production. Now whenever we load database schema or database structure by running rake db:schema:load or rake db:structure:load Rails will check if Rails environment is “production” or not. If not then Rails will raise an exception and thus preventing the data wipeout.

To skip this environment check we can manually pass DISABLE_DATABASE_ENVIRONMENT_CHECK=1 as an argument with load schema/structure db command.

Here is an example of running rake db:schema:load when development db is pointing to production database.

$ rake db:schema:load

rake aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database.
If you are sure you want to continue, run the same command with the environment variable:
DISABLE_DATABASE_ENVIRONMENT_CHECK=1

As we can see Rails prevented data wipeout in production.

This is one of those features which hopefully you won’t notice. However if you happen to do something destructive to your production database then this feature will come in handy.

Rails 5 adds finish option in find_in_batches

This blog is part of our Rails 5 series.

In Rails 4.x we had start option in find_in_batches method.

Person.find_in_batches(start: 1000, batch_size: 2000) do |group|
  group.each { |person| person.party_all_night! }
end

The above code provides batches of Person starting from record whose value of primary key is equal to 1000.

There is no end value for primary key. That means in the above case all the records that have primary key value greater than 1000 are fetched.

Rails 5 introduces finish option that serves as an upper limit to the primary key value in the records being fetched.

Person.find_in_batches(start: 1000, finish: 9500, batch_size: 2000) do |group|
  group.each { |person| person.party_all_night! }
end

The above code ensures that no record in any of the batches has the primary key value greater than 9500.

Rails 5 introduces country_zones helper method to ActiveSupport::TimeZone

This blog is part of our Rails 5 series.

Before Rails 5, we could fetch all time zones for US by using us_zones method as follows.

> puts ActiveSupport::TimeZone.us_zones.map(&:to_s)
(GMT-10:00) Hawaii
(GMT-09:00) Alaska
(GMT-08:00) Pacific Time (US & Canada)
(GMT-07:00) Arizona
(GMT-07:00) Mountain Time (US & Canada)
(GMT-06:00) Central Time (US & Canada)
(GMT-05:00) Eastern Time (US & Canada)
(GMT-05:00) Indiana (East)

Such functionality of getting all the TimeZone objects for a country was implemented only for one country, US.

The TimeZone class internally uses the TzInfo gem which does have an api for providing timezones for all the countries.

Realizing this, the Rails community decided to introduce a helper method country_zones to ActiveSupport::TimeZone class that is able to fetch a collection of TimeZone objects belonging to a country specified by its ISO 3166-1 Alpha2 code.

> puts ActiveSupport::TimeZone.country_zones('us').map(&:to_s)
(GMT-10:00) Hawaii
(GMT-09:00) Alaska
(GMT-08:00) Pacific Time (US & Canada)
(GMT-07:00) Arizona
(GMT-07:00) Mountain Time (US & Canada)
(GMT-06:00) Central Time (US & Canada)
(GMT-05:00) Eastern Time (US & Canada)
(GMT-05:00) Indiana (East)

>puts ActiveSupport::TimeZone.country_zones('fr').map(&:to_s)
 (GMT+01:00) Paris

Rails 5 provides fragment caching in Action Mailer views

This blog is part of our Rails 5 series.

Fragment cache helps in caching parts of the view instead of caching the entire view. Fragment caching is used when different parts of the view need to be cached and expired separately. Before Rails 5, fragment caching was supported only in Action View templates.

Rails 5 provides fragment caching in Action Mailer views . To use this feature, we need to configure our application as follows.

config.action_mailer.perform_caching = true

This configuration specifies whether mailer templates should perform fragment caching or not. By default, this is set to false for all environments.

Fragment caching in views

We can do caching in mailer views similar to application views using cache method. Following example shows usage of fragment caching in mailer view of the welcome mail.

<body>

 <% cache 'signup-text' do %>
   <h1>Welcome to <%= @company.name %></h1>
   <p>You have successfully signed up to <%= @company.name %>, Your username is:
 <% end %>

     <%= @user.login %>.
     <br />
   </p>

 <%= render :partial => 'footer' %>

</body>

When we render view for the first time, we can see cache digest of the view and its partial.

  Cache digest for app/views/user_mailer/_footer.erb: 7313427d26cc1f701b1e0212498cee38
  Cache digest for app/views/user_mailer/welcome_email.html.erb: 30efff0173fd5f29a88ffe79a9eab617
  Rendered user_mailer/_footer.erb (0.3ms)
  Rendered user_mailer/welcome_email.html.erb (26.1ms)
  Cache digest for app/views/user_mailer/welcome_email.text.erb: 77f41fe6159c5736ab2026a44bc8de55
  Rendered user_mailer/welcome_email.text.erb (0.2ms)
UserMailer#welcome_email: processed outbound mail in 190.3ms

We can also use fragment caching in partials of the action mailer views with cache method. Fragment caching is also supported in multipart emails.

Rails 5 adds OR support in Active Record

This blog is part of our Rails 5 series.

Rails 5 has added OR method to Active Relation for generating queries with OR clause.

>> Post.where(id: 1).or(Post.where(title: 'Learn Rails'))
   SELECT "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."title" = ?)  [["id", 1], ["title", "Learn Rails"]]

=> <ActiveRecord::Relation [#<Post id: 1, title: 'Rails'>]>

This returns ActiveRecord::Relation object, which is logical union of two relations.

Some Examples of OR usage

With group and having
>> posts = Post.group(:user_id)
>> posts.having('id > 3').or(posts.having('title like "Hi%"'))
SELECT "posts".* FROM "posts" GROUP BY "posts"."user_id" HAVING ((id > 2) OR (title like "Rails%"))

=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 4>,
#<Post id: 6, title: "Another new blog", user_id: 6>]>
With scope
class Post < ApplicationRecord
  scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") }
end

>> Post.contains_blog_keyword.or(Post.where('id > 3'))
SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 3))

=> <ActiveRecord::Relation [#<Post id: 4, title: "A new blog", user_id: 6>,
#<Post id: 5, title: "Rails blog", user_id: 4>,
#<Post id: 6, title: "Another new blog", user_id: 6>]>
With combination of scopes
class Post < ApplicationRecord

  scope :contains_blog_keyword, -> { where("title LIKE '%blog%'") }
  scope :id_greater_than, -> (id) {where("id > ?", id)}

  scope :containing_blog_keyword_with_id_greater_than, ->(id) { contains_blog_keyword.or(id_greater_than(id)) }
end

>> Post.containing_blog_keyword_with_id_greater_than(2)
SELECT "posts".* FROM "posts" WHERE ((title LIKE '%blog%') OR (id > 2)) ORDER BY "posts"."id" DESC

=> <ActiveRecord::Relation [#<Post id: 3, title: "Hi", user_id: 6>,
#<Post id: 4, title: "A new blog", user_id: 6>,
#<Post id: 5, title: "Another new blog", user_id: 6>,
<#Post id: 6, title: "Another new blog", user_id: 6>]>

Constraints for using OR method

The two relations must be structurally compatible, they must be scoping the same model, and they must differ only by WHERE or HAVING.

In order to use OR operator, neither relation should have a limit, offset, or distinct.

>> Post.where(id: 1).limit(1).or(Post.where(:id => [2, 3]))

ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:limit]

When limit, offset or distinct is passed only with one relation, then it throws ArgumentError as shown above.

As of now, we can use limit, offset or distinct when passed with both the relations and with same the parameters.

>> Post.where(id: 1).limit(2).or(Post.where(:id => [2, 3]).limit(2))

SELECT  "posts".* FROM "posts" WHERE ("posts"."id" = ? OR "posts"."id" IN (2, 3)) LIMIT ?  [["id", 1], ["LIMIT", 2]]

=> <ActiveRecord::Relation [#<Post id: 1, title: 'Blog', user_id: 3, published: true>,
#<Post id: 2, title: 'Rails 5 post', user_id: 4, published: true>]>

There is an issue open in which discussions are ongoing regarding completely stopping usage of limit, offset or distinct when using with or.

Rails 5 adds ArrayInquirer and provides friendlier way to check contents in an array

This blog is part of our Rails 5 series.

Rails 5 introduces Array Inquirer that wraps an array object and provides friendlier methods to check for the presence of elements that can be either a string or a symbol.

pets = ActiveSupport::ArrayInquirer.new([:cat, :dog, 'rabbit'])

> pets.cat?
#=> true

> pets.rabbit?
#=> true

> pets.elephant?
#=> false

Array Inquirer also has any? method to check for the presence of any of the passed arguments as elements in the array.

pets = ActiveSupport::ArrayInquirer.new([:cat, :dog, 'rabbit'])

> pets.any?(:cat, :dog)
#=> true

> pets.any?('cat', 'dog')
#=> true

> pets.any?(:rabbit, 'elephant')
#=> true

> pets.any?('elephant', :tiger)
#=> false

Since ArrayInquirer class inherits from Array class, its any? method performs same as any? method of Array class when no arguments are passed.

pets = ActiveSupport::ArrayInquirer.new([:cat, :dog, 'rabbit'])

> pets.any?
#=> true

> pets.any? { |pet| pet.to_s == 'dog' }
#=> true

Use inquiry method on array to fetch Array Inquirer version

For any given array we can have its Array Inquirer version by calling inquiry method on it.

pets = [:cat, :dog, 'rabbit'].inquiry

> pets.cat?
#=> true

> pets.rabbit?
#=> true

> pets.elephant?
#=> false

Usage of Array Inquirer in Rails code

Rails 5 makes use of Array Inquirer and provides a better way of checking for the presence of given variant.

Before Rails 5 code looked like this.

request.variant = :phone

> request.variant
#=> [:phone]

> request.variant.include?(:phone)
#=> true

> request.variant.include?('phone')
#=> false

Corresponding Rails 5 version is below.

request.variant = :phone

> request.variant.phone?
#=> true

> request.variant.tablet?
#=> false

Rails 5 renamed transactional fixtures to transactional tests

This blog is part of our Rails 5 series.

In Rails 4.x we have transactional fixtures that wrap each test in a database transaction. This transaction rollbacks all the changes at the end of the test. It means the state of the database, before the test is same as after the test is done.

By default this functionality is enabled. We can choose to disable it in a test case class by setting the class attribute use_transactional_fixtures to false

class FooTest < ActiveSupport::TestCase
  self.use_transactional_fixtures = false
end

Rails also comes with fixtures for tests. So it may seem that use_transactional_fixtures has something to do with the Rails fixtures. A lot of people don’t use fixtures and they think that they should disable use_transactional_fixtures because they do not use fixtures.

To overcome this confusion, Rails 5 has renamed transactional fixtures to transactional tests making it clear that it has nothing to do with the fixtures used in tests.

In Rails 5, the above example will be written as follows.

class FooTest < ActiveSupport::TestCase
  self.use_transactional_tests = false
end

Data exchange between React Native app and WebView

A project we recently worked on needed some complicated charts. We built those charts using JavaScript library and it worked fine on browsers.

Now we need to build mobile app using React Native and it would take a lot of time to build those charts natively. So we decided to use WebView to render the html pages which already displays charts nicely.

React Native comes with WebView component by default. So rendering the html page using WebView was easy. However, once the page is rendered the React Native app could not exchange any data with the web page.

In this blog post we’ll discuss how to make React Native app communicate with the pages rendered using WebView with the help of react-native-webview-bridge library.

What is React Native WebView Bridge ?

react-native-webview-bridge is a wrapper on top of React Native’s WebView component with some extra features.

First we need to install react-native-webview-bridge package.

npm install react-native-webview-bridge --save

Next we need to import the WebView bridge module.

// ES6
import WebViewBridge from 'react-native-webview-bridge';

// ES5
let WebViewBridge = require('react-native-webview-bridge')

Now let’s create a basic React component. This component will be responsible for rendering html page using WebView.

React.createClass({

  render: function() {
    return (
      <WebViewBridge
        ref="webviewbridge"
        onBridgeMessage={this.onBridgeMessage.bind(this)}
        source={ {uri: "https://www.example.com/charts"} } />
    );
  }
});

After the component is mounted then we will send data to web view.

componentDidMount() {
  let chartData = {data: '...'};

  // Send this chart data over to web view after 5 seconds.
  setTimeout(() => {
    this.refs.webviewbridge.sendToBridge(JSON.stringify(data));
  }, 5000);
},

Next, We will add code to receive data from web view.

onBridgeMessage: function (webViewData) {
  let jsonData = JSON.parse(webViewData);

  if (jsonData.success) {
    Alert.alert(jsonData.message);
  }
  console.log('data received', webViewData, jsonData);
  //.. do some react native stuff when data is received
}

At this time code should look something like this.

React.createClass({

  componentDidMount() {
    let chartData = {data: '...'};

    // Send this chart data over to web view after 5 seconds.
    setTimeout(() => {
      this.refs.webviewbridge.sendToBridge(JSON.stringify(data));
    }, 5000);
  },

  render: function() {
    return (
      <WebViewBridge
        ref="webviewbridge"
        onBridgeMessage={this.onBridgeMessage.bind(this)}
        source={ {
          uri: "https://www.example.com/charts"
        } } />
    );
  },

  onBridgeMessage: function (webViewData) {
    let jsonData = JSON.parse(webViewData);

    if (jsonData.success) {
      Alert.alert(jsonData.message);
    }
    console.log('data received', webViewData, jsonData);
    //.. do some react native stuff when data is received
  }
});

Okay, We’ve added all the React Native side of code. We now need to add some JavaScript code on our web page to complete the functionality.

Why do we need to add JavaScript snippet on my web page?

This is a two way data exchange scenario. When our React Native app sends any data, this JavaScript snippet will parse that data and will trigger functions accordingly. We’ll also be able to send some data back to React Native app from JavaScript.

The example in the README of WebViewBridge library shows how to inject JavaScript snippet in React component. However, we prefer JavaScript code to be added to web page directly since it provides more control and flexibility.

Coming back to our implementation, Let’s now add the snippet in our web page.

<script>
 (function () {
    if (WebViewBridge) {

      // This function gets triggered when data received from React Native app.
      WebViewBridge.onMessage = function (reactNativeData) {

        // Converts the payload in JSON format.
        var jsonData = JSON.parse(reactNativeData);

        // Passes data to charts for rendering
        renderChart(jsonData.data);

        // Data to send from web view to React Native app.
        var dataToSend = JSON.stringify({success: true, message: 'Data received'});

        // Keep calm and send the data.
        WebViewBridge.send(dataToSend);
      };
    }
  }())
</script>

Done! We’ve achieved our goal of having a two way communication channel between our React Native app and the web page.

Checkout this link for more examples of how to use WebView Bridge.

Rails 5 adds ignored_columns for Active Record

This blog is part of our Rails 5 series.

Somtimes we need to ignore a database column. However Rails 4.x doesn’t have any officially defined method which ignores a database column from Active Record. We can apply our patch on model to ignore certain columns.

class User < ActiveRecord::Base

  # Ignoring employee_email column
  def self.columns
    super.reject {|column| column.name == 'employee_email'}
  end

end

Rails 5 added ignored_columns

Rails 5 has added ignored_columns to ActiveRecord::Base class.

Here is revised code after using ignored_columns method.

class User < ApplicationRecord
  self.ignored_columns = %w(employee_email)
end

Rails 5 adds a hidden field on collection radio buttons

This blog is part of our Rails 5 series.

Consider the following form which has only one input role_id which is accepted through collection_radio_button.

<%= form_for(@user) do |f| %>
  <%= f.collection_radio_buttons :role_id, @roles, :id, :name %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

In the controller, we can access role_id using the strong parameters.

def user_params
  params.require(:user).permit(:role_id)
end

When we try to submit this form without selecting any radio button in Rails 4.x, we will get 400 Bad Request error with following message.

ActionController::ParameterMissing (param is missing or the value is empty: user):.

This is because following parameters were sent to server in Rails 4.x .

Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "commit"=>"Create User"}

According to HTML specification, when multiple parameters are passed to collection_radio_buttons and no option is selected, web browsers do not send any value to the server.

Solution in Rails 5

Rails 5 adds hidden field on the collection_radio_buttons to avoid raising an error when the only input on the form is collection_radio_buttons. The hidden field has the same name as collection radio button and has blank value.

Following parameters will be sent to server in Rails 5 when above user form is submitted:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "user"=>{"role_id"=>""}, "commit"=>"Create User"}

In case we don’t want the helper to generate this hidden field, we can specify include_hidden: false.

<%= f.collection_radio_buttons :role_id, Role.all, :id, :name, include_hidden: false %>

Rails 5 extracted attributes assignment from Active Record to Active Model

This blog is part of our Rails 5 series.

Before Rails 5, we could use assign_attributes and have bulk assignment of attributes only for objects whose classes are inherited from ActiveRecord::Base class.

In Rails 5 we can make use of assign_attributes method and have bulk assignment of attributes even for objects whose classes are not not inherited from ActiveRecord::Base.

This is possible because the attributes assignment code is now moved from ActiveRecord::AttributeAssignment to ActiveModel::AttributeAssignment module.

To have this up and running, we need to include ActiveModel::AttributeAssignment module to our class.

class User
  include ActiveModel::AttributeAssignment
  attr_accessor :email, :first_name, :last_name
end

user = User.new
user.assign_attributes({email:      'sam@example.com',
                        first_name: 'Sam',
                        last_name:  'Smith'})
> user.email
#=> "sam@example.com"
> user.first_name
#=> "Sam"

Rails 5 supports configuring Active Job backend adapter for each job

This blog is part of our Rails 5 series.

Before Rails 5 we had ability to configure Active Job queue_adapter at an application level. If we want to use sidekiq as our backend queue adapter we would configure it as following.

config.active_job.queue_adapter = :sidekiq

This queue_adapter would be applicable to all jobs.

Rails 5 provides ability to configure queue_adapter on a per job basis. It means queue_adapter for one job can be different to that of the other job.

Let’s suppose we have two jobs in our brand new Rails 5 application. EmailJob is responsible for processing basic emails and NewsletterJob sends out news letters.

class EmailJob < ActiveJob::Base
  self.queue_adapter = :sidekiq
end

class NewletterJob < ActiveJob::Base
end

EmailJob.queue_adapter
 => #<ActiveJob::QueueAdapters::SidekiqAdapter:0x007fb3d0b2e4a0>

NewletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::AsyncAdapter:0x007fb3d0c61b88>

We are now able to configure sidekiq queue adapter for EmailJob. In case of NewsletterJob we fallback to the global default adapter which in case of a new Rails 5 app is async.

Moreover, in Rails 5, when one job inherits other job, then queue adapter of the parent job gets persisted in the child job unless child job has configuration to change queue adapter.

Since news letters are email jobs we can make NewsLetterJob inherit from EmailJob.

Below is an example where EmailJob is using rescue while NewsLetterJob is using sidekiq.

class EmailJob < ActiveJob::Base
  self.queue_adapter = :resque
end

class NewsletterJob < EmailJob
end

EmailJob.queue_adapter
 => #<ActiveJob::QueueAdapters::ResqueAdapter:0x007fe137ede2a0>

NewsletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::ResqueAdapter:0x007fe137ede2a0>

class NewsletterJob < EmailJob
  self.queue_adapter = :sidekiq
end

NewsletterJob.queue_adapter
 => #<ActiveJob::QueueAdapters::SidekiqAdapter:0x007fb3d0b2e4a0>

Rails 5 accepts 1 or true for acceptance validation

This blog is part of our Rails 5 series.

validates_acceptance_of is a good validation tool for asking users to accept “terms of service” or similar items.

Before Rails 5, the only acceptable value for a validates_acceptance_of validation was 1.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
end

> user = User.new(terms_of_service: "1")
> user.valid?
#=> true

Having acceptable value of 1 does cause some ambiguity because general purpose of acceptance validation is for attributes that hold boolean values.

So in order to have true as acceptance value we had to pass accept option to validates_acceptance_of as shown below.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service, accept: true
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = '1'
> user.valid?
#=> false

Now this comes with the cost that 1 is no longer an acceptable value.

In Rails 5, we have true as a default value for acceptance along with the already existing acceptable value of 1.

In Rails 5 the previous example would look like as shown below.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = '1'
> user.valid?
#=> true

Rails 5 allows user to have custom set of acceptable values

In Rails 5, :accept option of validates_acceptance_of method supports an array of values unlike a single value that we had before.

So in our example if we are to validate our terms_of_service attribute with any of true, "y", "yes" we could have our validation as follows.

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service, accept: [true, "y", "yes"]
end

> user = User.new(terms_of_service: true)
> user.valid?
#=> true

> user.terms_of_service = 'y'
> user.valid?
#=> true

> user.terms_of_service = 'yes'
> user.valid?
#=> true

Rails 5 supports bi-directional destroy dependency

This blog is part of our Rails 5 series.

In Rails 4.x, it is not possible to have destroy dependency on both sides of a bi-directional association between the two models as it would result in an infinite callback loop causing SystemStackError: stack level too deep.

class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy
end

class Profile < ActiveRecord::Base
  belongs_to :user, dependent: :destroy
end

Calling User#destroy or Profile#destroy would lead to an infinite callback loop.

>> user = User.first
=> <User id: 4, name: "George">

>> user.profile
=> <Profile id: 4>

>> user.destroy
=> DELETE FROM `profiles` WHERE `profiles`.`id` = 4
   ROLLBACK
SystemStackError: stack level too deep

Rails 5 supports bi-directional destroy dependency without triggering infinite callback loop.

>> user = User.first
=> <User id: 4, name: "George">

>> user.profile
=> <Profile id: 4, about: 'Rails developer', works_at: 'ABC'>

>> user.destroy
=> DELETE FROM "profiles" WHERE "posts"."id" = ?  [["id", 4]]
   DELETE FROM "users" WHERE "users"."id" = ?  [["id", 4]]
=> <User id: 4, name: "George">

There are many instances like above where we need to destroy an association when it is destroying itself, otherwise it may lead to orphan records.

This feature adds responsibility on developers to ensure adding destroy dependency only when it is required as it can have unintended consequences as shown below.

class User < ApplicationRecord
  has_many :posts, dependent: :destroy
end

class Post < ApplicationRecord
  belongs_to :user, dependent: :destroy
end

>> user = User.first
=> <User id: 4, name: "George">

>> user.posts
=> <ActiveRecord::Associations::CollectionProxy [<Post id: 11, title: 'Ruby', user_id: 4>, #<Post id: 12, title: 'Rails', user_id: 4>]>

As we can see “user” has two posts. Now we will destroy first post.

>> user.posts.first.destroy
=> DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 11]]
   SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 4]]
   DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 12]]
   DELETE FROM "users" WHERE "users"."id" = ?  [["id", 4]]

As we can see, we wanted to remove post with id “11”. However post with id “12” also got deleted. Not only that but user record got deleted too.

In Rails 4.x this would have resulted in SystemStackError: stack level too deep .

So we should use this option very carefully.

Rails 5 adds after_{create,update,delete}_commit callbacks aliases

This blog is part of our Rails 5 series.

Rails 4.x has after_commit callback. after_commit is called after a record has been created, updated or destroyed.

class User < ActiveRecord::Base
  after_commit :send_welcome_mail, on: create
  after_commit :send_profile_update_notification, on: update
  after_commit :remove_profile_data, on: destroy

  def send_welcome_mail
    EmailSender.send_welcome_mail(email: email)
  end
end

Rails 5 added new aliases

Rails 5 had added following three aliases.

  • after_create_commit
  • after_update_commit
  • after_destroy_commit

Here is revised code after using these aliases.

class User < ApplicationRecord
  after_create_commit	:send_welcome_mail
  after_update_commit	:send_profile_update_notification
  after_destroy_commit	:remove_profile_data

  def send_welcome_mail
    EmailSender.send_welcome_mail(email: email)
  end
end

Note

We earlier stated that after_commit callback is executed at the end of transaction. Using after_commit with a transaction block can be tricky. Please checkout our earlier post about Gotcha with after_commit callback in Rails .

Rails 5 allows updating a record without updating timestamps

This blog is part of our Rails 5 series.

In Rails 4.x, when we save an ActiveRecord object then Rails automatically updates fields updated_at or updated_on.

>> user = User.new(name: 'John', email: 'john@example.com')
>> user.save
 INSERT INTO "users" ("name", "created_at", "updated_at", "email") VALUES (?, ?, ?, ?)  [["name", "John"], ["created_at", 2016-03-16 09:12:44 UTC], ["updated_at", 2016-03-16 09:12:44 UTC], ["email", "john@example.com"]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:12:44 UTC +00:00

>> user.name = "Mark"
>> user.save
  UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["name", "Mark"], ["updated_at", 2016-03-16 09:15:30 UTC], ["id", 12]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

Addition of touch option in ActiveRecord::Base#save

In Rails 5, by passing touch: false as an option to save, we can update the object without updating timestamps. The default option for touch is true.

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

>> user.name = "Dan"
>> user.save(touch: false)
  UPDATE "users" SET "name" = ? WHERE "users"."id" = ?  [["name", "Dan"], ["id", 12]]
=> true

>> user.updated_at
=> Wed, 16 Mar 2016 09:15:30 UTC +00:00

This works only when we are updating a record and does not work when a record is created.

>> user = User.new(name: 'Tom', email: 'tom@example.com')
>> user.save(touch: false)
 INSERT INTO "users" ("name", "created_at", "updated_at", "email") VALUES (?, ?, ?, ?)  [["name", "Tom"], ["created_at", 2016-03-21 06:57:23 UTC], ["updated_at", 2016-03-21 06:57:23 UTC], ["email", "tom@example.com"]])

>> user.updated_at
=> Mon, 21 Mar 2016 07:04:04 UTC +00:00

Rails 5 adds a way to get information about types of failed validations

This blog is part of our Rails 5 series.

Let’s look at a validation example in Rails 4.x.

class User < ActiveRecord::Base
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.messages
=> {:email=>["can't be blank"]}

In this case, we do not get any information about the type of failed validation as ActiveModel#Errors only gives the attribute name and the translated error message.

This works out well for normal apps. But in case of API only applications, sometimes we want to allow the client consuming the API to generate customized error message as per their needs. We don’t want to send the final translated messages in such cases. Instead if we could just send details that presence validation failed for :name attribute, the client app would be able to customize the error message based on that information.

In Rails 5, it is now possible to get such details about which validations failed for a given attribute.

We can check this by calling details method on the ActiveModel#Errors instance.

class User < ApplicationRecord
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.details
=> {:email=>[{:error=>:blank}]}

We can also add custom validator types as per our need.

# Custom validator type
>> user = User.new
>> user.errors.add(:name, :not_valid, message: "The name appears invalid")

>> user.errors.details
=> {:name=>[{:error=>:not_valid}]}

# Custom error with default validator type :invalid

>> user = User.new
>> user.errors.add(:name)

>> user.errors.details
=> {:name=>[{:error=>:invalid}]}

# More than one error on one attribute

>> user = User.new
>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet")
>> user.errors.add(:password, :invalid_length, message: "Password must have atleast 8 characters")

>> user.errors.details
=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}

Passing contextual information about the errors

We can also send contextual data for the validation to the Errors#add method. This data can be later accessed via Errors#details method because Errors#add method forwards all options except :message, :if, :unless, and :on to details.

For eg. we can say that the password is invalid because ! is not allowed, as follows.

class User < ApplicationRecord
  validate :password_cannot_have_invalid_character

  def password_cannot_have_invalid_character
    if password.scan("!").present?
      errors.add(:password, :invalid_character, not_allowed: "!")
    end
  end
end

>> user = User.create(name: 'Mark', password: 'Ra!ls')
>> user.errors.details
=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]}

We can also use this feature in our Rails 4.x apps by simply installing gem active_model-errors_details.

Using Image as a Container in React Native

Adding a nice looking background to a screen makes an app visually appealing. It also makes the app look more sleek and elegant. Let us see how we can leverage this technique in React Native and add an image as a background.

We’ll need to create different sizes of the background image, which we’re going to use as a container. React Native will pick up appropriate image based on the device’s dimension (check Images guide for more information).

- login-background.png (375x667)
- login-background@2x.png (750x1134)
- login-background@3x.png (1125x2001)

Now we’ll use these images in our code as container.

//...
render() {
    return (
      <Image
        source={require('./images/login-background.png')}
        style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </Image>
    );
  }
//...

const styles = StyleSheet.create({
  container: {
    flex: 1,
    width: undefined,
    height: undefined,
    backgroundColor:'transparent',
    justifyContent: 'center',
    alignItems: 'center',
  },
});

We’ve intentionally left height and width of the image as undefined. This will let React Native take the size of the image from the image itself. This way, we can use Image component as View and add other components as a children to build UI. image as container in react native