Notes

Hyphenation via css now works!

Just add the “hyphens: auto” to your p’s. Also, do not forget all vendor-specific and adding a "lang=‘de/en/…’ tag to your body/html tag.

If you are using SASS/SCSS, here the mixin code

Rails Mountable Engine - Useful starting point with rspec, factory girl and friends

Since Ruby on Rails 3.1, engines were introduced. Unfortunatly, the generated code needs some love. I copied together some settings from various Stackoverflow posts regarding this topic.

Creating new engine

rails plugin new my_engine -T --mountable --dummy-path=spec/dummy

Creates a mountable (seperated) engine without test:unit.

Now we can add some useful gems to my_engine.gemspec:

# let's bundler take care of our file list
  s.files        = `git ls-files`.split("\n")  
  s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")

  s.add_dependency "rails", "~> 3.2"
  # s.add_dependency "jquery-rails"

  s.add_development_dependency "sqlite3"
  s.add_development_dependency "rspec-rails"
  s.add_development_dependency "shoulda-matchers", "~>1.0"#, "~> 3.0"
  s.add_development_dependency "factory_girl_rails"
  s.add_development_dependency "database_cleaner"
  # s.add_development_dependency "geminabox"  
  # if you are using your own gem server, take geminabox
end

Configuring Rspec

Adding also the spec_helper-File for RSpec to work with our dummy app.

# Configure Rails Envinronment
ENV["RAILS_ENV"] = "test"
require File.expand_path("../dummy/config/environment.rb",  __FILE__)

require 'rspec/rails'
require "factory_girl_rails"
require "database_cleaner"
require 'shoulda/matchers/integrations/rspec'

ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each {|f| require f }

RSpec.configure do |config|
  config.use_transactional_fixtures = false
    config.before(:suite) do
      DatabaseCleaner.strategy = :transaction
      DatabaseCleaner.clean_with(:truncation)
    end
    config.before(:each) do
      DatabaseCleaner.start
    end
    config.after(:each) do
      DatabaseCleaner.clean
    end
end

Testing

Now we can use “bundle exec rspec” to run our tests from within the dummy app. Later, when we want to do integration with our real app, we should start by adding our gem to the app’s Gemfile via file-path. Doing so, we are able to work at our engine and viewing the results right in our development app (see later in this article).

Working

Engines are IMO a very great idea. The engine’s directory structure is very similiar to a Rails dir structure. So placing all our stuff in the right place, will automatically load that files, e.g.:

  • my_engine/config/locales/en.yml
    • Custom I18n strings
  • my_engine/app/controllers/models/my_engine/model1.rb
  • my_engine/db/migrate/123123123_migration1.rb
  • my_engine/app/assets/stylesheets/my_engine.css.scs
    • This stylesheet can be included in our real App with “require my_engine” inside the application.css manifest

Namespacing everything

It is a good idea, to namespace the models, controllers etc. So make sure, the lib/feedmaker/engine.rb includes the isolated namespace:

module MyEngine
  class Engine < ::Rails::Engine
    isolate_namespace MyEngine
  end
end

Also, the models can be put into the same namespace, to avoid name collisions in the app later on.

# app/models/my_engine/model1.rb
class MyEngine::Model1< ActiveRecord::Base
  • Namespace the controller in the same way, putting everything under app/controllers/my_engine.
  • Same for views, which are located under app/views/my_engine/….

Routes

Add routes to config/routes in this way:

MyEngine::Engine.routes.draw do
  resources "feeds"
  root to: "feeds#index"
end

Due to our isolation, this resource will look for an feeds_controller inside the my_engine namespace.

Using the engine

Adding the gem to the Gemfile

# real applications Gemfile
#gem "my_engine"  # production
#gem "my_engine" ,path: '~/repos/my_engine'   # development
  • Copy over the migrations mit
    rake my_engine:install:migrations
    
    and migrate.
  • Add assets to the manifest files, if required
  • Mount the engine to a specific URL:
    # config/routes.rb
    mount MyEngine::Engine => "/engine"
    
  • This will map the “/engine” url to the root-path from MyEngine.
  • Every other resource is also located under this namespace, e.g. the feeds_controller from above can be reached from “/engine/feeds”

