Monthly Archives: September 2018

Use your gettext translations in your React components

Your ads will be inserted here by

Easy Plugin for AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

Use your gettext translations in your React components

Photo remix available thanks to the courtesy of miguelb. CC BY 2.0

In one of our projects, we are using gettext for i18n. We were putting Handlebars x-handlebars-template templates directly in Haml templates to provide translated views for frontend part – all translations were made on backend. Recently we have rewritten our frontend to React and decided not to use ruby for translations anymore.

Transitioning from backend to frontend

During rewrite, we created an simple API endpoint on backend that was returning translation for given key and locale and mixed it with React component that was asynchronously getting translations. The code was pretty simple and was using jQuery promises:

React = require('react') {span} = React.DOM  cache = {}  lookupCache = (key, locale) ->   cache[locale] ||= {}   cache[locale][key]  updateCache = (key, locale, translation) ->   cache[locale] ||= {}   cache[locale][key] = translation  translate = (key, locale) ->   if translation = lookupCache(key, locale)     new $.Deferred()       .resolve(translation)       .promise()   else     $.ajax       url: '/api/gettext'       data:         key: key         locale: locale       dataType: 'JSON'       type: 'GET'     .then (response) ->       updateCache(key, locale, response.translation)     .fail ->       key  Translation = React.createClass   displayName: 'translation'    getInitialState: ->     translation: null    componentDidMount: ->     translate(@props.key, @props.locale)       .always (translation) =>         if @isMounted()           @setState(translation: translation)    render: ->     if @state.translation       span(null, @state.translation)     else       span(null, '...')  module.exports = (key, locale) ->   React.createElement(Translation, key: key, locale: locale) 

i18next – move your gettext to frontend

This approach was good for quick start but did not scale – it required multiple ajax calls to backend on each page render, so we decided to find something better. After some research we have chosen i18next – full-featured i18n JS library that have pretty good compatibility with gettext (including pluralization rules). With i18next you can easily return translations using almost the same API as in gettext:

# {"key": "translation"} i18n.t('key') # => translation for key 

This library also supports variables inside translation keys:

# {"key with __variable__": "translation with __variable__"} i18n.t('key with __variable__', {variable: 'value'}) # => translation with value 

It has also sprintf support:

# {"Some text with string %s and number %d": "Hello %s! You're number %d!"} i18n.t('Some text with string %s and number %d', {postProcess: 'sprintf', sprintf: ['world', 1]}) # => Hello world! You're number 1! 

And supports plurar forms (even for languages with multiple plural forms):

# {"key": "__count__ banana", "key_plural": "__count__ bananas"} i18n.t("key", {count: 0}) # => 0 bananas i18n.t("key", {count: 1}) # => 1 banana i18n.t("key", {count: 5}) # => 5 bananas 

There are much more configuration options and features in i18next library so you’d better look at their docs.

Your ads will be inserted here by

Easy Plugin for AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

To convert our gettext .po files to json format readable by i18next, we’re using i18next-conv tool and store generated json in public/locale directory of our Rails app. Here’s a simple script we’re using during deploy to compile JS translations (script/compile_js_i18n):

#!/bin/bash npm install . for locale in de en fr pl; do     for file in acme.po support.po tags.po; do         ./node_modules/.bin/i18next-conv -l $locale -s locale/$locale/$file -t public/locale/$locale/${file/.po/.json}     done done 

To use it, just run script/compile_js_i18n in your app’s root directory (but make sure you have node & npm installed and "i18next-conv": "~> 0.1.4" line in your package.json file before). What’s great about i18next-conv, it has built-in plural forms for many languages.

i18next has also a bunch of initialization options. Here’s our setup that works in our app:

i18n.init   ns:      defaultNs: 'acme'     namespaces: ['acme', 'support', 'tags']   lngWhiteList: ['de', 'en', 'fr', 'pl']   fallbackLng: 'en'   resGetPath: '/locale/%{lng}/%{ns}.json'   interpolationPrefix: '%{'   interpolationSuffix: '}'   keyseparator: '<'   nsseparator: '>' 

Some of those initialization options need more explanation. First, we’re using variable interpolation in our gettext translations. They have format different than i18next defaults (%{variable_name} instead of __variable_name__) so we had to set interpolationPrefix and interpolationSuffix. Second, since we’re using english translations as gettext msgids (usually full sentences), we need to change key and namespace separator (keyseparator and nsseparator options). The default key separator in i18next is a dot (.) and namespace separator is a colon (:) and that was making most of our translations useless, since they were not translated at all when translation key contained . or :. We also had to change resGetPath since we decided to store our json in public/locale (e.g. public/locale/en/acme.json for acme namespace). In our app, we wrapped initialization code in i18n CommonJS module for easier use:

i18n = require('i18next') i18n.init(   ns:      defaultNs: 'acme'     namespaces: ['acme', 'support', 'tags']   lngWhiteList: ['de', 'en', 'fr', 'pl']   fallbackLng: 'en'   resGetPath: '/locale/%{lng}/%{ns}.json'   interpolationPrefix: '%{'   interpolationSuffix: '}'   keyseparator: '<'   nsseparator: '>' })  module.exports = i18n 

With this helper, you don’t need to initialize library each time you use it, it would be initialized only once, on first use.

By default, i18next retrieves translations asynchronously, using ajax get requests to the endpoint set in resGetPath when you set locale using i18n.setLng method. setLng method accepts locale as first parameter and optional callback that would be fired after loading translations. You can make use of it in your’s app bootstrap code:

