Many To Many associations with multiple databases

Sometimes you need to use multiple databases in your Rails projects. Usually when some data must be shared between different applications. When this happens you usually have some models in a shared database, and some other models in the specific application database. This can be easily done using the establish_connection method in the shared models to tell them they have to connect to a different database.

However, when you need some interaction between those shared models and the models of your specific application, like a has_many, :through association, some problems arise. The typical Many To Many association uses an intermediate database table that links the relation between two models, and allows you to add some extra information on that relation. When navigating through the association, Rails tries to make an SQL query that joins the model with this intermediate table. For example, imagine you have a Team model, which has many Players, but a player can also be on more than one team. We use an intermediate model TeamPlayers (and we can also use it to save the role of that player into that team, for example). You would have those three tables:

  • teams
  • players
  • teams_players

When asking for the players of a given Team, Rails would do something similar to this:

SELECT "players".* FROM "players" INNER JOIN teams_players" ON "players".id = "teams_players".player_id WHERE "players".team_id = 1

Where 1 is the id of the team you asked for. This [obviously] works perfectly fine when everything is in the same database, and it's as efficient as the SQL database manager you're using. What happens, however, when we have the Player model in another database? It will miserably fail because Rails will try to join with a table that doesn't exist.

 

Unfortunately, there's no efficient way to solve this problem, that is, using SQL, as you can't work with tables from different databases. However, there's a rather elegant solution that Brian Doll cared to implement as a gem a while ago. As indicated in the GitHub readme, you just have to use a has_many_elsewhere relation instead of the usual one, and make sure that the model referenced has the connection established to the shared database. And that's all.

The magic donde behind the scenes is pretty simple: this gem just replicates the same methods that the ActiveRecord::Base class does in the has_many method call, changing the failing unique SQL calls to double SQL calls, one for each database, fetching the intermediate models first, and then fetching the remote models using those ids.

This method is not perfect, as probably not all the goodness of the original association can be done with it, but for simple scenarios is more than enough.

Reprocessing all the assets of your Ubiquo Application

When in your project you have defined custom formats for your images in config/initializers/ubiquo_config.rb file, if you modify any of these formats, you must reprocess all the assets you have uploaded. If you don't do it, they won't be in the correct formats.

If you don't have a huge amount of assets, you can create a migration adding this line to its "up" method:

        Asset.all.each { |a| a.resource.reprocess! }

On the other hand, if you have lots of assets, you should do it as a job, not a migration.

Posted by davidrr 

Comments [0]

Setting up the locales of your Ubiquo Application

It's sometimes required to build a multilanguage web application. For those cases, Ubiquo provides a plugin that uses the Rails I18n infrastructure to do so. In this post I'll explain you how to set up the default locale for your application and how to tell Ubiquo how many languages you want to have support for.

By default, new Ubiquo projects are configured to use English (default), Catalan and Spanish languages. However, you may want to support more languages, or have a different default language instead of English. Both parameters are changed in the config/environment.rb file, using the Ubiquo Config framework:

Ubiquo::Config.set(:default_locale, :ca) will use Catalan as the default locale.

Ubiquo::Config.set(:supported_locales, %w[ca]) will set the supported locales of your application.

Keep in mind that the default locale must be one of the locales set up in the supported locales array. Also, be wary on changing those parameters outside of the environment file, since it might led to strange behaviours.

Feel free to check out the Ubiquo I18n Configuration Guide for information on how to manage translatable models.

Filed under  //   ubiquo_i18n  

Creating your own Ubiquo Job Managers

Sometimes can be useful to create different managers. An example of this situation is when you want to run different kind of jobs in different circumstances.

Ubiquo Jobs provides a default manager which will get ActiveJob jobs depending on priorities and schedule times:

def self.get(runner)
  recovery(runner)
  candidate_jobs = job_class.all(
    :conditions => [
      'planified_at <= ? AND state = ?',
      Time.now.utc,
      UbiquoJobs::Jobs::Base::STATES[:waiting]
    ],
    :order => 'priority asc'
  )
  job = first_without_dependencies(candidate_jobs)
  job.update_attributes({
      :state => UbiquoJobs::Jobs::Base::STATES[:instantiated],
      :runner => runner
    }) if job
  job
end

The job_class variable defaults to UbiquoJobs::Jobs::ActiveJob. If you want to make your own manager to handle special jobs, or change the way the jobs are picked, the best way to do so is to implement your own manager. A nice rails-like way to do that is include them in the lib/ folder of your ubiquo project. The class you should inherit from is UbiquoJobs::Managers::ActiveManager. If you wanted the manager to just pick up a specific subclass of ubiquo jobs, it would suffice to reimplement the self.job_class class method to return your own kind of job:

