So as you can see from the backtrace the error was about order
method:
1
|
|
Yeah, this ArgumentError
was added to the Rails 4.0 and it raises when you’re trying to call order
method with blank arguments. Honestly, I don’t see any reason for adding such “feature” to Rails (you can find an explanation of this behaviour here). Anyway let’s go further.
If you didn’t know Devise uses orm_adapter gem to access different databases with different ORMs. If you look at the backtrace you’ll see this line of code. So we’re calling order
with some params. After some time spent on debugging I figured that order_clause(order)
returns blank string (""
) in our Devise-specific case. But the thing is that order
method accepts a blank string since order
method uses splat operator and our blank string becomes an array with a blank string ([""]
) which returns false for blank?
. So why the hell it doesn’t work?
And then I looked at the lib
app and I saw this (you can find that app here):
1 2 3 4 5 |
|
Yeah, that’s it. Just a simple Ruby core extension to the String class. What bad can happen?
But let’s look closer to the RubySpec of splat operator:
What does it mean? Let’s try splat operator in IRB:
1 2 |
|
and with that extension:
1 2 |
|
See the difference? With this extension it becomes a blank array []
which returns true for blank?
test.
Why? According to the RubySpec
it assigns the returned value of to_a when the splatted object responds to to_a
Since we have method to_a
defined on String object it’s predicted behaviour for the Ruby but not for our app and Rails in particular. And ActiveRecord fails because it knows nothing about this.
So please, be careful with extensions to the Ruby core classes!
]]>I hate timezones. And I believe you hate them too. But it happens that you have to work with them pretty often. And one day I had to detect timezones based on coordinates. You know, you have latitude and longitude and you need to know a timezone of this place. So what did I do? Right, I’ve googled it and found a couple of services that could do this kind of work for me like Geonames, Askgeo, Google, Yahoo Places and so on.
Geonames API is pretty nice so I’ve gave it a shot. It was working quite good but one day it became very slow. I mean VERY slow: I had to wait about 5-10 seconds to get requested timezone. Actually I don’t know why exactly it happened but it was awful. The application that used timezone feature became unusable.
So I decided to make my own service that I could run on my server and have a total control of it. Please welcome, Timezoner:
So how it works? After you set it up just make a request to the server with lat
and lng
params and you get JSON with detected timezone for the given place.
We need PostGIS to make only one query to the database using ST_Contains function. You can read more about how to use ST_Contains
in docs but our case can be illustrated with the following image:
where that point is a coordinates defined with passed lat
and lng
params and that polygon is a polygon that defines appropriate timezone (thank to efele.net for this data)
That’s all. It was pretty easy to replace not very fast (at least for that specific moment) service with our own which works way more faster.
]]>Favor ‘object composition’ over ‘class inheritance’. (Gang of Four 1995:20)
Object composition allows us to create more reusable pieces of code that could be combined together keeping our code simple and understandable.
Let’s take an example of making a new interface over existing object. For example, we need a SimpleQueue class which is based on Array but have more idiomatic interface – enqueuing using enq
method and dequeuing using deq
method.
To achieve it we could just define those methods and call push
and shift
methods inside them:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Method delegation is commonly used so Ruby Stdlib have some sugar which helps us doing delegation on a method by method basis. Using Forwardable we’re able to delegate methods in declarative way:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
So def_delegator
defines enq
method as a call to push
method on @queue
object. The same with deq
method.
There are also some array methods that could be useful for our queue object. We can easily delegate them all at once using def_delegators
:
1
|
|
Here is an example of using a new SimpleQueue class which has a new interface based on the existing one:
1 2 3 4 5 6 |
|
And it’s important that SimpleQueue class has only methods that it has to have and doesn’t have any additional methods inherited from Array.
Forwardable library also includes a SingleForwardable module which might be useful in API client libraries. Consider we created an API client library with the following interface:
1 2 |
|
It would be great to have an option to call API methods on a global client object instead of making a new client object every time we needed to do a request. We could use SingleForwardable to delegate methods to a global client object:
1 2 3 4 5 6 7 8 9 |
|
SingleForwardable defines methods on object which it’s called on. So def_delegators
declaration creates class methods in the example above and makes possible to perform API calls without instantiating an API client object manually:
1 2 |
|
If you were using both Forwardable and SingleForwardable in one class then you would need to call def_instance_delegator
and def_single_delegator
methods.
May objects be with you.
]]>CityHash is a family of non-cryptographic hash functions, designed for fast hashing of strings. It has 32-, 64-, 128-, and 256-bit variants. © Wikipedia
And when Wikipedia says fast it means really fast. Let’s compare CityHash 128-bit variant to MD5 hash function. The whole idea to compare these functions is about my curiosity. As you know Rails’s Asset Pipeline uses MD5 hash function against the content of assets to generate a name for css, js files. And I was thinking what if we would use CityHash for this task?
And I wrote a benchmark. I used cityhash gem - it’s a Ruby wrapper around C++ lib. And my test file was compiled css file from Github. Here is my benchmark:
1 2 3 4 5 6 7 8 9 10 |
|
and there are the results:
1 2 |
|
Yeah, CityHash.hash128
is 16x faster that Digest::MD5.hexdigest
. Pretty impressive, right? And yes, I know that calculationg asset names is not a Rails’s bottleneck. It was just the first real life scenario of using MD5 hash function on big files that came up in my head.
JFYI, I’ve tested CityHash, MD5, MurmurHash3, and xxHash against files of different size. Here is the results:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
So if you need a fast non-cryptographic hash function you definitely should try CityHash.
]]>In computer programming, named parameters or keyword arguments refer to a computer language’s support for function calls that clearly state the name of each parameter within the function call itself. © Wikipedia
Right now (I mean in Ruby <= 1.9.3) you can ‘emulate’ it with passing a hash as an argument to a method:
1 2 3 4 5 |
|
I bet you saw this a lot. But the main problem with this technique is you can’t easily set default value for arguments. For sure you can solve it like this:
1 2 3 4 5 6 |
|
but it doesn’t look like a clean solution. So what will be changed with Ruby 2.0? Let’s look at this CHANGELOG entry:
Implement keyword arguments. The feature is promised to be included in 2.0, but the detail spec is still under discussion; this commit is a springboard for further discussion. Please try it and give us feedback.
Sounds promising! As you can see there is a chance that keyword arguments will be a part of ruby syntax. So how to use it? There is a couple of tests here. Let’s take a brief look at some of them:
1 2 3 4 5 6 7 8 |
|
So as you can see you don’t need to do merge or something to achieve default values and if you pass an argument that doesn’t match arguments from the method’s definition you’ll get ArgumentError
. To make something similar Rails extends Hash
class with assert_valid_keys method.
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
As you can see there is a new thing like **
. It allows you to pass a hash to this method because you can’t write
1 2 3 |
|
It won’t work. You’ll get something like syntax error, unexpected ')', expecting end-of-input
.
1
|
|
So let’s keep fingers crossed for adding this feature to ruby 2.0.
]]>1
|
|
This works well if there are small number of records to be returned. But what happens when we need to return 10,000 records at once? Things slow down dramatically and the most time-consuming parts are JSON serialization and database operations.
The first obvious thing is generating JSON with only attributes that we need, e.g.:
1
|
|
Tidying JSON gives over 20% performance:
1 2 |
|
Second, we should consider selecting only required columns when we don’t need all of them.
1
|
|
It’ll help us to avoid transferring a huge amount of data to the application from the database and gives 2x speedup:
1 2 3 |
|
Let’s implement a method to return “lightning” array of hashes instead of ActiveRecord instances:
1 2 3 4 5 6 7 |
|
It works the same way as pluck but returns array of hashes instead of array of single column values. Invoke a new method in controller:
1
|
|
Using lightweight hashes makes JSON rendering 2x faster:
1 2 3 4 |
|
There are several JSON libraries available:
It’s a good idea to use the fastest dumper of them:
1 2 3 |
|
So we prefer using Oj dumper:
1
|
|
Summarized benchmark results are:
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 |
|
Right. It works in Ruby and is even used in ActiveModel codebase as column names in databases can have spaces for instance. So if column name is “total price” you can call this method which was defined by ActiveModel for you as:
1
|
|
And you definetely can’t call it as
1
|
|
because, you know, it’s impossible for parser to understand this code as a method call.
Actually I’ve discovered this feature when geocoder gem broke our application. Look at the following diff:
1 2 3 4 5 6 |
|
Before this was fixed as you can see there was a typo in defining response_attributes
array (which is used to define methods with corresponding names). Yeah, extra commas (note: you don’t need to separate items in array if you use %w[]
syntax). But it worked OK because we can define methods with commas and other stuff. And thanks to that typo class
method wasn’t redefined in our code. Fortunately it’s already fixed.
Hey, readers! Today I want to share with you some news about upcoming SimpleForm 2.1.0 release. A lot of bugs were fixed and some awesome features were added. So let’s take a look on these goodies:
Now you can pass an array to :checked
option and these values will be checked:
1
|
|
Just wow, right?
Imagine you have such code:
1 2 3 |
|
So if @documents
is a nil
object then all documents will be loaded. Looks like a security hole, right? Not anymore. In SimpleForm 2.1.0 f.association
creates blank select for blank association. That’s right, mind = blown.
Guys, guys, guys! With new SimpleForm form you can add all these additional CSS classes (like string
for string inputs, text
for text inputs, etc.) only for wrapper! I can’t believe in this but it’s a pure truth! All you need is to set up config.generate_additional_classes_for
with [:wrapper]
option:
config/initializers/simple_form.rb:
1
|
|
Oh. My. God. With new SimpleForm you can specify checked and unchecked values for boolean inputs:
1
|
|
And now instead of default 1
for checked value and 0
for unchecked you’ll get on
and off
. Unbelievable. I was dreaming about it soooooo long.
And last but not least. Make sure you’re sitting right now. Hey, you, I’m not kidding, take a sit because this last awesome feature will turn your world upside down.
You can specify different wrappers for different input types in your SimpleForm’s config! Take a look at this example below:
config/initializers/simple_form.rb:
1
|
|
BOOM! All your string inputs uses :prepend
wrapper. Crazy, right?! Just think about it. You can specify different wrappers for different input types in your SimpleForm’s config. WOOOOW!
Yeah, this 2.1.0 version is not released yet but you always can try it from git:
1
|
|
You can read full CHANGELOG here
Phew, that’s all. I hope you like it. Cheers!
]]>1 2 3 |
|
It’s worth to have simple input definition in views without need to specify all those options every time. The following looks much better:
1
|
|
Or even:
1
|
|
But every time implementing custom inputs we should bother about writing tests for them. Let me show the way we write tests for custom SimpleFrom inputs. We’ll use RSpec and put input specs into spec/inputs
directory.
Before writing tests we need to implement an example group module that will be included into input specs. To do this you can create spec/support/inputs.rb
with the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Now you have input_for
test helper method available in your input spec. So it’s time to describe the birth date input:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
We use input_for
test helper method here and assert_select
method from Rails.
app/inputs/birth_date_input.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Finally we need to configure input mappings in order to use f.input :birth_date
instead of f.input :birth_date, as: :birth_date
for attributes that have birth_date
in their names. This is done modifying SimpleForm configuration in config/initializers/simple_form.rb
:
1 2 3 4 5 |
|
Using this approach we’re able to clean views using custom SimpleForm inputs and easily write tests for them.
]]>In our application we have app admins and company (account) admins along with regular members. Company admins should be restricted to create only company admins and company members of their company. Regular members cannot change theirs role or company. App admins are allowed to do anything with users.
As both app admins and company admins have similar functionality it’d be good to have a one controller to manage users. We may have something like this in the admin/users
controller:
1 2 3 4 5 6 7 |
|
But we need to restrict company admins from creating application admins and forbid changing their company. It means that we need to use different validations and different attr_accessible
attributes. It can be achieved using attr_accessor
in the User
model or some complex parameter filtering in the controller, but let’s imaging how the controller code can be written to keep it simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
In order to make the code above work it’s possible to use inheritance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
So we introduced User::AdminContext
and User::CompanyAdminContext
to be used in different cases. They have their own attr_accessible
attributes and validations. Also we implemented the base User::Context
to make contexts to use the same model_name
as the User
model.
This way allows the controller to choose a model behavior and the model isn’t got polluted with complex code.
]]>active_merchant
by Shopify:
ActiveMerchant is a simple payment abstraction library used in and sponsored by Shopify.
So when I had to add Robokassa payment system to our project I opened a list or the supported integrations and was kinda disappointed because active_merchant didn’t have it. Latter I found a fork that had Robokassa support but it was outdated and some tests failed ec801d3d4f8. So I decided to look at this code and fix it instead of writing all this stuff from scratch.
Actually to make that tests work I had to fix a small typo 07fb5494134. Yeah, it was easy. The next thing I decided to add to that implementation was different urls for sandbox and production environments (yeah, Robokassa has a rule that you should test your code in sandbox at first and when all your stuff work you can use production environment). You can see this code here - c2ec85d53cb
And then it’s time to add active_merchant to the project. Add it to your Gemfile
:
1
|
|
To use ActionView
helpers like payment_service_for
you should add this to activemerchant.rb
file and put in in the initializers
folder:
1 2 3 4 |
|
If you want to use production mode add this line to initializers too:
1
|
|
The next step is routes. Robokassa makes a push request to our application when transaction was made. So let’s add these routes:
1 2 3 4 5 6 |
|
To make this work we should create controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
And the last thing we need is to add form to our page:
1 2 3 4 5 6 |
|
That’s it! Now if we have @payment
object and we submitting this form we will be redirected to the Robokassa site where we can make payment with amount of @payment.amount
.