React = require('react') i18n = require('i18n') Gui = React.createFactory(require('gui'))  class App   constructor: (locale, node) ->     i18n.setLng locale, =>       # ...       React.render(Gui(), node)       # ...  # ...  new App(window.locale, document.body) 

Having this setup we can just use regular i18next API in our React components:

i18n = require('i18n')  module.exports = React.createClass   displayName: 'Foo'    render: ->     React.DOM.span(null, i18n.t('Hello world!'))  

i18next has much more features and integrations, including localStorage caching, jQuery integration and ruby gem that can automatically rebuild your javascript translations from YAML files. Have a look at their docs for further information.

Blogging – start from the middle

Your ads will be inserted here by

Easy Plugin for AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

Time constraints seems to be the main reason for not blogging more often.

Today, I’d like to teach you a technique which reverses the situation – the blog post is created in your available time.

Start from the middle

I call this technique – “start from the middle”. The point is to start your post with the main message. Find the MVP of your blog post.

What’s the minimal content to write and still get the message through?

What’s the minimal thing you need to write to help your readers in the chosen topic?

Sometimes, it may be just 3-4 sentences. Sometimes it’s a picture with a short explanation. It can also be a piece of code. It can be just a list of points.

Shippable

The point is to make it consistent and shippable. If I run out of time, I want to ship (publish) the post and still deliver value. It doesn’t need to be perfect. It probably won’t be.

This approach is not about being perfect. If you’re a perfectionists, you may have hard time applying it in practice.

Does it sound similar to what you’ve done before? The agile approach to coding is very similar. Release early, release often. Start with something minimal which works and then iteratively improve it.

Timeboxing

The start from the middle technique works well in combination with timeboxing. Timeboxing is reversing the situation with lacking time for blogging. You start with the constraint – you only have N minutes to ship this blog post. You no longer have the problem of lacking time. You now have a different challenge – choosing the scope of the blog post.

That’s often what product owners, PMs and clients need to do when it comes to giving work to developers. They need to scope the feature so that it’s not too expensive cost-wise and time-wise. As a developer you’re usually presented with the scope and your job is to make it under the available time.

In case of blogging this means that you’re playing both roles. You are the product owner. You are the developer. Deal with it 🙂

What’s a good time constraint?

Depends on your availability. I experiment with 30 minutes, but I may have it easier, after writing > 100 blog posts. 60 minutes may be a good start.

As for perfectionism – this may be hard to some of you. Your blog post won’t be perfect. It’s similar to the features you sometimes ship. Product owners need to make such decisions – what to omit.

It’s your role now – decide what is the thing that you’re going to explain in the blog post as first. By this decision, you’re also deciding what’s not included – thanks to the time constraints.

Keeping it shippable is the key here. Even if you timebox the writing to 120 minutes, try to keep it shippable from the beginning. If you’re using a tool like nanoc (as I’m doing it now), then the post is in the repository. Commit often.

TODO

Keep your TODO list for the blog post. Prioritise the TODO items. Start from the top. If you have more time, just keep going with the tasks. What can be a blogging task? For this blog post, I wasn’t sure if I cover all the points, so I split them into points:

  • start from the middle
  • shippable
  • timeboxing
  • TODO
  • editing, typos, headers, bolds
  • screenshots of a blog post in progress
  • blog post header picture

As you see, I’ve managed to do almost all of them. I didn’t finish the last ones – dealing with screenshots and pictures takes more minutes and I put it at the end – no time available for that.

BTW, It’s an interesting situation. Now, when I present you the list of points, you see that this post is unfinished. What if I didn’t present the list to you? The thing is, as authors we know more than our readers. It’s only our mind that makes us think it’s unfinished – because we started with some bigger vision. Our readers usually don’t know the vision.

How to get anything done? – 4 tips

If you have low self-esteem and constantly worry (like I do), your own thoughts might be the biggest obstacle preventing you from getting great things done. Here are 4 mental tips that I use to overcome my objections.

Start enough times

Don’t think about finishing a project. About the end of it. You will get there. Just start working. Because this is not how things get done most of the time:

How to get anything done? - 4 tips

This is:

How to get anything done? - 4 tips

If you start enough times, and continue making progress every time, you will eventually finish the task. So notice how you perceive time . and just start working on the next task. Without the promise or expectation that you will finish anything in current session.

Accept negative feelings and start working

Awful lot of people get stressed about their work before it even begins. Often in the mornings, during commuting, before you even open the door of your office. If you are like me, you might even get stressed the evening of one day before (especially on Sundays).

Do i have the skills to do this task? Will I have to use the library of framework I am not yet fully comfortable with? How long will it take? Will I end up writing code that I am not fond of? Maybe even ashamed? Will my coworkers like it? Will it work? Will it deploy without any problems?

When we worry we tend to avoid the job and procrastinate. We don’t want to be confrontend with the fact that it might not work, that are we not as good as we think we are. We don’t want our ego to get hurt.

Don’t run away from those feelings. They are real. Try to accept them.

Yes, I worry. Yes, I am not sure if I can do it. Even less sure if the end effect will be pretty and likeable. Accept it, confirm it, don’t deny it. But, start working. With the negative feelings, alongside with them. The only thing that makes them fade away is progress.

Love the grind

