Rails bulk delete items on frontend part

So when your database grows it might come in handy if you give your customers the option to bulk manipulate data in lists. I’ve got a list of products and my client told me, that they often create dozens of test items to check out things and then they delete them. Clicking one by one and, especially, waiting every time for the page to load can be tiresome. Here is my approach:

Wrap the listing table in a form

=form_tag admin_bulk_delete_path(sublevel: params[:sublevel], :id => "bulk-edit-form") do
          =hidden_field_tag :object, object_name(object)
                  -if object.has_attribute? :url
                -object.each do |obj|
                      =check_box_tag "object_ids[]", obj.id, false, class: "i-checks bulk_actionable"
                    -if obj.respond_to?(:url)
                        =link_to t('admin.form.actions.edit.verb'), send("edit_admin_#{local_assigns[:namespace]}#{object_name(object)}_path", obj), class: "btn-white btn btn-xs", id: "edit_list_item_#{obj.id}"
                        =link_to t('admin.form.actions.delete'), send("admin_#{local_assigns[:namespace]}#{object_name(object)}_path", obj), method: :delete, data: {confirm: t('admin.form.confirm.main') }, class: "btn-white btn btn-xs", id: "delete_list_item_#{obj.id}"
                  = submit_tag t('admin.form.actions.bulk_destroy'), class: "btn btn-xs btn-danger", id: "bulk-delete", data: {confirm: t('admin.form.confirm.bulk_delete')}

Don’t get confused by my ‚object’… err… object, it serves the purpose of enabling many models to use this listing partial. The main parts are ‚form_for‘, the checkbox and the ’submit_tag‘. It is important to use an array for storing the checkbox values, or else we won’t be able to retrieve all id’s at once.

Now the form leads us to the custom controller (‚rails g controller bulk_actions‘):

class Admin::BulkActionsController < AdminController

  def bulk_delete
    object = params[:object]
    if params[:object_ids].is_a?(Array) && params[:object_ids].length > 1  #let's make sure we got what we expected
      @objects = object.singularize.capitalize.constantize.find(params[:object_ids])
      @objects.each do |object|
      flash[:success] = t('admin.flash.success.delete')
      redirect_to send("admin_#{object.pluralize}_path", sublevel: params[:sublevel])
      flash[:danger] = t('admin.flash.fail.delete')
      redirect_to send("admin_#{object.pluralize}_path", sublevel: params[:sublevel])

First of all, I’m getting the object from ‚params[:object]‘ (this is why I included the hidden_field_tag in my form). Since I’m using the partial for any model, I need to know, do I delete the Post model or the Product model right now. The if-clause makes sure we only handle a bunch of items, it does not make any sense to bulk delete 1 item. Since my object is rather a string, I need to convert it to a modal constant. Then we simply iterate over each found object and destroy it. You can disregard the ’sublevel‘ part.