def self.job_class
  UbiquoJobs::Jobs::YourJobClass 
end

However, there’s a better way to do this. For this special case, the default UbiquoJob class provides a special member which stores the job’s class name, allowing you to select all objects subclasses of ActiveJob by its classname. For example, imagine you have a kind of job for special tasks that you know for sure will take a long time to complete. Seems reasonable to have a different manager to handle those jobs. You would create a new job in the file app/jobs/very_long_job.rb:

class VeryLongJob < UbiquoJobs::Jobs::ActiveJob
  def do_job_work
    #Do what needs to be done here
    return 0
  end
end

Then you could create a manager that handles only those kind of jobs by implementing your own subclass of the UbiquoJobs::Managers::ActiveManager class:

module JobManagers
  class VeryLongJobManager < UbiquoJobs::Managers::ActiveManager
    def self.get(runner)
      recovery(runner)
      candidate_jobs = job_class.all(
        :conditions => [
          'planified_at <= ? AND state = ? AND type = ?', 
          Time.now.utc,
          UbiquoJobs::Jobs::Base::STATES[:waiting],
          'VeryLongJob'
        ],
        :order => 'priority asc'
      )
      job = first_without_dependencies(candidate_jobs)
      job.update_attributes({
          :state => UbiquoJobs::Jobs::Base::STATES[:instantiated],
          :runner => runner
        }) if job
      job
    end
  end
end

The code is exactly the same as the default ActiveManager class, but the finder will take an extra parameter, 'VeryLongJob', to indicate that only the ActiveJob objects that are of the subclass VerylongJob should be taken.

After that, you need to modify the task that calls the workers so it takes your manager, or create a new task that will run your manager. The default task that will start a worker looks as this:

desc "Starts a new ubiquo worker"
task :start, [:name, :interval] => [:environment] do |t, args|
  options = {
    :sleep_time => args.interval.to_f
  }.delete_if { |k,v| v.blank? }
  UbiquoWorker.init(args.name, options)
end

This uses a special configuration parameter to determine the manager to use. This configuration option is stored in Ubiquo::Config.context(:ubiquo_jobs), the name of the configuration option is :job_manager_class, and takes the manager class as a value. So in order to create a task that will use your manager, you should create a new task like this one:

desc "Starts a new ubiquo worker"
task :start_very_long_jobs, [:name, :interval] => [:environment] do |t, args|
  options = {
    :sleep_time => args.interval.to_f
  }.delete_if { |k,v| v.blank? }
  Ubiquo::Config.context(:ubiquo_jobs).set(:job_manager_class, JobManagers::VeryLongJobManager)
  UbiquoWorker.init(args.name, options)
end

Your should call this task like this (assuming it’s on the same namespace as the default task):

rake ubiquo:worker:start_very_long_jobs[name,interval]

Filed under  //   ubiquo_jobs  

How to override an Ubiquo::Connector