aka stack the bricks. Your success or failure don’t depend on one big thing. Your success or failure is compound of thousands (if not millions) of micro-failures and micro-successes.

It doesn’t matter what your job is. Even if you are working at your dream company (what is it nowdays? Tesla, Apple, Google, Airbnb …?) your job will still consist of some amount of repetitive tasks. Can you learn to love it? Could it be enjoyable like in games ?

Or could you do them anyway, despite your feelings? Because you care. You feed your animals even when you are tired and exhausted. You make sure that your kids are safe and loved even at the end of a very long day. Because you care.

If you are working on things that matter to you, that you care about, you can learn to love the grind. Why are you doing this blogpost, this side-project? Do you remember? Is that important to you?

Detach yourself from results

More than one year ago I wrote a rails related blogpost that is still visited by more than 4K developers every month. That’s a lot in my world. It set the bar very high for me. I am still trying to beat it.

But when I worry about pageviews, likes and upvotes on reddit and hackernews, it’s very hard to ship anything. So I lowered my expectations. I write a blog post to help a single person. If I helped one person, then I am good. It was worth writing, worth spending time, worth the effort.

Sure, from time to time you write a post or create a product that helps hundreds or thousands. But you might also get 4 upvotes, dozen of visits and that’s it.

Detach from results doesn’t mean to ignore the data. You can use it to get better, to improve (especially product). But your code is not you, and your upvotes are not you. That might sound obvious. But I still need to remind myself about it.

You might even start to believe that you are not helping anyone according to some random internet comments. But then one year after a not-much-noticed blogpost, someone writes you an email to say thank you and ask a very good question regarding the topic.

Remember, most of posts that were helpful to your readers won’t get a retweet, comment or upvote. I don’t jump quickly myself into saying thank you for every blogger that helped me today finish my task. Perfectly normal. But the appreciation is there.

Now

Few weeks ago in a shopping-center I got a wristband from anti-cancer group for filling out a survey. It says best time for action is now.

How to get anything done? - 4 tips

I don’t want to wait till cancer to get myself into doing something meaningful. This wristband actually changed something in me for the better. I do more. Now.

That’s it, going back to writing a book.

How to split routes.rb into smaller parts?

How to split routes.rb into smaller parts?

Each application created using Ruby on Rails framework has a routing engine and config/routes.rb file where we define routes paths. That file very often becomes very large in the proces of development. Each additional line makes the routing file harder to maintain. Also, searching for specific paths during the development phase becomes increasingly difficult. Currently, I work on an application in which the routing file contains about 500LOC. Quite a lot, isn’t it? The solution is very simple. All you need to do is split the file into a couple of smaller ones.

Order of loading files

When a request comes, routes.rb file is processed in “top to bottom” order. When the first suitable entry is found, the request is forwarded to the appropriate controller. In case of not finding a matching path in the file, Rails will serve 404. Because of the possibility of sorting the order of loading files, we can define the priorities for our namespaces.

Solution

The following example is a short part of routes.rb:

ActionController::Routing::Routes.draw do   root to: "home#index"   get "/about   get "/login" => "application#login"     namespace :api do     #nested resources   end    namespace :admin do     #nested resources   end    namespace :messages do     #nested resources   end    namespace :orders do     #nested resources   end end 

There are some some default namespace (with /home, /about, /login routes) and four other namespaces. These namespaces define nicely existing contexts in our application. So, they are great canditates for division to other files. So we have created api.rb, admin.rb, messages.rb and orders.rb. Usually, I put the separated files in config/routes/ directory which is created for this purpose. Next step is to load above files. We can do this in several ways. In applications based on Rails 3, loading route files from application config is a very popular method . Finally, we have to add to our application.rb following line:

