Skip to content

Simple Smusher

I wrote a post on compressing images with TinyPNG, but that 500 image limit ran out fast!  So I decided to see if I could put together my own image optimization service.

After a little digging I found the image_optim gem.  It’s very nice and straightforward, and includes image_optim_pack gem which installed all the compression libraries as well – making for a very hassle free set-up.  After getting those installed and setting up my rails project, it really came down to a simple interface with one controller and view.

Controller:

require 'open-uri'
require 'image_optim'
require 'fileutils'
 
class WelcomeController < ApplicationController
  def index
    Dir.glob("public/tmp/*").
      select{|f| File.mtime(f) < (Time.now - (60*120)) }.
      each{|f| File.delete(f) }
 
    image = params[:i]
    extension = File.extname(image)
 
    acceptable_extensions = [".jpg", ".gif", ".png"]
 
    return @json = "{\"error\":\"only jpg, png, and gif can be resized\"}" if !acceptable_extensions.include?( extension.downcase )
 
    value = ""; 8.times{value  << (65 + rand(25)).chr}
    tmp_image = "public/tmp/#{value}#{extension}"
 
    open(tmp_image, 'wb') do |file|
      file << open(image).read
    end
 
    before_filesize = (File.size(tmp_image) * 0.001).floor
 
    image_optim = ImageOptim.new
    image_optim = ImageOptim.new(:pngout => false)
    image_optim = ImageOptim.new(:nice => 10)
 
    image_optim.optimize_image!(tmp_image)
 
    after_filesize = (File.size(tmp_image) * 0.001).floor
 
    tmp_image = "http://yourdomain.com/tmp/#{value}#{extension}"
 
    @json = "[{\"dest\":\"#{tmp_image}\",\"src_size\":#{before_filesize},\"dest_size\":#{after_filesize}}]"
  end
 
end

View:

1
<%= raw @json %>

I’m including open-uri to download the image to be compressed, image_optim to do the compressing, and fileutils to check the file size.

  1. I start with the Dir.glob to first delete any temporary images that are over 120 hours old.
  2. Then I grab the image from the url provided with the i param, and make sure it has the right extention
  3. Then generate a random string and create the temporary filename and save the image there
  4. Then I optimize the image and save over it
  5. Then I just write out the JSON I want by hand – I wrote it to resemble smush.it’s syntax

You will need to change “yourdomain.com” to your actual domain here, looking back I should have done that programmatically – but it’s not a big deal.

I have to whole project available on git hub for download.

Delete PHP files with errors in bash

I needed to go through and delete a lot of automatically generated PHP files that contained errors (pretty much all because they got cut off before the generator finished)  So I poked around and put together a code that will go though a designated directory and delete PHP files. I also added on to have it limited to a certain number, so I could monitor it.

1
2
3
4
5
6
7
find ... -exec bash -c '
    if php -l "$1" >&/dev/null; then
        echo "$1: pass" >&/dev/null;
    else
        sudo rm $1
        echo "$1: fail"
    fi' -- {} \; | tail -n +1 | head -50

And you can double check there are no files with errors using this command:

1
find ... -iname "*.php" -exec php -l {} \; | grep -i "Errors.parsing"

Sources:
http://stackoverflow.com/questions/28413735/how-do-i-limit-the-results-of-the-command-find-in-bash
http://superuser.com/questions/341601/find-with-exec-how-to-perform-multistep-conditional-action-on-each-file
http://superuser.com/questions/554855/how-can-i-fix-a-broken-pipe-error

Compress Rails project images

We were having some issues with slow loading times on our site, so I set out to compress all of our uploaded images.  Since we have thousands of images doing it by hand is not really an option, and plenty of the images are transparent PNGs.  So I enlisted the help of TinyPNG.

TinyPNG is an online application that compresses both PNG and JPG images, and preserves the alpha layer for PNG images, and it offers an API along with a ruby gem.  So you can use it to automate compressing images.  First start by installing the gem, and I also use the fastimage gem for this script so if you don’t have it install that too.