Pitfalls

  • The engine routes and url-helper are automatically namespaced to our engine’s namespaces, due to the “isolated Engine” directive. So do not use “my_engine_model1_path”, but “model1_path” in the engine’s views and controller.
  • An isolated mountable app normally render’s it own’s layout file (e.g. active admin has a own admin template). If you want to render from the Main-app’s template, change the “app/controllers/my_engine/application_controller.rb”, to inherit from the main app’s controller:
    class MyEngine::ApplicationController < ApplicationController
    
    • If you are in the situation, that you use url-helpers in the app’s layout (or included partials), than you will get an error, that these helpers can not be found. Prefix the helper’s call with “my_app” → “link_to my_app.search_path”

Conclusion

Looks a little bit messy, but after some setup everything works very nicely. Engines should be used, if there is a clearly distinguishable part of the app, which is independent from the rest. e.g. provision of assets, CMS-related features.

Installing and compiling Sphinx edge with Libstemmer support for German, French, etc....

Using and installing the Sphinx Search Daemon with a wide range of stemmer, not only English

cd mysrc
wget http://sphinxsearch.com/files/sphinx-2.0.3-release.tar.gz
tar xf sphinx-2.0.3-release.tar.gz 
cd sphinx-2.0.3-release/
cd libstemmer_c/
wget http://snowball.tartarus.org/dist/libstemmer_c.tgz
tar xf libstemmer_c.tgz 
mv libstemmer_c/* . -f
cd ..
./configure --with-libstemmer 
make
sudo make install

Then, the stemming can be activated for:

danish
dutch
english
finnish
french
german
hungarian
italian
norwegian
portuguese
romanian
russian
spanish
swedish
turkish

In thinking sphinx, the great Rails gem for managing Sphinx, sphinx.yml should be updated like this:

morphology = libstemmer_de, stem_en

So, more than one stemmer can be activated.

Installing Ruby (in a usable state)

Here are some thoughts on how to install Ruby with rvm and most features on a Debian/Ubuntu plattform:

apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core \
   zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 \
   libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake \
   libtool bison subversion 

bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

rvm install 1.9.3
rvm use 1.9.3 --default
rvm use @global   #using global gemset, which all gemsets inherit
gem install bundler pry awesome_print nokogiri thin capistrano guard \
    rspec rb-inotify guard-rspec spork 
# tmuxinator is also very nice if you are using tmux as a collab tool

We also install RVM and compile the latest 1.9.3 branch and setting it to our shell’s default. Aftwards I install some gems which i need in almost every project.

Apache Proxy Pass -> Custom 503 Error Document

When the underlying server is not reachable, Apache generates an ugly 503 Status message. To change that and inform the users that our server will be up soon again (due to a deployment):

Vhost/http conf

DocumentRoot /home/stefan/repos/empfehlungsbund/public
ProxyPreserveHost On

# if you running in production, maybe handling the assets by apache self, instead
# of thin/unicorn etc.
# ProxyPass /assets !


# Here the interesting lines:
ProxyPass /error-documents !
ErrorDocument 503 /error-documents/503.html
Alias /error-documents /home/stefan/repos/empfehlungsbund/public

# BTW: apache does not allow encoded slashes in URL,
# it will always generate 404. We used base64 encoded urls, which included this problem,
# to fix:
AllowEncodedSlashes on  

spree ecommerce: setting default locale to German and stuff

Spree is a fantastic eCommerce Rails-Engine. To change it, to speak German, one has to:

# Gemfile

# original repository not compatible at the moment, use fork
#gem 'spree_i18n', :git => 'git://github.com/spree/spree_i18n.git'
gem 'spree_i18n', :git => 'git://github.com/kares/spree_i18n.git'

# sh
bundle
rake spree_i18n:update_default
rake spree_i18n:sync
wget https://github.com/svenfuchs/rails-i18n/raw/master/rails/locale/de.yml -O config/locales/rails_de.yml

# config/initializers/spree.rb
if Spree::Config.instance
  Spree::Config.set(:default_locale => 'de')
end

this should get you started

bundle install results in infinite loop when ge requires active_support instead of activesupport and one of its dependencies rails

bundle install results in infinite loop when gem requires active_support and one of its dependencies rails.

s.add_runtime_dependency "active_support"
# change to:
s.add_runtime_dependency "activesupport"

Easy XML-Sitemap with Rails

Rails is too custom, to provide a generic sitemap as a gem for any situation. But it ist not too hard anyway! Here is a frame how to make one:

rails g controller sitemap

add the index action to sitemap-controller:

class SitemapController < ApplicationController
  def index
    respond_to do |f|
      f.xml
    end
  end
end

add the route (bonus points for restricting the format to xml)

# config/routes
  ....
  get "sitemap", :controller => :sitemap, :action => :index

Generate the sitemap with builder:

# app/views/sitemap/index.xml.builder 
xml.instruct!
xml.urlset(:xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9",
           "xmlns:xsi"=> "http://www.w3.org/2001/XMLSchema-instance",
            "xsi:schemaLocation"=>"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd") do
  # whatever logic your app has, e.g. static pages:
  @pages.each do |page|
    xml.url do
      xml.loc page_url(page)
      xml.lastmod page.updated_at.to_date.to_s(:db)
      xml.changefreq "monthly"
      xml.priority 0.3
    end
  end
end

Do not forget to add the sitemap to your robots.txt

Sitemap: http://www.mydomain.de/sitemap.xml

Easy ruby Tagcloud calculation and generation script from array

Yes I know, Tagclouds are deprecated. Nevertheless, sometimes required. To not always start from the beginning for this task, here an idea, how to make a tagcloud with variable formular, given as a block. This script does not generate the cloud by itself, but only calculates weights/sizes. But I also included a full usage example in the Readme.

Loading google maps v3 asynchronously/dynamically

If you want to load the GoogleMaps from Javascript, instead standard in-head-script-tag, you have to use a callback-function. Otherwise the maps is not loaded completly.

if (typeof google == "undefined") {
  jQuery.getScript("http://maps.google.com/maps/api/js?sensor=false&callback=open_google_map")
  // no success callback necessary, google can load our stuff-todo-function
} else {
   // if gmaps already loaded, we can just continue whatever else we want to do
   open_google_map()
}

This above snippet was used, to open up a googlemap in a modal/Lightbox-frame. In this way, GoogleMaps Javascript is only loaded, when the user really wants to see a map

formtastic - multiple forms with check boxes leads to double used html id

Problem: When creating muliple, similar structured forms with formtastic and using check boxes, then all the forms seems to be correlated: Checking a check_box of the check_boxes in one form, changes them in another, too. This is because they all used the same id for the check box input.

One can change this, by using the option “index => object.id” in the semantic form for:

semantic_form_for user, :index => user.id do |f|
 .... 
  f.input :options, :as => :check_boxes
end

In this way, the checkboxes get a prefix of the given index.
This option seems to be undocument, but can be found in the source code “choice_input_dom_id”.

Apache2 - transparent proxy to another Server domain/subdomain

Aim: Typing notes.pludoni.de in browser shows the same page as notes.it-jobs-und-stellen.de.

ProxyVia full
#ProxyPreserveHost On
#ProxyPass /assets
ProxyPass / http://notes.it-jobs-und-stellen.de/
ProxyPassReverse / http://notes.it-jobs-und-stellen.de/

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

e.g. this Blog is hosted unter notes.it-jobs-und-stellen.de but should be reachable as notes.pludoni.de. The problem was the option ProxyPreserveHost, which sended the domain notes.pludoni.de to the it-jobs-und-stellen.de-Server. The result was that this server delivered a wrong content, because he do not know anything about “notes.pludoni.de” (resulted in Plesk default page).

Mysterious double POST Requests in Rails 3.1 with ActiveAdmin

Make sure to change application.js to not require the whole tree, because the activeadmin jquery files will get loaded. This will mess up your normal behaviour, especially because they will not have a CSRF Token.

Rake: Install from local Gem-Dev-Dir as a remote global gem

For installing and updating a (private) gem that was developed locally, we defined a rake task.

def run(cmd)
  puts "CMD: #{cmd}"
  `#{cmd}`
end

desc "Install Gem auf pludoni Server"
task :install_remote => :build do
  file = `ls -t pkg/ |head -n 1`.gsub("\n"," ")
  server = ENV['SERVER'] || "root@pludoni.de"

  run "scp pkg/#{file} #{server}:/tmp/#{file}"
  run "ssh #{server} 'gem install -l /tmp/#{file}'"
end

Usefull, if you develop private gems that are not published at rubyforge, because they are very company/app-specific.

Win - using Autohotkey to switch to VM instead

When using Guake/Yakuake often, one easily tempted to use the key F12 in Windows… and nothing happens!
Now, I am using Autohotkey to open my VM instead – and there I usually have a beautiful yakuake with 256 colors, instead of an ugly cmd or putty.

SetTitleMatchMode 1
~F12::

IfWinExist Ubuntu
	WinActivate
return

One caveat: Doesn’t seems to work with Firebug, which takes the F12 key by itself.