config.paths["config/routes"] += Dir[Rails.root.join('config/routes/*.rb)] 

If you want to have control over the order of loading files you can do this this way:

config.paths["config/routes"] = %w(       config/routes/messages.rb       config/routes/orders.rb       config/routes/admin.rb       config/routes/api.rb       config/routes.rb     ).map { |relative_path| Rails.root.join(relative_path) } 

However, since version 4 of Ruby on Rails if you attempt to add the above line application will throw an exception. The Rails 4 does not provide [‘config/routes’] key in Rails::Engine. There is another option that works in both versions of the framework. Here we have another solution:

YourApplication::Application.routes.draw do  def draw(routes_name)     instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb"))) end    draw :messages   draw :orders   draw :api   draw :admin    root to: "home#index"   get "/about   get "/login" => "application#login"   end 

It allows us to add a new method to ActionDispatch::Routing module which helps as to load paths. Rails 4 initially had a similar solution but it has been removed. You can check out the git commit here: https://github.com/rails/rails/commit/5e7d6bba79393de0279917f93b82f3b7b176f4b5

Conclusion

Splitting files is a very simple solution for improving the condition of our routes files and developers lives. In this regard, this solution makes life easier in this.

The reasons why programmers don’t blog

I was wondering why only few programmers actively blog, so I asked for reasons on Twitter. Here are the responses.

If you’re a programmer, could you reply to me – why are you not blogging? If you are, then please reply, why are you not blogging more?

— Andrzej Krzywda (@andrzejkrzywda) February 25, 2015

Click here to see all the responses as they appeared in original.

Here’s a short summary of the reasons:

  • Time
  • “What I do is not interesting to others”
  • Perfectionism
  • Coding is better than writing
  • I’m not doing anything innovative
  • “I don’t do side projects”
  • “I don’t know what to write about”
  • shame
  • “what I want to write about is too obvious”
  • writing is harder than coding
  • everyone knows that already
  • longer feedback loop, as compared to coding

Is there any other reason you’d like to add?

It’s worth noting that this survey may not be a good sample – those are from programmers who don’t blog, but they do tweet (which on its own is quite interesting).

Do you think the reasons can be addressed in any way? Does it make sense for programmers to blog more? I’ll leave you with those questions here 🙂

My favorite ActiveSupport features

My favorite ActiveSupport features

This is short and not so comprehensie list of my favorite ActiveSupport features.

Array#second

%i(a b).second # => :b 

Useful in ad-hoc scripts when you get primitive data from file or API. Together with map they give you some_array.map(&:second) to get what you want.

Array#extract_options

def options(*args)   args.extract_options! end  options(1, 2)        # => {} options(1, 2, a: :b) # => {:a=>:b} 

But with ruby 2.0 keyword arguments aka kwargs already present and ruby 1.9.3 support ending in February 2015 you should probably migrate to it:

def options(*args, **kwargs)   args   kwargs end  options(1, 2)        # => {} options(1, 2, a: :b) # => {:a=>:b} 

Array#in_groups_of and Array#in_groups

%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group} #   ["1", "2", "3", "4"] #   ["5", "6", "7", nil] #   ["8", "9", "10", nil]  %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}   #   ["1", "2", "3"]   #   ["4", "5", "6"]   #   ["7", "8", "9"]   #   ["10", nil, nil] 

Remember that you can add false as a second argument to avoid nils in the arrays.

Array#wrap

Wraps its argument in an array unless it is already an array.

def method(one_or_many)   Array.wrap(one_or_many).each(&:do_something) end  method(1) method([3,4,5]) 

Nicely explained in the documentation why it is better then usual ruby idioms

to_formatted_s on many types

BigDecimal.new("12.23").to_s #=> "0.1223E2"  require 'active_support/all'  BigDecimal.new("12.23").to_s #=> "12.23" BigDecimal.new("12.23").to_formatted_s #=> "12.23"  Time::DATE_FORMATS[:w3c] = "%Y-%m-%dT%H:%M:%S%:z" Time.now.to_s(:w3c) #=> "2015-02-25T17:51:53+00:00" 

ActiveSupport overwrites to_s on my types to use its to_formatted_s version instead (especially when arguments provided)

#ago, #from_now

 Time.now.ago(3.months) # => 2014-11-25 17:55:53 +0000  3.months.ago # => 2014-11-25 17:56:00 +0000  3.months.ago(Time.now) #=> 2014-11-25 17:56:03 +0000 

beginning_of_... & end_of_...

%i(   beginning_of_minute   beginning_of_hour   beginning_of_day   beginning_of_week   beginning_of_quarter   beginning_of_month   beginning_of_year   end_of_minute   end_of_hour   end_of_day   end_of_week   end_of_month   end_of_quarter   end_of_year ).each{|method| puts "#{method} - #{Time.now.public_send(method)}"}   # beginning_of_minute - 2015-02-25 18:03:00 +0000 # beginning_of_hour - 2015-02-25 18:00:00 +0000 # beginning_of_day - 2015-02-25 00:00:00 +0000 # beginning_of_week - 2015-02-23 00:00:00 +0000 # beginning_of_quarter - 2015-01-01 00:00:00 +0000 # beginning_of_month - 2015-02-01 00:00:00 +0000 # beginning_of_year - 2015-01-01 00:00:00 +0000 # end_of_minute - 2015-02-25 18:03:59 +0000 # end_of_hour - 2015-02-25 18:59:59 +0000 # end_of_day - 2015-02-25 23:59:59 +0000 # end_of_week - 2015-03-01 23:59:59 +0000 # end_of_month - 2015-02-28 23:59:59 +0000 # end_of_quarter - 2015-03-31 23:59:59 +0000 # end_of_year - 2015-12-31 23:59:59 +0000 

ActiveSupport::Duration

The class behind the little trick:

3.seconds.class # => ActiveSupport::Duration 

ActiveSupport::TimeWithZone

I hate time zones, but I love ActiveSupport::TimeWithZone. It is so easy to use.

Time.use_zone("Europe/Moscow"){ Time.zone.now } # => Wed, 25 Feb 2015 21:09:12 MSK +03:00  Time.find_zone!("America/New_York").parse("2015-03-03 12:00:11") # => Tue, 03 Mar 2015 12:00:11 EST -05:00  Time.find_zone!("America/New_York").parse("2015-03-03 12:00:11").utc # => 2015-03-03 17:00:11 UTC  Time.utc("2015-03-03 12:00:11").zone # => "UTC" 

And I love that it can properly compare times from different timezones based on what moment of time they point to.

moment = Time.utc("2015-03-03 12:00:11") #=> 2015-01-01 00:00:00 UTC  moment.in_time_zone("Europe/Warsaw") == moment.in_time_zone("America/Chicago") => true 

Hash#except

Returns a hash that includes everything but the given keys.

hash = { a: true, b: false, c: nil} hash.except(:c) # => { a: true, b: false} 

Except that I always think that this method is called #without.

Hash#slice

Slice a hash to include only the given keys.

 { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b) # => {:a=>1, :b=>2} 

If only I could remember that this method is not named #only 🙂

Hash#reverse_merge

options.reverse_merge(size: 25, velocity: 10) 

is equivalent to

{ size: 25, velocity: 10 }.merge(options) 

This is particularly useful for default values.

Module#delegate

class Foo < ActiveRecord::Base   belongs_to :greeter   delegate :hello, to: :greeter end 

Reading this is way easier for me, compared to Forwardable#def_delegator.

With prefix and allow_nil options that you can use with it, it probably solves 95% of my delegation cases.

Object#blank? and Object#present? and Object#presence

 nil.blank? # => true "  ".blank? #=> true [].blank? => true  title = commment[:title].presence || "Missing title" 

Never check for nil or empty string again.

Enumerable#sum

[2,3,5].sum # => 10 

ActiveSupport::Notifications

Too long for our short blogpost but check out instrumentation API

ActiveSupport::MessageVerifier

You can use it to generate and verify signed messages

@verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: JSON) @verifier.generate("private message") #=> "InByaXZhdGUgbWVzc2FnZSI=--43fc83190b28daf8df04c0b86ff2976931a6dcd2" @verifier.verify("InByaXZhdGUgbWVzc2FnZSI=--43fc83190b28daf8df04c0b86ff2976931a6dcd2") #=> "private message"  @verifier.generate("a" => "private message") #=> "eyJhIjoicHJpdmF0ZSBtZXNzYWdlIn0=--b253af3e77622f743cf6804c870f4a95cbbd6f00" @verifier.verify("eyJhIjoicHJpdmF0ZSBtZXNzYWdlIn0=--b253af3e77622f743cf6804c870f4a95cbbd6f00") => {"a"=>"private message"} 