Now for the frontend – I wanted the delete button only to appear if two or more items are selected and be hidden elsewise. Since I’m using iChech checkboxes we need to hook up on different methods. Particularly it is ‚ifToggled‘ in this case:

  $('input').on('ifToggled', function(event){
      var check_count = $('input:checked').length;  //count the number of checked elements
      if( check_count > 1 ) {
      } else {

Add one last thing – the route

post 'bulk_delete', to: 'bulk_actions#bulk_delete'

And you are ready to go!

Heavily inspired by this article.

Create a search…then speed it up by a factor of 50!

One great way to implement a basic search is to put all your data fields into one big array and then perform the search on this one field. Let’s say, you’ve got a Customer model. Open up customer.rb (in app/models/) and write this few lines of code:

class Blueprint < ActiveRecord::Base
 scope :search, ->(search){ where('keywords LIKE ?', "%#{search.downcase}%") }

 before_save :set_keywords

 def set_keywords
    self.keywords = [name, email, birthdate, etc.].map(&:downcase).join(' ')

To pick only the searched items we’re creating a scope to which we’re passing the search argument. You can add a function, which will turn all separate fields into one big keywords array.

Now, open up your Controller (CustomersController.rb)

class CustomersController < ApplicationController
  @customers = Customer.search(params[:search])

If desired, you can add some validation for cases if no search parameter is provided.

This approach won’t yield you amazon like results, but can bring one pretty far.

Step up the game

If you want to make one little step further and do not want to bother with „heavy industry“ tools like ElasticSearch, then you can utilize PostgreSQL’s awesome built in indexing features. This approach requires a bit more setup, than the first one, but once you follow those steps, it will appear straight forward.

The main goal is to write our custom SQL query. In the end what we want to achieve is something like this:

  lower(first_name) LIKE 'bob%' OR
  lower(last_name)  LIKE 'bob%' OR
  lower(email)      LIKE 'bob@example.com'
  email = 'bob@example.com' DESC,
  last_name ASC

Using rails syntax this would mean we need a query like this:

Customer.where("lower(first_name) LIKE :first_name OR " +
  "lower(last_name) LIKE :last_name OR " +
  "lower(email) = :email", {
    first_name: "bob%",
    last_name:  "bob%",
    email: "bob@example.com"
}).order("email = 'bob@example.com' desc, last_name asc")

To create this query and keep things clean I’m using an extra class (models/customer_search_term.rb):

class CustomerSearchTerm
  attr_reader :where_clause, :where_args, :order
  def initialize(search_term)
    search_term - search_term.downcase
    @where_clause = ""
    @where_args = {}
    if search_term =~ /@/

#Following code goes there

Now we need this helper function, „build_for_name_search“ (I’ll omit the first one for now).

def build_for_name_search(search_term)
	@where_clause << case_insensitive_search(:first_name)
	@where_args[:first_name] = starts_with(search_term)

	@where_clause << " OR #{case_insensitive_search(:last_name)}"
	@where_args[:last_name] = starts_with(search_term)

	@order = "last_name asc"

def starts_with(search_term)
	search_term + "%"

def case_insensitive_search(field_name)
	"lower(#{field_name}) like :#{field_name}"

This function does nothing else rather than building the string for our SQL query. You can verify it by examining the variables – @where_clause is a string, while @where_args is a hash.

Finally, let’s build our controller:

class CustomersController < ApplicationController
	def index
		if params[:keywords].present?
			@keywords = params[:keywords]
			customer_search_term = CustomerSearchTerm.new(@keywords)
			@customers = Customer.where(
			@customers = []

We’re almost there. This search works, but it’s still a bit slow (see below for speed results).

Now we need to create custom indexes on those tables. We will stick to old up and down migration methods, since rails won’t understand our custom SQL migration if we’d stick to „change“.

class AddLowerIndexesToCustomers < ActiveRecord::Migration
  def up
  	execute %{
  			customers (lower(last_name) varchar_pattern_ops)

  	execute %{
  			customers (lower(first_name) varchar_pattern_ops)

  	execute %{
  			customers (lower(email))

  def down
  	remove_index :customers, name: 'customers_lower_last_name'
  	remove_index :customers, name: 'customers_lower_first_name'
  	remove_index :customers, name: 'customers_lower_email'

It’s important to use „varchar_pattern_ops“, since we are not using an exact match, but instead using a like operator. Now, if we fire up rails dbconsole and perform EXPLAIN ANALYZE on both methods, we can see the difference.Ama

This was the original result:

Started GET "/customers?utf8=%E2%9C%93&keywords=andres148127%40dicki.biz&commit=Find+Customers" for ::1 at 2017-01-04 22:19:21 +0300
Processing by CustomersController#index as HTML
  Parameters: {"utf8"=>"✓", "keywords"=>"andres148127@dicki.biz", "commit"=>"Find Customers"}
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
  Customer Load (561.3ms)  SELECT "customers".* FROM "customers" WHERE (lower(first_name) like 'andres%' OR lower(last_name) like 'andres%' OR lower(email) like 'andres148127@dicki.biz')  ORDER BY lower(email) = 'andres148127@dicki.biz' desc, last_name asc
  Rendered customers/index.html.haml within layouts/application (572.4ms)
Completed 200 OK in 591ms (Views: 27.8ms | ActiveRecord: 561.6ms)

After spicing things up:

Started GET "/customers?utf8=%E2%9C%93&keywords=andres148127%40dicki.biz&commit=Find+Customers" for ::1 at 2017-01-04 22:40:59 +0300
Processing by CustomersController#index as HTML
  Parameters: {"utf8"=>"✓", "keywords"=>"andres148127@dicki.biz", "commit"=>"Find Customers"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
  Customer Load (2.1ms)  SELECT "customers".* FROM "customers" WHERE (lower(first_name) like 'andres%' OR lower(last_name) like 'andres%' OR lower(email) like 'andres148127@dicki.biz')  ORDER BY lower(email) = 'andres148127@dicki.biz' desc, last_name asc
  Rendered customers/index.html.haml within layouts/application (12.7ms)
Completed 200 OK in 32ms (Views: 28.3ms | ActiveRecord: 2.3ms)

This is an increase by a factor of 243!

Eve-trader.net – the new source for EVE Online productioneers

Greetings, capsuleers!

Few days ago I launched my long time dreamed project – eve-trader.net (http://eve-trader.net). This services is intended to be a lightweight alternative to updating your excel sheets daily with current prices (most likely you will get them from eve-central.com). Via the eve-central.com API I fetch the prices on requested blueprints, calculate for you the cheapest material prices and suggest where you can sell the end product most expensive.

Settings and assumptions as of today:

  • Areas considered for calculation are all solar systems within 5 jumps around the five main trade hubs (which currently are – Jita, Amarr, Rens, Dodixie, Hek) and which are high sec
  • All skills are lvl 5 (which you probably should have if you considering make money with industry)
  • No fees and taxes are calculated (if they are hindering you to make profit you are producing the wrong thing)

The service is in it’s early stage, so be prepared to encounter some changes in near future. What is planned:

  • Having at least a few options available in an „expert“ menu
  • News section
  • Implement user accounts where user can save their blueprints, thus, having a faster access to their favourite blueprints
  • Having a history saved for each blueprint.
  • A suggestions feature, where users can suggest new things and other users can vote on them.
  • Making market predictions based on Monte-Carlo-Simulation or Expected Shortfall methods.

Rails eShop – my first big project using Ruby on Rails

[readolog_dropcap ]H[/readolog_dropcap]appy new Rails year, fellow riders!


I finished my very first „serious“ project – yay! It looks and behaves like a rails eShop (you can find link further down below). Before I go into technical details some intro – this website was created for friends of mine and although it was kind of experimental for me (first real app), it turned out to be quite decent as one might say so.

As with every customer-oriented project, there was a lot of JavaScript involved, which is by far not my strongest language. It started calm and nicely with pure ruby on rails, I could use a lot of stuff I learned from this book – Agile Web Development With Rails. But then… as more and more wished appeared I realized that there is no way around some front-end development.

Enough smalltalk, let’s get straight to the data:

  • Bootstrap Theme used – „Minimal
  • Notable gems: figaro (managing global variables), devise (users), carrierwave (images uploads), sendgrid (for sending any kinds of mails)
  • Database: postgresql (I think it’s pretty a must-have for shops because of hstore – which, as far as I understand makes postgres almost non-relational, because hstore columns allow to store hash/value pairs)
  • Deployment: capistrano, puma, nginx (you can check out my article about deployment here)
  • Other features: protected admin namespace

[readolog_dropcap ]M[/readolog_dropcap]ost notable code


JavaScript – a calculator for end value of a product based on amount, delivery method and size (plus additional services, which are to be differentiated between one for each product and one for a full order). There is some hard coded stuff and of course this piece of code might not be the state of the art, however it works pretty well.

$(document).ready ->

    if $('a.size_input').length != 0

jQuery ->
    $('li a').click (event) ->
        # event.preventDefault()

    $('form').on 'click', '.add_fields', (event) ->
    	time = new Date().getTime()
    	regexp = new RegExp($(this).data('id'), 'g')
    	$(this).before($(this).data('fields').replace(regexp, time))

    get_multiplier = ->
        amount = $('#amount').val()
        multiplier = 1.5
        multiplier = switch
            when amount <= 99 then multiplier = 1.5
            when amount >= 100 and amount < 300 then multiplier = 1.4
            when amount >= 300 and amount < 500 then multiplier = 1.3
            when amount >= 500 and amount < 1000 then multiplier = 1.25
            when amount >= 1000 and amount < 3000 then multiplier = 1.23
            when amount >= 3000 and amount < 7000 then multiplier = 1.2
        return multiplier

    get_delivery = ->
        delivery = $('#delivery').val()
        return parseFloat(delivery).toFixed(2)

    get_size = ->
        # in this method we either select the price value based on the flash
        # drive size, or, if it has a basic price, just simply the basic price
        # from the hidden #basicprice id

        selected_size = $('input[name=volume]:checked').val()
        if (selected_size == undefined)
            return parseFloat($('#basicprice')[0].innerText)
            array = selected_size.split(",")[1].replace(']','')
            return parseFloat(array).toFixed(2)

    get_all_addservices = ->
        # This method delivers all checked addservice price
        # so we can first substract it before applying any multiplications
        addservices = new Array()
        addservicesParty = new Array()

        console.log('inside get all addservices:')
        checkboxes = document.getElementsByName("addservices[]")
        for checkbox in checkboxes

            addserviceprice_splitted = (checkbox.value).split(" ")
            # console.log(addserviceprice_splitted[2])
            # Here we need to check if the 'apply to whole party is true or false'
            party = addserviceprice_splitted[2]

            addserviceprice = parseFloat(addserviceprice_splitted[1]).toFixed(2)
            if checkbox.checked
                parsedPrice = parseFloat(addserviceprice)

                if party == 'true'

        if addservices.length > 0
            sum = addservices.reduce((a,b) => a+b)
            sum = 0

        if addservicesParty.length > 0
            sumParty = addservicesParty.reduce((a,b) => a+b)
            sumParty = 0

        console.log('Addservices combined: ' + addservices + '| Addservice to whole party: '+ addservicesParty)
        return [parseFloat(sum), parseFloat(sumParty)]

    recalculate = ->
        # This function recalculates the price
        base = get_size()
        deliveryCoefficient = get_delivery()
        amountCoefficient = get_multiplier()
        addservices = get_all_addservices()

        addserviceOne = addservices[0]
        addserviceParty = addservices[1]

        amount = $('#amount').val()

        console.log('base - ' + base + '| delivery - ' + deliveryCoefficient + ' | add service sum - ' + addservices + ' | amount coefficient - ' + amountCoefficient)

        endPriceOne = (base*deliveryCoefficient*amountCoefficient) + addserviceOne
        endPriceParty = (base * deliveryCoefficient * amountCoefficient) * amount
        endPriceAll = endPriceOne * amount + addserviceParty
        endPriceServices = (addserviceOne * amount) + addserviceParty


    $('.size_input').click (event) ->
    $('.addservice_checkbox').change (event) ->

    $('#amount').on('input', (event) ->

    $('#delivery').change (event) ->

    $("#thediv").click (event) ->

    $('#load_more_btn').click (event) ->

Here we have some fancy helper methods which I grabbed from Ryan Bates screencasts. They allow you to add fields on the go:

module ApplicationHelper

	def hidden_div_if(condition, attributes = {}, &block)
		if condition
			attributes["style"] = "display: none"
		content_tag('div', attributes, &block)

	def link_to_add_fields(name, f, association)
		new_object = f.object.send(association).klass.new
		id = new_object.object_id
		fields = f.fields_for(association, new_object, child_index: id) do |builder|
			render(association.to_s.singularize + "_fields", f: builder)
		link_to(name, '#', class: 'add_fields', data: {id: id, fields: fields.gsub("\n", "")})

[readolog_dropcap ]L[/readolog_dropcap]ast, but not least


we have of course a fully functional rails eShop with a working cart, order placement, sending confirmation letters to customers and admins, an administration panel for managing products etc. etc.

Unfortunately the owners decided to put this page on ice, therefore it’s unlikely that you will receive some flash drives. However you can check the page out all by yourself – http://souvenirnya-produktsiya.ru/


Deploying your Rails app on a VDS

Recently I finally finished development of my very first serious app, which was ready to be deployed on a production machine. This, however, turned out to be trickier than anticipated. I’ve read myself into various tutorials, decided to spin up a droplet on digitalocean with preinstalled Unicorn + Nginx bundle. While this droplet works well with the initial app I had a hard time trying to make

a) multiple sites work

b) any site but the default work

I surely don’t want to assume that unicorn is somewhat hard to handle, it may have been just me, who is unable to accomplish such a task. On the other hand I found that using puma instead of unicorn is far more profitable.

I grabbed this outstanding tutorial – https://www.digitalocean.com/community/tutorials/deploying-a-rails-app-on-ubuntu-14-04-with-capistrano-nginx-and-puma and was able to get the whole app running on production within few hours.

Don’t forget to install ImageMagick sudo apt-get install imagemagick if you suddenly discover, that there are no image uploads on production. Check out this article too – https://chuanhesmile.wordpress.com/2014/12/13/issue-fixed-rollback-transaction-when-uploading-images-using-carrierwave/

This is important because there might be no errors at all and it did cost me a lot of time to figure that out on my own.

My last advise – check out how to create a memory swap because that’s one of the first issues I ran into while deploying my application (I’m using the 5$ droplets with 512 MB RAM for all my projects).

PostgreSQL Rails migration – how to fix DatatypeMismatch error

So recently I made my very first blog app (you can watch this awful creation here) and while uploading to Heroku was as expected stress free, migrations weren’t.

The error was

[readolog_blockquote ]

PG::DatatypeMismatch: ERROR:  column „category_id“ cannot be cast automatically to type integer

HINT:  Specify a USING expression to perform the conversion.


I could imagine this would cause a problem, because initially „category_id“ was named plain „category“ and was a string instead of an integer.

By simply creating a new migration

class FixColumnName < ActiveRecord::Migration
  def change
  	rename_column :posts, :category, :category_id
  	change_column :posts, :category_id, :integer

I thought to have it done until I stumbled upon the error.

My first search led me to this awesome article – check it out http://makandracards.com/makandra/18691-postgresql-vs-rails-migration-how-to-change-columns-from-string-to-integer

(You basically need to follow the HINT and convert your data as following:

class FixColumnName < ActiveRecord::Migration
  def change
  	rename_column :posts, :category, :category_id
  	change_column :posts, :category_id, 'integer USING CAST(category_id AS integer)'

However, after adding a new migration… the error still occured. Until I watched carefully the error logs

== 20150520191141 FixColumnName: migrating ====================================
-- rename_column(:posts, :category, :category_id)
   -> 0.0029s
-- change_column(:posts, :category_id, :integer)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::DatatypeMismatch: ERROR:  column "category_id" cannot be cast automatically to type integer
HINT:  Specify a USING expression to perform the conversion.

As you can see, not the new migration is causing problems, but the old one! So if you encounter the same problem as I did, please check your older migrations – you just need to change this line in the old migration as well:

change_column :posts, :category_id, 'integer USING CAST(category_id AS integer)'


The Software Development Edge – Book Review – Part I

Hear me, I beg. And say thankya, big-big. This one is going to be a hell of long post, but do not worry, you can browse through the cites really fast and get all the essential data you need. My very short summary – go and buy this book, remember (almost) every page, it will do you some good. The software development edge by Joe Marasco should be your holy book, your guidance in the dark realm of leading teams and developing software.

I counted exactly 170 good ideas, worth remembering. Regarding a total count of 370 pages we have a score 170/370 ~ 0.46, which is amazing! It means almost every second page you’ll encounter contains something useful.

As you might guess in my book summary I’ll have to cover over 185 pages of ideas. Since it would take too long to release a complete overview at once I decided to split it up in four parts, each covering around 100 pages of text. The first one ends exactly at page number 100.

For now, here we go – book’s best points, no cites but rephrases:


Spend a lot of time talking to your developers, get familiar with details and problems.

Worst combination possible – keenness absence and sloppy management. Mostly, it will lead to programs that don’t work and if they do, not very well.

If you happen to be the team leader, do not easily yield to your superior (of course you should listen and give in in most of the times, but not easily, because your superiors may suggest (due to their unknowingness) unreasonable demands.

Your iterative approach to solving problems: observe, listen, empathize, synthesize, testing, write down.
Observe – look around you. What’s happening? Where could be the source of a problem or maybe it’s symptoms?
Listen – after you’ve located the problem, talk to people and listen to their opinion. Talk less, write more.
Empathize – the clear difference is here to „listen“ – while empathizing you not only collect objective data, but also give subjective feedback.
Synthesize – now put all parts together: your objective data while observing and listening, emotional aspects while empathizing, your box of tools for solving problems (should be mostly your experience). The result – a possible approach for the given problem – should be put out to test.
Write down – now you should write down everything that occurs while your approach is doing it’s job. If you don’t do it it will become much harder to convince others to accept your approach. Besides, what’s not written down is soon forgotten.

Doubt everything. Check every partial solution.

In every big project someone, who is on his/her own is a potential danger. However you shouldn’t throw all people in one big basket. Create as little teams as possible with as little members as possible. Four groups three or four members each may be way more productive than an horde of 50 employees.

Sooner or later every leader will have to make choices. If your team members were chosen correctly they will accept your decision because having one is better, than none decision at all. However it doesn’t mean that you are free to come up with any crap idea – you still need to analyze give situation. The only thing to avoid is to be paralyzed.


The mountain top – the end of your project – should always be the main goal. Everything that hinders you and your team on the way up must be cut off.

On the other hand climbing up in real life is just the half of it, you also need to come down. This can be compared with supporting your existing project – a successful one can be completed and then supported over a long period of time.

Why projects fail

Nonrealistic time schedule

Too many team members, team contains a lot of mediocre developers compared to the best ones.

Neverending stories projects lasting so long, that all requirements change multiple times.

Ignore first iterations‘ results. No analyze and change after seeing them.

Lack of clear goal, understandable by everyone

What can lead to success

Small, but smart plan with few details is more effective than plan overloaded with details

Clear your work before requirements change

Correct your movement’s vector (use small iterations) on the fly

Durability – extraordinary peak strength means nothing on the long term

Concentration – your team don’t lose sight of the goal


Don’t hire high skilled people to involve them in trivialities.

Main point of every task should be a client’s problem. Even more important is for your product to generate additional value for your customer (by solving his problems).

Best leaders can project their sense of goal to others – by giving them a good example. Managers make sure that this goal is achieved. Best people out there unit qualities of leaders and managers.

Take care of every small problem in time. Even smallest problems tend to grow massively. This leads to two kinds of bad managers – the „ostrich“ ones, who won’t see the problem and the „lazy“ guys, who will postpone everything until it’s too late. Don’t be one of them, problems don’t disappear magically.

Do not let you lead easily. Always remember – your goal is to solve a problem, achieve some result, not to make you most popular and well-liked guy in the department.

Don’t panic. Seriously. Be solid as rock. Every crisis will be over, your duty is to participate in solving this crisis, not in overreacting. Be an example for everyone else (rock-solid).

Laugh. Even when facing dumbest customer’s demand, better laugh about it, do it, forget it than making drama.

Teams forgive their managers a lot of things, but laziness, incompetence, lack of reward for efforts and lack of humor are not among them.

Trust your instincts – if you are really feeling uncomfortable with something, trust your inner self, most likely you’ll turn our being right (this also means do not hire people who will make you uncomfortable – in exceptional cases it might be worth the risk).

Developing software

Goals are most likely not where they meant to be. Often because at the beginning the technical requirements are not quite clear und well understood.

During the process people will make mistakes.

Your goal is a moving one.

A good manager will always make a lot of little steps rather than few big ones. This allows to shorten the overall distance because after one misstep you haven’t travelled a big distance and after a dozen steps your next one will most likely be close to optimal.

Do not start with easiest problems. By doing so you’re digging your own grave. Your team won’t bother with upcoming possible risks. Even the easiest task will grow big (there is always room for improvement). This leads to scarcity of time for the real challenge and to one further problem – explaining that you need more time doing the most difficult part, covering the biggest risks is much easier to your superiors than begging for more time because your team was wasting it.

Iterative approach forces you to apply all lessons learned from this project (all previous iterations) to all upcoming iterations of the same project.