Rails

Using Rails with webpack generated assets

Our team started using webpack with Rails. Referencing/Sharing asset information from webpack to Rails is the problem being solved in this post. We want to use the webpack assets that are exposed in our Rails app. Not setting up a new plugin for uploading the generated assets would be a plus.

Our team used webpack to compile javascript and related assets. (If you are trying to learn webpack, note that it has a very steep learning curve). Once you get the hang of it, you should be able to integrate/debug any other webpack plugin easily. I followed survivejs book.

After moving our Angular project including CSS, images and fixing compatibility issues. We had assets in Rails which we cannot move because they’d come from other gems. The application uses asset_sync to sync data to CDN. The final step was to use webpack assets in the existing rails app. For this we need to have a link point. The asset-manifest generated by sprockets has become our sync point.

We have a webpack-manifests file, rails manifest file. We need merge the webpack asset list into the Rails one so that we can do two things.

  1. use webpack assets in Rails
  2. upload webpack assets into the same storage service which is behind the CDN.

Here’s the code to get it done

Details

The rake task merges webpack asset files with rails before the asset:syncstep and after the assets:precompile step. We have enhanced the rake task to achieve this (last few lines). sprockets gem does not currently allow to add any custom files into the manifest. I raised another PR for allowing users customize the manifest file in sprockets.

The .manifest-random-string.json file is a hash. The two keys are files and assets. asset_sync gem uses the assets key which has file => file-fingerprint and uploads it. Uploads work great, but has a problem.

asset_sync chooses to upload only rails fingerprinted files as ‘Cacheable’ in S3 (or other fog-storage adapters). I have made a code change to allow this to be customised. Now you can pass in cache_asset_regexps to match the ones generated by webpack will solve the problem with ‘Cacheable’.

The regular expression usually matches these for webpack and rails

webpack assets -> vendor.[0-9a-f}{8}.js
rails_assets -> application.[0-9a-f]{32}.js 

You can identify if this is working by checking the headers from the CDN for webpack specific files. Browser should not be re-requesting them on every browser reload.


Identifying Bad Tests

Look at this code Rails FactoryGirl definition for a regular user class

FactoryGirl.define do
  factory :user do
    first_name "John"
    last_name  "Doe"
    subscription_end_date Date.parse("2019-01-01")
  end
end

Does not look harmful. This is a base model. When your other spec files start using it, everything will work fine when you evaluate the subscription_end_date with other fields, but, exactly after 2019-01-01, on 2nd Jan 2019, the tests dependent on this value will start failing.

Anything that is hard coded when tested with relative time like, before/after (based on when the test is running) will start failing. Either use all relative dates or all absolute dates. Time.zone.now is relative unless you perform a timecop.freeze before hand.

Happy hacking!


How to Track User Agreement Acceptance in Database

Problem Statement

How would you model to check if a user has accepted your “Terms and Conditions” or “COPPA” when building your website? Lets talk how would you track in your data store (MySQL or Postgres or Mongo DB).

The front-end is represented by a checkbox or click of a button ‘I Accept’, this translates to either a true or false and gets stored into the a column in the users table in your database which is a boolean field.

The Implicit Requirement

Sounds fair. One thing you may have missed is that all websites like Microsoft, Github, Google, Dropbox tend to update their Policies. They usually send out an email and give the users time to review and accept theT&C which is usually applicable from a particular date. Now this is a requirement that is generally missed in a fine print since the stories create miss this for various reasons.

Respecting the Schema

If your Product Owner mentions this has to be done, you have updated your T&C, you want to be a good person and want to notify your users on the next sign on or not continue using the services or use older code base or mechanisms so that there is no service interruption. If you had a boolean field, this is one may tend to do.

  1. Run a cron to have everyone ‘UnAccept’ the T&C and update the new ’T&C’ data and try to log out users
  2. If you have a notifications module, you send a notification to the customers
  3. If you have a banner module, you may add it to the banner where you usually mention announcements or outage information

Banners and Notifications should be presented to only those who did not accept. but its hard to track this information. Since once you updated the column information, you lost precious information about the user. Also,

  1. We don’t want to show the banners for those who have accepted.
  2. Some people may have accepted from their Profile page
  3. You need to group users by who have not accepted the T&C versions (when you tend to modify them often and may have different settings for them instead of stopping to service them)

Elegant Schema Solution

The solution from this set of requirements would be to modify the boolean column in the database to use a CHAR or an integer (positive number) column with a NULLable or with a default value like 19700101 as the original date or a sequence of generated version numbers.

Every time a new T&C is updated, your code has the ability to compare the current version of the T&C with the one user has last read and accepted.

Other Advantages

This is good for analytical purposes as well and would allow user to accept T&C before hand itself instead of waiting till the switch is flipped after a deployment.

When you think from the User Experience point of view, it would be a blocking experience for the user to navigate and read the T&C and understand or discuss and accept. Notifying the user early and having the user accept early on would help them not worry on the day of your deployment.

Also, this approach helps your content/legal team deal with new T&C without developer intervention and unrelated to deployments.

One thing to note is that the T&C versions generated every time are an incremental sequence so that you can compare them.

This is a general advice for the type of data which should be accepted but the master reference can change over time.

Let me know what you think.


Open Source Contribution to Spree/Solidus

This was one of the open source contribution in 2015 to a major Rails project used by e-commerce companies.

It usually takes hours/days to understand the basic framework, goals, motivations, features vs limitations etc., to start contributing to an open source project based on the amount of domain knowledge (especially for matured projects).

Its usually hard to find and understand bugs in a very well mature framework or language. One of the good things about using open source libraries is you get to know both the code and get to know the eco system while getting paid on the job.

The Bug

I found a bug in the admin section of Spree. The images when re-ordered are not maintaining their position. I identified this issue while trying to work on a similar feature to order categories.

I made a fix locally to verify it has solved the issue and later fixed it

tl;dr

acts_as_list index starts from 1
Javascript is sending indices starting from 0.

acts_as_list has top_of_list defaulted as 1 - Source: https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L38

Steps to reproduce the Issue:

1. Go to images under product.
2. Take any element after the 1st one to the top.
3. Edit the one at the top (that was just dragged)
4. Coming back to the index page shows it in the 2nd position.

What is happening in the frontend?

1. Javascript sends the indices starting 0.
2. The update_positions method in resource_controller just sets the positions as they were sent (with more stuff going on)
3. When element in the 0th position is modified. acts_as_list figures 1 is the top of the list and sets the value to 1 along with setting anything which is already in position 1 to 0.

The FIX

This fix will not modify existing positions, but will only fix those resources for new updates.

Other Solutions

Another solution could be to set the configuration for all models which use acts_as_list to top_of_list: 0 which could be an alternative solution which fixes this issue from re-occurring in the first place.

The Code Change

  • Though the code change was in Javascript, it was sending the data to the ruby backend which was using acts_as_list which uses to store the data using ActiveRecord based on indexes.
  • Link to commit on Github