Summary

That’s it. You can browse entire ActiveSupport codebase quickly and easily at github

If you liked it, you may also enjoy Hidden features of Ruby you may not know about

Looking for an easy solution to build simple dynamic pages?

The Beginners Guide to jQuery.Deferred and Promises for Ruby programmers

Working with asynchronous code is the bread and butter for frontend developers. You can find it hard when working with it at first – as a Rails developer, you live with code which is usually synchronous. Luckily, promises were something which allowed us to get a huge step forward to deal with callbacks in a sane way. While widely adopted by backend JavaScript developers, Promises and their tooling is something often missed by Rails devs.

It can be different – and you have all important libraries bundled by default in our Rails app. Let’s see what jQuery can offer you here – and I’ll discuss how I managed to solve typical async problem with features that jQuery provides.

Problem (in the domain way)

I’m working on fairly big project which have frontend composed of microapps. Such microapp is providing services to build our frontend – it’s often a package of React components to build a view, commands which you invoke to perform an action on backend, and the storage – a class which encapsulates our data and reacts if this data is changed (it is similar to Store in Flux terminology). The whole thing is ‘glued’ together in a dispatcher – you may think of it as a telephone central when we ‘route’ events from our pieces to another piece.

In our app we are making some kind of surveys (called assessments) to rate “assets” in a certain way. There are two roles in the process – an user which is the owner of a given asset (it’s often a piece of hardware). Owner can go to a view and perform as many surveys as he want to provide rating of his owned assets. An analyst defines what assets are and who owns them – he also defines criteria of surveys.

Everything was working fine without much coordination before a request from my client came. Some surveys are way more important than the other – just because some assets are more ‘critical’ now than the others. He asked me to introduce feature of assigning things – it’s basically just a reminder via an e-mail to do this particular survey, chosen by an analyst. To implement such thing I’d need a direct link to a survey which I can include in an e-mail. After selecting this link a modal with a survey should pop and the asset owner can start working on this survey without hassle. After closing this survey he also needs to have an overview of his owned assets, opened at exact place where rating change occurs.

Problem (in the code)

This feature is dependent on three apps – Assets which provides an overview of assets available for the owner, Surveys which is responsible for surveys modal and Rating which is an app for storing rating we surveyed before. We can complete a survey exactly once – once a rating is set, it cannot be changed. That’s why we need to ask whether we need to complete a survey or not – clicking on a link second time should present an user with a message that this rating is already set.

These apps works completely asynchronously – each starts, fetches its data, sets dispatcher and waits. When an asset owner roams through components of Assets app to find a survey he like to make, timing issues does not occur. But in this particular case we want to present survey modal as soon as possible – timing is critical to make user’s experience fine. I decided to put the code of this new feature in the Surveys app – in our case it must wait for data from Rating (to ensure the survey is still valid), Assets (we need to open tabs in the view on the certain asset, so we need to have this data loaded first) and its own data (fetched from backend), and THEN open our modal. How it can be achieved? Apparently, jQuery comes with an elegant and concise solution.

Solution, part #1: Surveys data

This is an easy one. Our storage objects have a sync method, which returns a Promise. What is a promise?

Promise is an object which is returned when there is a process waiting for its completion. For example, $.ajax method returns a Promise. Promises can be rejected or resolved – and you can register callbacks to each case using success and fail methods of each Promise object.

Here’s an example:

 # ajaxResponse is a Promise ajaxResponse = $.ajax(url: '/surveys.json', type: 'GET')  # Promise is resolved (we got data!) ajaxResponse.success (returnedData) =>   console.log("returned data: ", returnedData)  # Promise is rejected (HTTP error, wrong data format returned etc.) ajaxResponse.fail =>   console.log("ERROR!")  ajaxResponse.fail =>   console.log("THIS CALLBACK WILL BE CALLED TOO! (as the second one)")  # as a bonus: ajaxResponse.always =>   console.log "I'll always be called, no matter promise is resolved or rejected!" 

