Debug JS Comple Errors on Heroku

Profile
by Jess Brown

ExecJS::RuntimeError: SyntaxError: Unexpected token punc «,», expected punc «:» (line: 1559, col: 7, pos: 56557)

Recently ran into this issue on Heroku and found a a great answer on Stack Overflow I wanted to share how it works:

Here's the link to the Stack Overflow article:

https://stackoverflow.com/a/38605526

Here's the one liner:

JS_PATH = "app/assets/javascripts/**/*.js"; Dir[JS_PATH].each{|file_name| puts "\n#{file_name}"; puts Uglifier.compile(File.read(file_name)) }

Save rails console output to file on Heroku

Profile
by Jess Brown

The rails console was one of the parts of rails that I quickly fell in love with. Quickly being able to run your app's code and access the database from a CLI was very empowering.

If you're debugging code, checking data, or running impromptu reports, the rails console is your goto tool.

If you need to do something with the data you're querying from the console and it's a lot of data, you may wish to save it to a file and this can be not-so-straight forward on Heroku.

Checkout the below video and let me know what you think:

System time zone on local ruby

Profile
by Jess Brown

Most production servers will have a system time zone of UTC. For example on heroku:

$ heroku run rails c
Running rails c on ⬢ brownwebdesign... up, run.2977
Loading production environment (Rails 4.1.16)
irb(main):001:0> Time.now
=> 2017-07-11 12:45:33 +0000

Most local development environments will have a time zone of the system time zone. If your Mac's time is Eastern, your time will look like this:

$ rails c
Loading development environment (Rails 4.1.16)
[1] pry(main)> Time.now
=> 2017-07-11 08:46:52 -0400

This typically will not cause any issues because rails will convert time to UTC to store in database and then convert time to the set time zone configured in the app. Woring with time zones in your rails app is a whole other topic (checkout this article for some great tips).

However, I recently ran into an issue where I was using the Chronic gem to parse user inputed time and it uses the system ruby time. My devlopment results were not matching production results. I found a neat tip to easily make your local system ruby use UTC just like the production system: just set the TZ environment variable:

$ TZ=UTC rails s

## or ##

$ TZ=utc rails c
Loading development environment (Rails 4.1.16)
[1] pry(main)> Time.now
=> 2017-07-11 12:55:45 +0000

An alternative to bundle update

Profile
by Jess Brown

Bundle update can be the cause of a splitting headache. A lesser known fact is that when you run bundle update GEMNAME bundler will actually update GEMNAME and all of GEMNAME's dependencies. And this of course can lead to a much higher chance of conflicts.

Checkout the video to see what I mean:

Give this a try next time you need to 'update' a gem. For more detail, checkout this great explanation and more tips: https://makandracards.com/makandra/13885-how-to-update-a-single-gem-conservatively

Auto Update Assets In Refinery

Profile
by Jess Brown

Refinery uses the Dragonfly gem to upload images and files. It's a really nice gem, but there is a problem when using it in RefineryCms. The problem is, that every time you update an image or file with Dragonfly, you get a new url to the asset. Dragonfly uses a job id in the asset url to encode the path to the image. So the image url may look like:

/system/resources/W1siZiIsIjIwMTQvMTEvMjYvMThfMTZfMjdfNzA4X3Rlc3QucGRmIl1d/test.pdf

and the path to the image is would look like 2014/11/26/18/16/27/708/test.pdf, which you'll see is a directory path of a timestamp.

So what's the problem?

If you link to this pdf in 10 pages of content, and later someone comes up with an edit to the pdf, you'll need to reupload the pdf, which will generate a new link to the pdf and break all of the links to the old version. This is really annoying because people like to update pdf's and images quite frequently and are used to updating the file or image without breaking the links.

How to fix?

Originally I tried to figure out a way to configure Dragonfly so that it would not regenerate the path or that maybe it would not use such a specific timestamp method of creating the directory. I could not figure this out. Plus, if do you that, then you still have an issue with caching, especially if you're using a CDN like CloudFront (note the remark about fingerprinting if you visit the link).

Then I had a thought. What if we just automated the updating of links each time an asset was updated? Well, I started exploring and it turns out it's pretty easy. Here's how it works.

First you need an after_update hook for the Resource (files) and Image models. Since these models are in the refinery gem, the best way is to use a decorator:

# app/decorators/models/refinery/image_decorator.rb
Refinery::Image.class_eval do
  include UpdateAssetReference
end

# app/decorators/models/refinery/resource_decorator.rb
Refinery::Resource.class_eval do
  include UpdateAssetReference
end

I created a concern here so we're not duplicating the content in both models:

# app/models/concerns/update_asset_reference.rb
module UpdateAssetReference
  extend ActiveSupport::Concern

  included do
    after_update :update_asset_references
  end

  def update_asset_references
    AssetUrlReplacer.new(self).update_asset_references
  end
end

In the concern, all we do is create the after_update hook and then pass in the instance of the file or image model to the AssetUrlReplacer, which is where all the real work is done.

# app/models/asset_url_replacer.rb
require 'nokogiri'

class AssetUrlReplacer
  attr_reader :asset

  def initialize(asset)
    @asset = asset
  end

  def asset_type
    if asset.is_a?(Refinery::Image)
      "image"
    else
      "file"
    end
  end

  def asset_name
    asset_type + "_name"
  end

  def references
    Refinery::PagePart::Translation
        .where("body like ?", "%#{asset.send(asset_name)}%")
  end

  def update_asset_references
    references.each do |translation|
      html = Nokogiri::HTML.fragment(translation.body)
      replace_attributes(html, "a", :href)
      replace_attributes(html, "img", :src)
      translation.body = html.to_html
      translation.save
    end
  end

  def replace_attributes(html, tag, attribute)
    if (tags = html.css(tag)).any?
      tags.each do |element|
        if element[attribute].include?(asset.send(asset_name)) 
          && element[attribute].include?("/system/")
          element[attribute] = asset.send(asset_type).url
        end
      end
    end
  end
end

Since the resource and image models use different method names to get the image url, we have to do a little meta programming. Other than that, the logic is rather straight forward. First we search for any references to the asset's file name. Then we loop through those records and search for either links or images that are referencing the file name. If there's a match, we update the html and save the record. We use nokogiri to parse the html so we don't have

That's it. Now whenever a client updates their files or images, they will be replaced in the content if they are referneced there.

What do you think? See any issues??


Navigation


Subscribe to our mailing list