Suppose you want to change the way some uhooks work for one of your models. You need to override this hook so it will do something different. The way to do it is extending the default I18n connector from, say, UbiquoMedia (overriding other plugin's connectors its done in the same way) and provide a custom method. Here's how to do it:

Place an extension somewhere in lib/. In this case we will use lib/ubiquo_media/connectors/my_i18n.rb.

Inside you copy the affected method's object hierarchy so our method overrides the original one. You must use its full class hierarchy while extending from I18n cause extending with "class MyI18n < I18n" alone clashes with rails I18n module raising an "Uncaught exception: superclass must be a Class (Module given)" error.

module UbiquoMedia
  module Connectors
    class MyI18n < UbiquoMedia::Connectors::I18n
      # Custom implementation
      
      def uhook_after_update
      # do stuff...
    end
  end
end

Then you require it, usually from a ubiquo_connectors.rb in lib/ like this:

require 'ubiquo_media/connectors/my_i18n'

Finally you tell Ubiquo::Config to use your connector instead of the original one, updating the ubiquo connector configuration (in config/initializers/ubiquo_config.rb), from this:

Ubiquo::Config.context(:ubiquo_media).set(:connector, :i18n)

to this:

Ubiquo::Config.context(:ubiquo_media).set(:connector, :my_i18n)

From now on, your method will be the one running in place of the original one.

Posted by Dani Donisa 

Comments [0]

How to make Lighthouse recognize you as a committer

In Lighthouse, traditionally all the commits were reported under the account that created the token in Github. Although a solution was made available some time ago, I don't think it has been very publicized, so here is how to do it.

You just have to go to edit your Profile and find the Committer Names section. Here you will be able to enter a name for every project you've been added to. Notice that the name is the full name in the commit, not the email or github nickname. As a picture is worth a thousand words, here is a screenshot showing an example from my account.

 

Ubiquo_1314183866825

Filed under  //   lighthouse  

compass watch internals and error logs

This morning I was looking for how compass internally works and didn't find a lot of information about the internals, so I'll explain some of the basics that satiated my curiosity.

When you initialize compass in a project by doing

$> compass init rails project

You will see that when you change your scss files, your css files are automatically refreshed. Both sass and compass have a watch utility, and I thought one of these was fired on background to perform the update when necessary. But looking at the running processes I could not find the one that was doing it.

In fact, this action is performed by a third mechanism: sass includes a rack middleware to do it in every petition: http://sass-lang.com/docs/yardoc/Sass/Plugin/Rack.html which in turn is initialized by Compass.

You can also see the Compass watch implementation as a File System State Monitor to verify that is "simply" an independent process, and that does not play with your requests.

In fact, everything started because, due to an error in my scss file, nothing was being rendered properly. As a Compass noob I was expecting the compile error to be shown in my rails log, or even in a separate error log file. But there was no trace of the error, and I couldn't find a reference of where it should be. Well, Firebug was telling me that the generated css had no rules, but that doesn't mean it's empty! I finally "discovered" that Sass puts your trace in the generated css, as a comment.

 

Filed under  //   compass   internals   logs   sass  

Increment page view counter even if page is cached


Adding cache to web pages is usually a good practice; however, making all the content static prevents you from adding some functionalities. A common case is when you need to increment a counter every time a cached resource is viewed.

If you have a model which is being observed by a Rails Sweeper, a simple update_attributes to increment the counter by 1 will expire all the cached object. And it will do it every time it is viewed. This behavior not only makes caching obsolete, but drastically decreases the overall performance of the site on each page hit.

There are some solutions to this problem, the most efficient probably would be using an alternate system to store stats like a key/value database (Redis, MongoDB, etc), but an easier approach would be to update the database directly to bypass the observer and skip the cache expiration. This can be done with the ActiveRecord method update_counters.

There are two problems with this solution. The first is obvious, with each page view the database will be hit, reducing a bit the performance. The second is that the cache has to be expired periodically to reflect the counter increments. But for most average sites it's a good solution.

Ubiquo Widgets expiring cache policy

With ubiquo it's quite easy to expire cached data of a widget after a given time. By defining the expires_in clause in the configuration policy file ("config/initializers/design_cache.rb") we can set the time to live (in seconds) of the cached object. Each widget should have it's own expiring configuration if needed.

Another way of expiring cached data is using a system cron job.

 

Filed under  //   ruby caching update_counters rails  

Adding new media selectors in ajax calls

To load a form (or some fields for a form) in ajax call when some media selectors will be generated, there is a instance var that we must set to prevent the rest of media selectors stop working.

We just need to set

@is_media_included = true

at controller that will attend ajax call, before the media selector routine is called

Posted by igallego 

Comments [0]

New javascript and stylesheet helpers

Few weeks ago, we did a change in helpers used by ubiquo to load stylesheet and javascript files. Our idea was to simplify the inclusion of plugin files and "railsify" the sintax of these helpers.

Here are some examples:

With ubiquo_stylesheet_link_tag :defaults the following files will be loaded: ubiquo.css, ubiquo_application.css, lightwindow.css, listing.css, red.css and special IE files, located in public/stylesheets/ubiquo/, and all the files installed by plugins located in public/stylesheets/ubiquo/plugins/.

For the basic work with ubiquo the option :defaults is essential. In addition, if we want change the color stylesheet file, we can call helper with the option :color, ex: ubiquo_stylesheet_link_tag :defaults, :color => 'blue'. With this option, we are thinking to add support for custom ubiquo themes in the future. 

If we only want to load a single file located in ubiquo folder, we can do this: ubiquo_stylesheet_link_tag 'login'. These are the basic options for the helper, any other option will be used in the internal call to the rails helper: stylesheet_link_tag.

ubiquo_javascript_include_tag works very similar of stylesheet helper. With :defaults option, the files ubiquo.js and lightwindow.js will be loaded. These files are located in public/javascripts/ubiquo. The files installed in public/javascripts/ubiquo/plugins/ are loaded too. We can also specify other files, ex: ubiquo_javascript_include_tag 'ubiquo-calendar', :defaults. And, as in the other helper, the rest of options will be used in the internal call to the rails helper: javascript_include_tag.

We hope that these changes are useful.

Posted by Toni Reina 

Comments [0]