You can register as many callbacks to Promises as you want. What’s more, even if you register a success callback after the Promise is resolved it’ll be fired immediately. The same goes for rejecting and fail callbacks.

We can’t resolve and reject Promises by ourselves. We can only register callbacks to it. It makes sense for a process like an AJAX request – jQuery handles this stuff for us and we’re only interested in getting our hands on data (or not, if error occurs). There must be a way to control this process – but I’ll write about it later.

So, our final solution looks like this:

 @storage.sync().success =>   # proceed with code... 

So far so good. Now we need to take care of the more complex thing – waiting for dependent microapps to be ready.

Solution, part II: Assets and Rating apps readiness

In architecture I have in my project applications communicate only through events – there is an @eventBus object which has @publish(eventName, data...) and @on(eventName, callback) methods to publish and listen to events. Since I started in Surveys app I had an direct access to storage object which is synced – so I had a nice Promise to register on – in this case I can only listen to an event. So I’ve introduced two new events – assetsStarted and ratingStarted which are published when those applications are ready for interactions.

That is not helpful, though. I could’ve made something like this:

 @assetsReady  = false @ratingReady  = false @surveysReady = false   @storage.sync().success =>   @surveysReady = true   @checkIfCanProceed()  @eventBus.on('assetsStarted', => @assetsReady = true; @checkIfCanProceed()) @eventBus.on('ratingStarted', => @ratingReady = true; @checkIfCanProceed())  checkIfCanProceed: ->   @proceed() if @assetsReady and @ratingReady and @surveysReady  

There is a lot of imperativeness here. And I repeat myself three times here – I consider this solution a hack. But what can I do to improve this code?

What’s less known, jQuery provides us a way to turn any process into a Promise. There are also tools which allows us to work with many promises. I’ll use this approach to implement this code in a cleaner way.