1
2
3
gem install tinify
gem install fastimage
rbenv rehash (if you're using rbenv)

Now you just need the script below:

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
require "tinify"
require 'fastimage'
Tinify.key = "YOUR API KEY"
 
i = 0
Dir.glob("public/system/rich/rich_files/rich_files/**/*.{jpg,png}") do |item|
  #only do 50 images at a time
  break if i == 10
 
  #ignorre directories and original files
  next if item == '.' or item == '..' or item.include? "original"
 
  #ignore iamges less than 50KB
  filesize = (File.size(item) * 0.001).floor
  next if filesize < 50
 
  #ignore images that are smaller thatn 100KB and over 600px large
  size = (FastImage.size(item).inject(:+))
  next if size >= 600 and filesize < 100
  i = i + 1
 
  source = Tinify.from_file(item)
  source.to_file(item)
 
  new_filesize = (File.size(item) * 0.001).floor
 
  puts "Converted: #{item}"
  puts "Original: #{filesize} Optimized #{new_filesize}"
end
 
puts "All done!"

Some things you’ll need to address to get this working are first, swap out “YOUR API KEY” with your actual API key, you can get one here. And if you’re not using the rich gem to handle image uploads you’ll need to change “public/system/rich/rich_files/rich_files” to whatever the location of your images is. The “**/*” part of the path will search all sub-directories and files in those sub-directories, and “.{jpg,png}” will only search jpg and png images.

The code will go through all the JPG and PNG files in your images directory, it will ignore all original versions, and then ignore files that are too small to bother with and then print out the files that were compressed along with their original and optimized sizes, then print out “All done!” once it’s finished.

Save it as tinify.rb and you can run it in the console with:

1
ruby tinify.rb

This will process 50 images at a time, and ignore images that I’ve set as too low to both with, so all images under 50KB are ignored, and images over 600x600px and lower than 100KB are ignored.  But this does pose a problem – images that aren’t reduced enough will still get re-reduced if you run the script multiple times.  For now I’m just monitoring it, and I’ll adjust it if too many images get caught.

Meal Replacement Bread Recipe ver 3

It’s time again to make some changes to my shake recipe, but it’s no longer a shake recipe!  As I’ve changed it so now I’m using it to bake bread 🙂  I was looking at the recipe…and it seemed to be getting weirder and weirder for a shake recipe, but after a little research I found it was perfect for a bread recipe!  It has various ingredients that are flour replacements, an egg, and applesauce is a apparently a good replacement for butter. So really the only difference is how I cook it.

So the changes to make this into bread are to ground up the apples into apple sauce and then cook it in the microwave for 5 minutes and that’s about it 🙂  To make sure it stays moist you need to put a cup of water in with it in the microwave. You can also add in some baking powder to make it nice and fluffy.

With this new version I’m looking to remove carbs from the recipe and bad macros to make it more healthy and better for weight loss.  I wrote up an article in the past on macro-nutrients which is my basis for making these changes.  I’m also changing back to Optimum Nutrition protein powder so it tastes a lot better :X  It does make it a bit more expensive, but I’m getting too tired of the chalky rice protein.

IngredientCarbsProteinFatFiberCalories$$/servingAmazon
Optimum Nutrition Protein3241.501201.07Buy
bob's flaxseed meal533.54600.11Buy
hooster powdered egg162.5800.56Buy
Dried Apple Rings (1/2 serving)15.502.5600.25Buy
bob's pea meal 9404500.02Buy
bob's almond meal (1/2 serving)3371.5400.21Buy
Totals3642.514.5124902.21

Bad Macros, before/after

  • 2/3 sat fat
  • 28/15.5 Sugar
  • 225/same cholesterol

At this point I think I’ve lost all faith in Fooducate so I’m removing it.  After finding this review of dymatise protein and this review of the same exact product.  I looked over the nutrition facts and ingredients, and they’re identical.  The image of the product as well.  So why is one entry a C and the other a B???  I have made thoughts of my making my own food rating system that’s completely automatic, and seeing results like these show up in fooducate make me think their more human method just isn’t that reliable.  One result that’s really vexed me on there is how they can give Tina’s Burritos a B, I just can’t believe that a .80 frozen burrito is better for you than protein powder.

I’m also considering ordering some Citrus aurantium powder, since it contains Synpehrine which is an active ingredient in Shred JYM.  And I’m also thinking about trying lemon peel and peppermint.

  1. Orange peel
  2. Lemon peel
  3. Peppermint

So my recommended spices would end up something like this:

  1. Cinnamon
  2. Matcha Powder
  3. Licorice Root
  4. Chia Seeds
  5. Orange Peel
  6. Lemon Peel
  7. Peppermint

But I’m thinking of saving that for the next round, at this point I’m feeling like there’s not a lot of more advancements I can make while sticking to my original goals.  To make it better for weight loss I’d need to remove more carbs, but all the carbs are from the fruit and vegetable ingredients – which having those was kind of the whole point in the first place. The only ingredient in here that’s not an appetite suppressant is the pea meal, but after doing a lot of research there’s not many vegetables that are as low carb as the pea flower and none that are as cheap.  So I might be reaching a final version 🙂

Simple reference for creating a Rails migration

I keep forgetting the rails generator syntax for migrations :X  I can never find just a quick easy guide for it – so I thought I’d write one up!

Instructions

  1. cd …
    obviously first make sure your in the main project folder in the console
  2. export RAILS_ENV=environment
    Also, make sure you’re in the right environment, you will probably want to make the change first in your test or development environment and then put it to production
  3. rails g migration AddColumnToModel column_name:column_type
    Here’s the main part, on  older versions rails g migration will be script/generate migration.  The “AddColumnToModel” syntax is important and should relate to the migration you’re making like “AddEmailToUser” and then at the end you can add a “column_name:column_type” reference for each column you want to add, for instance with the last example you would put “email:string
  4. rake db:migrate
    run the migration and add those columns to the database!  You’ll probably want to review the migration that was generated first though – it’ll be the latest file in the …/db/migrate directory

AND MAKE SURE TO RESTART YOUR SERVER AFTER MAKING A CHANGE

Column Types

  • binary: for files/ data blob
  • boolean: true/false
  • date: only date: (year, month, day)
  • datetime: date + time
  • decimal: precise decimal numbers – for when math neds to be accurate
  • float: decimal numbers
  • integer: whole numbers
  • string: 255 max alphanumeric characters
  • text: unlimited alphanumeric characters
  • time: only time (hours, minutes, seconds)
  • timestamp: same as datetime

 

And, if you make a mistake a quick way to go back and fix it is “rake db:migrate:redo” that will undo your last change, and re-run it.

Also, if you want to only run “rake db:migrate:up VERSION=20090408054532”

In a pinch you can also run a migration by hand in the console with the following format:
ActiveRecord::Migration.add_column :table, :column_name, :column_type

References:
http://stackoverflow.com/questions/11889048/is-there-documentation-for-the-rails-column-types
http://stackoverflow.com/questions/15162055/rails-generate-migration
http://stackoverflow.com/questions/7694487/ruby-on-rails-how-can-i-revert-a-migration-with-rake-dbmigrate