A colleague and I are working in different projects that share some models. So, we are sharing the models through a git submodule.
Additionally, we'd like to be able to also share migrations:
In this way, my colleague's migrations would be in the folder db/migrate/other_db of my project.
How can I configure rails migrations to also run the migrations in this extra folder?
In your config file (config/application.rb for all environments or config/environments/$(environment).rb only for particular environment) add this line:
config.paths['db/migrate'] += 'db/migrate/other_db'
And if you want to change default 'db/migrate' path (config.paths['db/migrate'] is an array with one string 'db/migrate' by default), do this:
config.paths['db/migrate'] = ['db/my_migrate']
Here are default config.paths, which we also can change:
"app" => ["app"],
"app/assets" => ["app/assets"],
"app/controllers" => ["app/controllers"],
"app/helpers" => ["app/helpers"],
"app/models" => ["app/models"],
"app/mailers" => ["app/mailers"],
"app/views" => ["app/views"],
"lib" => ["lib"],
"lib/assets" => ["lib/assets"],
"lib/tasks" => ["lib/tasks"],
"config" => ["config"],
"config/environments" => ["config/environments"],
"config/initializers" => ["config/initializers"],
"config/locales" => ["config/locales"],
"config/routes" => ["config/routes.rb"],
"db" => ["db"],
"db/migrate" => ["db/migrate"],
"db/seeds" => ["db/seeds.rb"],
"vendor" => ["vendor"],
"vendor/assets" => ["vendor/assets"],
"vendor/plugins" => ["vendor/plugins"],
"config/database" => ["config/database.yml"],
"config/environment" => ["config/environment.rb"],
"lib/templates" => ["lib/templates"],
"log" => ["log/development.log"],
"public" => ["public"],
"public/javascripts" => ["public/javascripts"],
"public/stylesheets" => ["public/stylesheets"],
"tmp" => ["tmp"],
Update for Rails 5/6;
Rails 5 recommends setting additional migration paths in your config/database.yml file. It's very easy, see this example;
development:
migrations_paths:
- "db/migrate/other_db"
- "db/migrate/something_else"
ActiveRecord::Migrator.migrations_path= will be deprecated in Rails 6.
based on the answer by Swanand, we can write a migration to load the migrations in an external dir:
class MigrateMetadata < ActiveRecord::Migration
MIGRATIONS_PATH='db/migrate/metadata'
def self.up
Dir["#{MIGRATIONS_PATH}/[0-9]*_*.rb"].
sort.map{|filename|require filename}.flatten.
each{|class_name| const_get(class_name).up}
end
def self.down
Dir["#{MIGRATIONS_PATH}/[0-9]*_*.rb"].sort.reverse.
map{|filename|require filename}.flatten.
each{|class_name| const_get(class_name).down}
end
end
By the way, if you are building a gem to work with Rails, you can place a block like the following in your rail tie to add the gem's own migrations.
root = ... # the path to your gem
initializer :append_migrations do |app|
unless app.root.to_s.match root
app.config.paths["db/migrate"] << File.join(root, 'db/migrate')
end
end
There is no need to copy migrations from your gem with generators if you use this technique.
You can make a method to yield the root directory of your gem with some thing like this...
module MyGemName
def root
File.expand_path '../..', __FILE__
end
module_method :root
end
... in the file lib/my_gem_name.rb in your gem.
I do not know of a very clean way to do it, but the code that runs migrations looks something like:
#migrations ||= begin
files = Dir["#{#migrations_path}/[0-9]*_*.rb"]
migrations = files.inject([]) do |klasses, file|
version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
Where,
#migrations_path = 'db/migrate'
So if you change this to read from config file instead, it may work out in your favour. But as I said, this is definitely not a very clean way to do it.
Just add this initializer to your lib/engine.rb:
initializer 'your_engine_name.migrations' do |app|
config.paths['db/migrate'].expanded.each do |expanded_path|
app.config.paths['db/migrate'] << expanded_path
ActiveRecord::Migrator.migrations_paths << expanded_path
if Rake.application.top_level_tasks.empty?
ActiveRecord::Migration.check_pending! if ActiveRecord::Migrator.needs_migration?
end
end
end
Related
I want to use this gem (sitemap_generator)
sitemap_generator
To create my sitemap xml file for my site.
So i create sitemap.rb inside config folder
Then i put this code inside
require 'rubygems'
require 'sitemap_generator'
SitemapGenerator::Sitemap.default_host = 'https://xxxx.com/'
SitemapGenerator::Sitemap.create do
# add '/home', :changefreq => 'daily', :priority => 0.9
# add '/contact_us', :changefreq => 'weekly'
add '/'
add '/signup'
add '/login'
Activity.find_each do |activity|
add activity_show_path(activity.id), :lastmod => activity.created_at
end
end
SitemapGenerator::Sitemap.ping_search_engines # Not needed if you use the rake tasks
But when i run
ruby config/sitemap.rb
I always got this
uninitialized constant Activity (NameError)
So how can i fixed this
(I guess the problem from the model)
Thanks!
I always run it through the rake task, try this:
rake sitemap:refresh:no_ping
It's possible the rake task does the magic to make the application code available when that's running.
Update: probably a duplicate of Rails sitemap_generator Uninitialized Constant? (sorry I should have looked first)
In my Rails application development, I would like to write a script to create a new database and a table in the new database (I am using MySQL), so that I can later run the script like following:
rails runner db/scripts/data_mover.rb
But I do not know how to do it in ruby code or Rails way...Anyone can provide some hint or sample?
write the code in a class, and place the file under app/models, such as `app/models/data_mover.rb'
class DataMover
def self.run
ActiveRecord::Base.connection.execute("CREATE DATABASE somedatabase")
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "myuser",
:password => "mypass",
:database => "somedatabase"
)
ActiveRecord::Base.connection.create_table :sometable do |t|
#...
end
end
end
Then you can run rails runner 'DataMover.run' to create databases and tables.
My webapp needs to encrypt its session data. What I setup is:
config/initializers/encryptor.rb:
require 'openssl'
require 'myapp/encryptor'
MyApp::Encryptor.config[ :random_key ] = OpenSSL::Random.random_bytes( 128 )
Session.delete_all
app/models/session.rb:
require 'attr_encrypted'
class Session < ActiveRecord::Base
attr_accessible :session_id, :data
attr_encryptor :data, :key => proc { MyApp::Encryptor.config[ :random_key ] }, :marshal => true
# Rest of model stuff
end
That all works great, and keeps the session data secured. Here's the problem: when I run my custom rake tasks it loads the initializer and clears all the sessions. Not good!
What can I put in my initializer to make sure it ONLY runs for the webapp initialization? Or, what can I put in my initializer to make it NOT run for rake tasks?
Update: OK, what I've done for the moment is add MYAPP_IN_RAKE = true unless defined? MYAPP_IN_RAKE to my .rake file. And then in my initializer I do:
unless defined?( MYAPP_IN_RAKE ) && MYAPP_IN_RAKE
# Web only initialization
end
Seems to work. But I'm open to other suggestions.
You might make a modification to your application in `config/application.rb' like this:
module MyApp
def self.rake?
!!#rake
end
def self.rake=(value)
#rake = !!value
end
Then in your Rakefile you'd add this:
MyApp.rake = true
It's nice to use methods rather than constants since sometimes you'd prefer to change or redefine them later. Plus, they don't pollute the root namespace.
Here's a sample config/initializers/rake_environment_test.rb script:
if (MyApp.rake?)
puts "In rake"
else
puts "Not in rake"
end
The programmable nature of the Rakefile affords you significant flexibility.
There is another work around:
unless ENV["RAILS_ENV"].nil? || ENV["RAILS_ENV"] == 'test'
When you launch with rake your ENV["RAILS_ENV"] will be nil. The test for 'test' is to avoid to run when using rspec.
I know that is reckon to use Rails.env but it return "development" when it is not initialised.
http://apidock.com/rails/Rails/env/class
# File railties/lib/rails.rb, line 55
def env
#_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"]
|| ENV["RACK_ENV"] || "development")
end
I am getting this error when I am using
acts_as_ferret :fields =>[:competitor], :remote => true
NameError in PartController#index
uninitialized constant PartController::Competitor
My Model
class Competitor < ActiveRecord::Base
validates_presence_of :fee_earner_id, :notes
belongs_to :fee_earner
belongs_to :country
belongs_to :state
belongs_to :user
acts_as_ferret :fields =>[:competitor], :remote => true
end
My controller
class PartController < ApplicationController
def index
#proscribeds = Competitor.paginate(:all,
:order => sort ,
:page => params[:page],
:per_page => 70 )
end
end
Its working fine in localhost but when I deploy it in the server than I get this error.
act_as_ferret is working good with other models. I don't know why this is not working with only Competitor model.
These might seem like simplistic suggestions, but here's what comes to mind:
Do you have the same OS on localhost and the server? I've been burned by little discrepancies like the differences between how Unix and Windows handle mixed case pathnames.
Have the most updated versions of all your files been moved? Can you do a diff and ensure that there isn't a missing config file or environment variable or something?
I know those suggestions aren't really Rail/Ruby-specific, but I've found that little configuration problems tend to give me more headaches than actual code errors do.
Good luck!
This could be a problem with the bin/ferret_server file on the remote drb server you are running. What it is complaining about is that it knows nothing about the PartController::Competitor model. This is because the ferret-server does not eager load all the Rails classes like Rails does by default.
I had a similar issue and the solution was to also require the Rails config/environment file as well as the Rails config/application file.
Something like this:
begin
ENV['FERRET_USE_LOCAL_INDEX'] = 'true'
if env = $ferret_server_options['environment']
ENV['RAILS_ENV'] = env
end
# determine RAILS_ROOT unless already set
root = File.expand_path(determine_rails_root)
begin
require File.join(root, 'config', 'application')
# Also require environment to eager load Rails classes
require File.join(root, 'config', 'environment')
rescue
puts "Error booting your rails app at #{root}: #{$!}\n#{$!.backtrace.join("\n")}"
raise $!
end
puts "Rails.root: #{Rails.root}"
puts "Rails.env: #{Rails.env}"
require 'acts_as_ferret/server/server'
ActsAsFerret::Server::Server.new.send($ferret_server_action)
rescue Exception => e
$stderr.puts(e.message)
$stderr.puts(e.backtrace.join("\n")) if $ferret_server_options['debug']
exit(1)
end
I am hosting my assets on s3. In production, rails is looking for /javascripts/cache/all.js and /stylesheets/cache/all.css. I'm using a plugin to swoop the public directory over to s3 when I deploy with cap. The problem is that rails doesn't create these cache files until they are requested for the first time, so they aren't around during deployment when I transfer the public dir. Is there an easy way to force the creation of these files during deployment?
The actual rails module that creates the cache files is ActionView::Helpers::AssetTagHelper which you may be able to re-use to create the cache files during deployment. The following worked okay for me:
require 'action_view'
class AssetCacheWriter
include ActionView::Helpers::AssetTagHelper
def write
write_asset_file_contents(File.join(JAVASCRIPTS_DIR, "all.js"), compute_javascript_paths([:all], true))
write_asset_file_contents(File.join(STYLESHEETS_DIR, "all.css"), compute_stylesheet_paths([:all], true))
'standard_all')
end
end
Both those write_asset_file_contents calls I've lifted from the rails code. The _DIR constants are defined in the included module and the true parameters in the compute_xxx_paths calls indicate that all files should be included recursively.
I created a simple cap task that just wrote the files out:
namespace :sample
task :assets do
AssetCacheWriter.new.write
end
end
I use this in a rake task file. Saves you from creating an additional class and you keep it DRY because you're using the values from the stylesheet_link_tag / javascript_include_tag
desc "Clears javascripts/cache and stylesheets/cache" task :clear => :environment do
puts "Clearing javascripts/cache and stylesheets/cache"
FileUtils.rm(Dir['public/javascripts/cache_[^.]*']) # use :cache => 'cache_all.js' in stylesheet_link_tag
FileUtils.rm(Dir['public/stylesheets/cache_[^.]*']) # use :cache => 'cache_all.css' in javascript_include_tag
end
desc "Recreate the javascripts/stylesheets cache."
task :generate => [:environment, :clear] do
puts "Recreate the javascripts/stylesheets cache"
ActionController::Base.perform_caching = true
app = ActionController::Integration::Session.new
app.get '/'
end
If you're using Engine Yard Cloud, this is what I did:
added a file /lib/tasks/assetcache.rake
require 'assetwriter'
namespace :assetcache do
desc "Clears javascripts/cache and stylesheets/cache"
task :clear => :environment do
puts "Clearing javascripts/cache and stylesheets/cache"
FileUtils.rm(Dir['public/javascripts/cache_[^.]*'])
# use :cache => 'cache_all.js' in stylesheet_link_tag
FileUtils.rm(Dir['public/stylesheets/cache_[^.]*'])
# use :cache => 'cache_all.css' in javascript_include_tag
end
desc "Recreate the javascripts/stylesheets cache."
task :generate => [:environment, :clear] do
puts "Recreate the javascripts/stylesheets cache"
AssetCacheWriter.new.write
end
end
then I added /lib/assetwriter.rb
require 'action_view'
class AssetCacheWriter
include ActionView::Helpers::AssetTagHelper
def write
write_asset_file_contents(File.join(JAVASCRIPTS_DIR, "cache/all.js"), compute_javascript_paths([:all], true))
write_asset_file_contents(File.join(STYLESHEETS_DIR, "cache/all.css"), compute_stylesheet_paths([:all], true))
end
end
then I added to /deploy/after_restart.rb
run "cd #{release_path} && bundle exec rake assetcache:generate"
I found that include order mattered (so, for example, jQuery is included before other scripts) and so used the below approach instead. Also allows for the use of :defaults.
First lib/asset_pacakger:
require 'action_view'
class AssetCacheWriter
include ActionView::Helpers::AssetTagHelper
include ActionView::Helpers::TagHelper
def write
ActionController::Base.perform_caching = true
stylesheet_link_tag *ApplicationController::STYLESHEET_INCLUDES
javascript_include_tag *ApplicationController::JAVASCRIPT_INCLUDES
end
end
Then scripts/package_assets:
#!/usr/bin/env ruby
RAILS_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
Dir.chdir(RAILS_ROOT)
RAILS_ENV = ENV['RAILS_ENV'] || 'development'
puts "Bundling assets for environment #{RAILS_ENV}"
require File.join('config', 'environment')
AssetCacheWriter.new.write
Then a cap task:
desc "Package assets"
task :package_assets do
%x[#{Dir.getwd}/script/package_assets]
%x[svn commit #{Dir.getwd}/public/javascripts/defaults.js #{Dir.getwd}/public/stylesheets/defaults.css -m "(capistrano) packaging assets to trunk"]
logger.info "packaged assets to trunk"
end
In Rails 3.0.7 none of the above approaches seemed to work. I got many errors about config being undefined.
After getting my hands dirty looking over the Rails code, I came up with this solution that is working for me.
lib/tasks/cache_assets.rake
require 'asset_packager'
desc "cache assets"
task :cache_assets do
puts "-----> Caching Assets"
AssetCacheWriter.new.write
puts "-----> Done!"
end
And then lib/asset_packager.rb
require 'action_view'
class AssetCacheWriter
include ActionView::Helpers::AssetTagHelper
include ActionView::Helpers::TagHelper
def config
ApplicationController
end
def controller
ApplicationController
end
def write
ActionController::Base.perform_caching = true
# You can replace these with your app's cached assets. They should look the same
# as in your view templates.
stylesheet_link_tag :vendor, :defaults, :cache => 'cached/all'
javascript_include_tag :bd_defaults, :'vendor-head', :cache => 'cached/defaults_vendor'
end
end
Then to execute it, just run rake cache_assets