We can turn our waiting for start events to a promise, using jQuery.Deferred:

 assetsAppPromise = new jQuery.Deferred((deferred) =>                      @eventBus.on('assetsStarted', deferred.resolve)                      # Error handling: Timeout? Just call deferred.reject()                    ).promise()  ratingAppPromise = new jQuery.Deferred((deferred) =>                      @eventBus.on('ratingStarted', deferred.resolve)                      # Same.                    ).promise() 

jQuery.Deferred is the side that jQuery have when managing our $.ajax calls – it’s a Promise with resolve and reject methods available. This way we can manually reject or resolve our Assets and Rating promises. promise method returns a real Promise from this object – without an ability to resolve and reject Promise. This is what we pass to our listeners.

You can pass a function to jQuery.Deferred constructor – it will be applied to the deferred object itself. The same code could be written as:

 assetsAppDeferred = new jQuery.Deferred() @eventBus.on('assetsStarted', assetsAppDeferred.resolve) # Error handling: Timeout? Just call assetsAppDeferred.reject() assetsAppPromise = assetsAppDeferred.promise()  ratingAppDeferred = new jQuery.Deferred() @eventBus.on('ratingStarted', ratingAppDeferred.resolve) # Same. ratingAppPromise = ratingAppDeferred.promise() 

Any arguments passed to resolve and reject methods of Deferred object will be passed to all success or fail callbacks, respectively.

deferred = new jQuery.Deferred() deferred.promise().success (a, b, c) =>   console.log a, b, c  deferred.resolve(1, 2, 3) # Console output: 1 2 3 

Deferred objects can be rejected or resolved only once – there are methods for making more ‘grained’ notifications from Promises (registering callbacks using progress and triggering those callbacks using Deferred‘s notify) but it’s beyond scope of this post.

Ok, so we got promises for our apps already. What we can do now? There is set of tools from jQuery available to work with Promises – pipe, then, when… they are all returning another Promises, transformed in a way. The idea is like with Enumerable collections in Ruby – you transform collections using Enumerable methods to achieve different Enumerables or the result.

In our case we’ll use jQuery.when method. It takes a set of Promises and returns a Promise which is resolved if and only if all passed Promises are resolved. If any of promises passed rejects, the whole when rejects. It resolves with data collected from contained promises.

deferred1 = new jQuery.Deferred() deferred2 = new jQuery.Deferred()  deferred1.resolve('a', 'b') jQuery.when(deferred1.promise(), deferred2.promise()).success (data1, data2) =>   console.log("data1 = ", data1)   console.log("data2 = ", data2) deferred2.resolve('c', 'd')  # Console output: # data1 = ['a', 'b'] # data2 = ['c', 'd'] 

Apparently it is exactly what we’re looking for!

assetsAppPromise = new jQuery.Deferred((deferred) =>                      @eventBus.on('assetsStarted', deferred.resolve)                      # Error handling: Timeout? Just call deferred.reject()                    ).promise()  ratingAppPromise = new jQuery.Deferred((deferred) =>                      @eventBus.on('ratingStarted', deferred.resolve)                      # Same.                    ).promise()  jQuery.when(assetsAppPromise, ratingAppPromise, @storage.sync()).success => @proceed() 

That’s it!

Conclusion

Promises are one of the most effective and elegant ways to deal with asynchronous code in JavaScript. They were introduced to deal with anti-pattern called “Callback hell” – 3-4+ levels of nested callbacks. jQuery already provides us quite powerful implementation of Promises out of the box. You can use it to greatly improve your frontend code! In Node.js promises is widely adopted tool to deal with 'async everything’ approach in their backend code.

Read more

  • CommonJS Promises/A – Promises specification introduced by CommonJS. Also links to many great tools which introduces or allows to work with Promises both on backend and frontend.
  • jQuery Deferred Documentation
  • RxJS – Reactive programming has much to do with Promises – it’s a “step forward” to generalize this neat tool.

Adding videos embedded in a page to a sitemap

Adding videos embedded in a page to a sitemap

One of our customer has a solution that allows them to quickly create landing pages that are then used for SEM. Of course such pages are listed in the sitemap of the application domain. The lates addition to that combo was to list the videos embedded on the landing pages in the sitemap. It sounded hard, but turned out to be quite easy.

Our landing pages contain html that is saved with content editor. It is just html. The videos are embeded in a normal way recommended by the providers such as:

<iframe width="560" height="315" src="https://www.youtube.com/embed/BBnN5VLuxKw" frameborder="0" allowfullscreen></iframe> <iframe src="//player.vimeo.com/video/2854412" width="500" height="311" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> 

Get the list of embedded videos from the html of landing page

Nokogiri for the rescue 🙂

 html = LandingPage.first.content  Nokogiri::HTML(html).xpath("//iframe").map do |iframe|   extract_metadata( iframe[:src] ) end.compact 

More about:

Get the metadata of videos based on their url

VideoInfo to the rescue 🙂

 VideoMetadata = Struct.new(   :title,   :description,   :thumbnail_location,   :player_location,   :duration_in_seconds,   :publication_date, )  def extract_metadata(url)   player_location = url   player_location = "http:#{url}" if URI(url).scheme.nil?    vi = VideoInfo.new(url)   VideoMetadata.new(     vi.title,     vi.description,     vi.thumbnail_large,     player_location,     vi.duration,     vi.date   ) rescue VideoInfo::UrlError, *NetHttpTimeoutErrors.all   return nil end 

If an iframe is not for a recognizable video then VideoInfo will raise an exception that we catch. If there is networking problem we gracefuly handle it as well.

Use metadata in the sitemap

SitemapGenerator to the rescue.

SitemapGenerator::Sitemap.create do   LandingPage.find_each do |landing_page|     videos = extracted(landing_page.content).map do |video_metadata|       {         title:            video_metadata.title,         description:      video_metadata.description,         thumbnail_loc:    video_metadata.thumbnail_location,         player_loc:       video_metadata.player_location,         duration:         video_metadata.duration_in_seconds,         publication_date: video_metadata.publication_date       }     end      add(       landing_page_path(id: landing_page.slug),       lastmod: landing_page.updated_at,       changefreq: 'monthly',       priority: 0.7,       videos: videos     )   end end 

That’s it

These three snippets are the essence of it. There are of course tests, and there is adapter for obtaining video data so that tests don’t connect to the internet.

But it turned out to be way simpler than I expected. Which is always a nice surprise in our industry.

upgrade capybara-webkit to 1.4 and save your time

upgrade capybara-webkit to 1.4 and save your time

I spent quite some time on Monday debugging an interesting issue. Our full stack acceptance tests stopped working on CI. Just CI. Everything was passing locally just fine for every developer. So I had to dig deeper.

After initial investigation it turned out that tests which were timing out on CI with 3 minutes limit of inactivity were passing given enough time (around 15 minutes). I used SSH to log into Circle CI instance and tried executing them myself to see that. So…

Suddenly, one day, subset of our tests become really slow. How would that happen?

I was stuck trying to figure out the reason when my coworker suggested that it might be related to problems with javascript files. At the same time I was in contact with Marc O'Morain from Circle who suggested it might be related to Mixpanel because other customers who used Mixpanel experienced problems as well.

So I disabled Mixpanel Javascript, tested it out and everything was working correctly. We were already using capybara-webkit version 1.3.1 with blacklisting feature to prevent exactly such kind of problems:

Capybara.register_driver :webkit_with_blacklist do |app|   driver = Capybara::Webkit::Driver.new(app)   driver.browser.url_blacklist = %w(     http://player.vimeo.com     http://maps.googleapis.com     http://google-analytics.com   )   driver end  Capybara.javascript_driver = :webkit_with_blacklist 

However mixpanel tracking was added later compared to this code. So it was never put on the blacklist because we simply forgot. What a shame.

capybara 1.4

But this is where new version of capybara-webkit comes into the story. It has a really nice feature which allows you to disable any external JS by calling

page.driver.block_unknown_urls 

That way you don’t need to remember in the future to blacklist any external dependencies in your project. They make your test much slower and unreliable because of possible networking issues. So blacklisting as much as possible will save you time on executing tests and on debugging such issues as mine.

It turned out that we couldn’t reproduce the problem locally because our developers work from Europe and the mixpanel networking issue occured in US only. Guess where Circle CI node is located 🙂

You can put the blocking snippet of code in before/setup part of your acceptance test, or in spec_helper or in a constructor of class that is using capybara api.

bbq

Because we use bbq gem in our project, for me it was:

class Webui < Bbq::TestUser   def initialize(*)     super     page.driver.block_unknown_urls if page.driver.respond_to?(:block_unknown_urls)   end end 

I added the respond_to? check because rack-test driver don’t have (and don’t need) this feature available.

rspec

If you follow standard way described in Using Capybara with RSpec you can write:

describe "the signin process", js: true do   before do     page.driver.block_unknown_urls   end    it "signs me in" do     visit '/sessions/new'     # ...   end end 

or in Capybara DSL:

feature "Signing in" do   background do     page.driver.block_unknown_urls   end    scenario "Signing in with correct credentials", js: true do     visit '/sessions/new'     # ...   end end 

Of course it doesn’t need to be in before/background/setup. It can be used directly in every scenario/it/specify but that way you will have to repeat it multiple times.

You can also configure it globally in spec_helper with:

RSpec.configure do |config|   config.before(:each, js: true) do     page.driver.block_unknown_urls   end end 

verbose

The nice thing about capybara 1.4 is that it is very verbose for the external resources that you haven’t specify allow/disallow policy about.

To block requests to unknown URLs:   page.driver.block_unknown_urls To allow just this URL:   page.driver.allow_url("http://api.mixpanel.com/track") To allow requests to URLs from this host:   page.driver.allow_url("api.mixpanel.com") 

So next time you add new external URL you will notice that you need to do something. Unless of course you went with page.driver.block_unknown_urls which I recommend if your project can work with it. For all other cases there is allow_url.

summary

  • Upgrade to latest capybara-webkit
  • Use page.driver.block_unknown_urls
  • Have more reliable and faster tests that don’t depend on network

Ruby Exceptions Equality

Ruby Exceptions Equality

Few days ago my colleague was stuck for a moment when testing one service object. The service object was responsible for a batch operation. Basically operating on multiple objects and collecting the status of the action. Either it was successful or failed with an exception. We couldn’t get our equality assertion to work even though at first glance everything looked ok. We had to dig deeper.

The problem boils down to exceptions equality in Ruby. And few tests in console showed precisely how it works, later to be confirmed by the documentation.

Let’s see a comparison case by case. But first, exception definition:

  RefundNotAllowed = Class.new(StandardError) 

Does two instances of same exception equal?

RefundNotAllowed.new == RefundNotAllowed.new # => true 

Yes. That was our first test and it behaved according to our intution. So why did our test fail if everything told us that we are comparing identical exceptions.

What about message?

RefundNotAllowed.new("one message") == RefundNotAllowed.new("another") # => false  RefundNotAllowed.new("one message") == RefundNotAllowed.new("one message") # => true 

Ok, so apparently the message must be identical as well. But in our case the message was equal and our exceptions were still non-equal. Bummer. Let’s think about one more aspect of exceptions: backtrace.

What about backtrace?

The backtrace of unthrown exception is…

RefundNotAllowed.new.backtrace  => nil 

Ok, I didn’t excepted that. I imagined that the backtrace is assigned at the moment of exception instantiation. But when you think deeper about it, you might realize that it wouldn’t make sense.

exception = RefundNotAllowed.new raise exception 

Would you like to know that the exception was raised at line 1 or rather line 2 in that example? So obviously backtraces are assigned when exception is actually raised, not merly instantiated.

But do they play any role in exception equality? Let’s see.

def one_method   raise RefundNotAllowed.new rescue => x   return x end  def another_method   raise RefundNotAllowed.new rescue => x   return x end  one_method == one_method          # => true another_method == another_method  # => true  one_method == another_method      # => false  exception_one = one_method exception_two = one_method exception_one == exception_two    # => false 

Apparently for two exceptions to be equal they must have identical backtrace. Even 1 line of difference makes them, well… , different.

What does the doc say?

ruby Exception#== documentation says: If obj is not an Exception, returns false. Otherwise, returns true if exc and obj share same class, messages, and backtrace.

In my original problem it lead me to realization that we were comparing raised-and-catched exception (thus with stacktrace) with a newly instantiated exception. That’s why we couldn’t get to make them equal.

When you use assert_raises(RefundNotAllowed) or expect{}.to raise_error(RefundNotAllowed) these matchers take care of the details for you:

But when you check something like expect(result.first.error).to eq(RefundNotAllowed.new) because your batch process collected them for you, then you are on your own and this might not be good enough and it won’t work. You might wanna just compare manually exception class and message.

What about custom exceptions with additional data?

Because they inherit from Exception (through StandardError) they share identical logic as described in documentation.

class RefundNotAllowed < StandardError   attr_reader :order_id   def initialize(order_id)     super("Refund not allowed")     @order_id = order_id   end end  RefundNotAllowed.new(1) == RefundNotAllowed.new(2) # => true 

If you want something better you need to overwrite == operator yourself.

class RefundNotAllowed < StandardError   attr_reader :order_id   def initialize(order_id)     super("Refund not allowed")     @order_id = order_id   end    def ==(obj)     super(obj) && order_id == obj.order_id   end end  RefundNotAllowed.new(1) == RefundNotAllowed.new(2) # false RefundNotAllowed.new(2) == RefundNotAllowed.new(2) # true 

My opinion

I am personally not convinced about the usability of including backtrace in exception equality logic because in reality one would almost never create two exceptions with the exact same backtrace to compare them. Although maybe for some usecases it is a nice way to determine if repeated attempt failed in exactly same way and for the same reason.

But you can always overwrite == in a way that would not call super and would completely ignore the backtrace, instead comparing only exception class, data, and perhaps message.