Monday, August 9, 2021

Using Shrine to add a notification alert to a dynamic favicon

If you've ever worked with the Trello app, you may have noticed that when you have a new notification, the favicon (the little icon in the browser tab) has a red dot added to it. This really helps grab your attention when you receive a notification, but you are working on another tab.

So when the question "can we do this in our app as well?" came up, at first I said, sure, no problem! After all, its just changing the href link to point to another favicon image, but with the red dot.

But then I remembered we allow users to upload their own favicons... Now there's a interesting challenge. 

Using a Shrine to Add the Alert Dot

In our project we use the Shrine gem to handle file uploads, and MiniMagick for image processing. So I added a new derivative to my favicon uploader:
 
class FaviconUploader < Shrine
  Attacher.derivatives do |original|
    magick = ImageProcessing::MiniMagick.source(original)
    red_dot = Rails.root.join('app', 'assets', 'images', 'favicons', 'overlays', 'alert.png')

    {
        favicon:  magick.resize_to_fill!(64, 64),
        alert:  magick.resize_to_fill(64, 64).composite!(red_dot,
          mode:    "over", 
          gravity: "south-east", 
          offset:  [0, 0])
    }
  end
end


Notice that in the original derivative we call resize_to_fill!, while in the new derivative we call resize_to_fill (without the bang) because the bang, which tells MiniMagick to perform the chained effects, should be on the last method called.


So in the new derivative we first resize the image, and then composite the image with a custom PNG image of a red dot, so that it sits in the bottom-right corner.


Dealing with Favicon Change

Now that we have the images, let's move on to the easy part - changing the favicon to the one with the dot when there are notifications, and changing back when the notifications are cleared.
I handled this by adding a data attribute to the link tags for each of the icons. For example, this is what the link tag for standard non-Apple devices looks like when using the static favicon:

<link rel="icon" type="image/png" sizes="64x64" href="/assets/favicons/favicon-default.png" data-alert-favicon="/assets/favicons/favicon-alert.png" data-default-favicon="/assets/favicons/favicon-default.png">

The link tag for the dynamic favicon is rendered like this:

<% data_attrs = %[data-alert-favicon=#{@main_scope.favicon_url(:alert)} data-default-favicon=#{@main_scope.favicon_url(:favicon)}] %>
<% default_href = @main_scope.favicon_url(:favicon) %>
<link rel="icon" type="image/png" sizes="64x64" href="<%= default_href %>" <%= data_attrs %>>

Then I can call the following function when a notification is added or when the last unread notification is marked as read:

function update_favicon_alert(alert_should_be_on){
  $('[rel="apple-touch-icon"], [rel="icon"]').each(function(){
    new_href = $(this).data(`${alert_should_be_on ? 'alert' : 'default'}-favicon`);
    if (new_href && new_href.length > 0) {
      $(this).attr('href', new_href);
    }
  })
}

That's it!

Conclusion

Shrine is a very powerful tool, and we love using it. It does have a steeper learning curve than the Paperclip gem or even Active Storage, but once you get over the hump, it's a versatile tool with enormous capabilities.

Technically we could also embed the number of notifications (from 1 up to say 9, and for any more show 9+) by having 9 such derivatives, and as before update the link to use the appropriate favicon, but that felt to us a bit cumbersome and very unnecessary.

Monday, May 20, 2013

Running a rails server to support SSL

Ok, so here's the problem. Rails' development server doesn't ship with the option to accept https requests, which is quite annoying when developing an application that demands it.

That is why I'm using the 'Thin' server instead of the default 'WEBrick' server.
Simply installed like this:

$ gem install thin

The next step is to generate the certificates (self-signed):
$ openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout .ssl/server.key -out .ssl/server.crt

Since these certificates should only be used in development, I added them to the .gitignore file.

Now, we just need to run Thin with the certificates. However, the server still cannot serve both http and https requests on the same port, so we create 2 separate servers, one for the non-SSL and one for the SSL requests:

$ thin start -p 3000
$ thin start -p 3001 --ssl --ssl-verify --ssl-key-file .ssl/server.key --ssl-cert-file .ssl/server.crt

Checkout this blog entry by Railway for more details, and a code snippet for letting the application handle the port switching: http://www.railway.at/2013/02/12/using-ssl-in-your-local-rails-environment/

Tuesday, October 30, 2012

New and Edit actions to render same template

When I started learning rails, the '_form.html.erb' partial wasn't generated automatically in the rails scaffold generator, but it was commonly used, and eventually turned into a standard. And it was very important in the DRY sense, a main goal in rails programming.
But to me this wasn't enough. It always bothered me that now, this left two view files, 'new.html.erb' and 'edit.html.erb', almost entirely bare.

#app/views/user/new.html.erb
<h2>New user</h2>

<%=  render 'form' %>


#app/views/user/edit.html.erb
<h2>Edit user</h2>

<%=  render 'form' %>

Recently I was working on a project that involved internationalization (i18n), so after using translations on the  headers, the view would look like this:

#app/views/user/new.html.erb
<h2><%= t 'users.new.title' %></h2>

<%=  render 'form' %>


#app/views/user/edit.html.erb
<h2><%= t 'users.edit.title' %></h2>

<%=  render 'form' %>


#config/locales/en.yaml
en:
  users:
    new:
      title: "New user"
    edit:
      title: "Edit user"

At this point I was really frustrated by how ridiculous it was to have almost identical code in these 2 files. So I said to myself "to hell with this!", and deleted both views.
Obviously that's not all I did. I renamed the '_form.html.erb' to 'form.html.erb' so that it was no longer considered a partial, I added the following 1st line to the end of my 'edit' and 'new' actions, and I added the following 2nd line to the top of my 'form.html.erb' file:

#new and edit actions render command:
  render 'form'

#h2 tag moved to 'form.html.erb':
  <h2><%= t "users.#{params[:action]}.title" %></h2>

I'm not entirely sure this would be considered a good practice, but having those 2 redundant files off the table feels sooo good :)
Also, I suppose this little trick doesn't seem worth it if you're not already using i18n, but if you too are annoyed by these kinds of things, you should give it a try.

Wednesday, August 8, 2012

Fetch the record

I've been working a lot with very standard controllers lately, with all the usual CRUD actions, and I've been implementing some useful before_filter methods to DRY things up. Mostly authentication methods, but also for fetching the record, instead of calling it in the various actions.
@user = User.find(params[:id])
So I created a simple method, stuck it in application_controller.rb, and thinned out my controller a bit more.

But then, once my apps got bigger and more complex, I found my application controller was getting chubby with record fetchers! So I started to look for a solution, and here is what I came up with. Create a single fetcher method and have it accept an argument to tell what model to refer to. This is how it looks at the moment:
  def fetch_record(record)
    begin
      eval "@#{record} = #{record.capitalize}.find(params[:id])"
    rescue NameError #raised when a non-existing model is called.
      render file: 'public/500.html', status: 500
    end
  end

The eval function is quite useful in this case, executing the string after it is evaluated.
A problem that arises is, how do you call a before_filter method with arguments?
Well, after digging around for a while, I found the solution is using a proc:
before_filter { |controller| controller.send(:fetch_record, "user") }
This will call that fetch_record method and send "user" as a parameter. However it will do so for every action, and that's not good.
The method before_filter itself may also receive a hash of options such as :only and :except, to restrict which actions will execute the called method. But in this case, the hash must be passed a little differently - in parentheses, before the proc:
before_filter(only: [:show, :edit, :update, :destroy]) { |controller| controller.send(:fetch_record, "product") }

I'm quite happy with this improvement so far, but I would be glad to hear if there are better practices out there you guys have seen or came up with.

Saturday, June 23, 2012

Arrays in Rails are awesome!

This week I was trying to figure out a way to store an array in the database of my Rails app. Apparently it's the easiest thing to pull, and considering how helpful the arrays and hashes in ruby are, this is a feature every Rails developer should be able to use.

Suppose we have the need of saving an array of objects within a model. There is no obvious way to do this, since the database can only accept certain simple types, like integers, strings, booleans, etc. But the trick is way too simple. ActiveRecord allows you to call the `serialize' method on a string or text (preferably text) column, turning it into a YAML parsed array or hash. This means every time the record is saved, the array is parsed into a YAML string and stored that way, and when it is pulled from the database the array is instantly rebuilt from the string. So as far as we should be concerned, the record contains an array, and array-related methods can be called directly on the relevant attribute.

Here's a short example to clarify. Say we have a user-portal, and we want to be able to show the user the last 5 times he or she logged in. We could do this by using 5 separate date columns, but that would be cumbersome and would not serve as a good example. So our migration and user.rb files should look something like the following:

# migration file
...
create_table :users do |t|
  t.text :latest_logins
...

# user.rb
attr_accessible :latest_logins ...
serialize :latest_logins
...

Now all that's left is to take care of the logic, which should be quite simple once you're familiar with the many methods for manipulating arrays and hashes in Ruby. For instance, we could put the following code in the user model for pushing a new log-in date and popping the earliest one.

# user.rb
...
def update_logins
  latest_logins.delete_at(0)
  latest_logins << Time.now
  self.save
end
...


Ruby arrays and hashes are so intuitive, and have some methods that make working with them efficient and simple.

Please let me know if this post helped you, or if you have a issue or question you wish discuss.

Thursday, June 14, 2012

First Fire Revised

It didn't take too long for me to find a better way to solve the issue from last week. I guess that's just the way these things happen...
If you haven't read last week's post, I'll summarize. I was looking for a way to validate a password and it's confirmation, without using a whole other column for the password confirmation field. M solution was to validate manually before updating, and then using the `except' command on the params hash to get just the parameters I want to save.
Turns out there's another option I did not think about, because I wasn't too familiar with the subject. This option involves using the `attr_accessor' method. This method, used in the model, creates a setter and getter for a virtual attribute for the model, which instead of being saved in the database, is only created as an instance variable. This method is mostly used in order to simplify complex forms, and it will also be very helpful in this scenario. Let's see how.

So here is my form again, just as it was in the previous post:
<%= simple_form_for(@user, ...) %>
...
<%= f.input :password %>
<%= f.input :password_confirmation %>
...
<% end %>

And here is my updated model:

#user.rb
...
attr_accessible :password_confirmation ...
attr_accessor :password_confirmation

validates :password, presence: true, confirmation: true
validates :password_confirmation, presence: true

And that's all there is too it. In fact, if you were to include the password_confirmation as a column in the database, everything would look the same except for that attr_accessor line.
It may be worth to mention that also in the form view the virtual attribute is treated as if it existed in the table, so no special code to add there either, while in the controller there is no need in manual validations or in manipulating the params hash like I did last time.

I must say, I was quite amazed by how simple this solution is, especially since it allows you to keep on using the normal validation methods.

That's it for this week. Next week I promise to fixate on something different :)

Tuesday, June 5, 2012

First Fire

Hi there.
This blog is here mainly in order to share the stuff I learn about RoR while on the job, for others to learn and contribute, for future reference, and for fun.
I am currently working on a pretty big project - creating an alumni web-portal for a relatively large non-profit organization. When I say relatively, I mean for me, and when I say large I mean about 3000 existing graduates, and about 300 more every year or so. I started a month ago, and the launching deadline is in about another month. Thing is, I'm a year-old noob as far as Ruby on Rails, and this is by far the most complex challenge I have ever been faced with in my career. Oh, and the god damn web-designers haven't come through with the final designs yet, so we're a bit off schedule.

I want to share a little neat trick I learned today, which, as far as I'm concerned is a solution to a fairly annoying problem. The thing is, I'm not quite sure it's the best solution out there, so if anyone knows of a better way to do this, let me know!
So let's dig in:

In the sign-up form, the user is asked to enter his password twice, once in the password field, and another in the confirmation field. In the from, the most simple way to do this would be this:

<%= simple_form_for(@user, ...) %>
...
<%= f.input :password %>
<%= f.input :password_confirmation %>
...
<% end %>

(* In my code I use many methods from gems such as simple_form and bootstrap that might be unfamiliar to some, but the methods are pretty straightforward, and besides, you should probably check these gems out!)

The problem with this is that in your standard create or update action, rails will try to pass this password_confirmation attribute to the model, and hit an error.

My initial solution to this was to create a column in the users table called password_confirmation, add the attribute to the attr_accessible method in the model, and then just live with an unnecessary column until the end of  time. That just won't do.

My second solution was quite silly, but it involved creating a dummy model, which would be used only to validate the password confirmation, and thus would never actually be saved. However this was like taking a walkman to a party after you've decided not to take a diskman. An unused model is far worse than an unused column.

My third solution was to somehow omit the password_confirmation from the parameters sent to the model, however this would mean implementing my own confirmation validation before saving. At this point I ran out of ideas, and the internet was giving me too many deprecated solutions. So I went for it, and this is how I did it:

def create
@user.confirm_password
@user= User.new(params[:user].except(:password_confirmation))
...
end
...
def update
@user.confirm_password
if @user.update_attributes(params[:user].except(:password_confirmation))
...
end

I think it's pretty staight forward, but in short, the except method allows you to omit certain entries from the params hash with ease. The confirm_password method is used to do exactly what the confirmation validation does, so it's not ideal, but I prefer that to having unnecessary columns or models...