Problem
I am trying to use phony tasks in Rake, to prevent forced rebuild of my FileTasks.
(Exerpt from ruby/lib/rake/phony.rb on GitHub)
# Defines a :phony task that you can use as a dependency. This allows
# file-based tasks to use non-file-based tasks as prerequisites
# without forcing them to rebuild.
Despite using the :phony task, my FileTasks still rebuild. I have made an example demonstrating this.
Example Scenario
We need to build final.file from source.file, using an :intermediate task.
It should only rebuild if final.file is out of date with source.file. However, listing :intermediate as a prerequisite, forces a rebuild every time.
Using rake/phony and the :phony task as a prerequisite is supposed to fix that, but doesn't.
Example Code:
require 'rake/phony'
task :default => 'final.file'
file 'final.file' => ['source.file', :intermediate] do |f|
touch f.name
end
# Marked as phony, so should not force FileTask to rebuild
task :intermediate => [:phony] do |t|
puts "#{t.name} task"
end
Problem Demonstation
$ touch source.file # Create the source file
$ rake # Run Rake to build 'final.file'
intermediate task
touch final.file
$ rake # Rake should NOT rebuild, but does. <<<<<<<<
intermediate task
touch final.file
Version
As you can see, I am using a more recent version of Rake than the one which introduced phony tasks (which was 0.9.4), so :phony should work.
$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]
$ rake -V
rake, version 10.4.2
I guess rake/phony is a gem? It's not something built into rake, I don't think, I've never seen it before.
Maybe this one? https://github.com/floere/phony But that seems like it might have nothing to do with what you're talking about, so maybe that's not it, and you are using something else.
Whatever it is you are using, you could try posting an Issue on their github, or otherwise asking them a question if their readme has other preferred methods of receiving questions. It might be a bug in that gem, or maybe that gem doesn't do what you expect.
It's likely few readers here are familiar with that gem, and at any rate don't have the interest to read your lengthy post/question where you paste a huge chunk of source code.
Since we don't know what it is or what it's supposed to do, we don't really have a way to know if it's a bug in that gem or if you're doing it wrong, and while there are lots of people on the internet who enjoy helping out by answering a targetted question on something they are familiar with, there are very few who enjoy going through a vaguely worded "Am I doing something wrong or is it broken?" question that pastes in gigantic swathes of source material, on something they are not familiar with.
I think lib/rake/task.rb has a broken definition of "timestamp" method.
Here is what I think it should be:
def timestamp # on Task
prerequisite_tasks.collect { |pre| pre.timestamp }.max || Time.now
end
I found this information in this page:
http://blog.zenspider.com/blog/2012/01/simulating-phony-in-rake.html
So one hack to fix it is to re-open the Task class and redefine the timestamp method like so:
require 'rake/phony'
task :default => %w[targetA]
file targetA: :test do |t|
sh "touch #{t.name}"
end
task :test do |t|
p t.name
end
task :test => :phony
class Rake::Task
def timestamp
prerequisite_tasks.collect { |pre| pre.timestamp }.max || Time.now
end
end
I guess rake/phony is a gem? It's not something built into rake, I don't think, I've never seen it before.
No, it's part of Rake:
https://github.com/ruby/rake/blob/master/lib/rake/phony.rb
It can be used only for File tasks, as a dependency, so that Rake stops complaining that it doesn't know how to build that dependency.
A basic usage example:
require 'rake/phony'
file "doc.html" => "doc.md"
file "doc.md" => :phony
This is taken from an actual Rakefile I use:
require 'rake/phony'
LIB_ES_SOURCES = FileList['alan_en/StartLib/*.i']
LIB_ES_SOURCES.each do |lib_src|
file 'alan_en/cloak/cloakv3.a3c' => "#{lib_src}"
file lib_src => :phony
end
where I need every *.i in the alan_en/StartLib/ folder to depend on the status of the alan_en/cloak/cloakv3.a3c (a compiled VM file); but then Rake would complain that it didn't know how to build all those .i files, hence the line that creates a File task for each .i file, making it dependent on :phony.
Once you require 'rake/phony' the :phony task is immediately available, and you can assign it as the dependency of any File task. It will never require executing the task since its always mimicking an up to date status, so it won't execute any custom code either (it's what its name indicates: a dummy/phony/fake task).
Related
Of course it is unusual for rake tasks to be triggered by a controller (and kind of kludgey) but very common for them to be triggered by cron. I would like to detect from within a rake task whether it was started manually on the command line, or not.
How can I do that? This is a pretty standard thing to do in a shell script, but I'm unable to find any documentation about how to do it with a rake task.
Why the hate? People are downgrading this simply because they don't know the answer? 🤦🏼♂️
Here's a stab I took.
I tested this in both CL and Rails Console. I also tacked an invocation at the end of Application.rb to double check. But I haven't tested it in all the many other ways one might, so people should use this only with caution.
Likewise, I'm not certain that index 7 will be universal.
But I'm pretty sure it's accomplishable if you really want it.
task who_called: :environment do
puts case caller_locations[7].label
when "<main>" then :rails
when "invoke_task" then :cli
else
raise "unknown caller: #{location}"
end
end
Another suggestion is to always invoke the task with an ENV variable or an argument. You can assume that nil defaults to the command line, so people don't have to type unnecessary arguments.
Try this:
if defined?(Rails::Console)
....
end
Or you can check what caller[0] returns when you call from the cmd and use that in the if instead.
I have a rails application using rake db:seed too fill in some of the basics in my DB to start working. This works through a little infrastructure and a fixtures.rb someone else wrote to load a YAML into the DB.
What I'm trying to do is take this infrastructure and move it into my own gem so that I can use it again elsewhere, this requires me both to have some of my own gem's models be inserted through the gem's seed task (which I want to have run from calling db:seed or something similar in the main app) and that the main app push some of the gem's models using it's own seed. The second part of this I already have working, it was a simple fix in the fixtures.rb file I was given.
The things I want to do now:
Move fixtures.rb into the gem: I still don't have any of the source running this in the gem. Now to do this I can probably require the file from the [MyGem::Engine.root, 'lib', ...].join then call a method there with a path to load YAML files from into the DB, which I don't see why it shouldn't work.
Get rake db:seed to run a task defined in my gem. I've added .rake files under lib/tasks of my gem (it's an engine) and I can't seem to call them from the main app rakefile though.
To make myself clear, what I want to do is through the gem (not the main app - or with 1 line of code in the main app) add a dependency onto the main apps seed task, so that when someone runs rake db:seed in the main app the gem will run additional seeding without the main app developer even having to know about them.
The dirty solution that I want to avoid is loading the .rake files from the gem inside the main app, or loading a seeds.rb in the gem from the one in the main app.
So what I'm asking is basically how to make the rake db:seed task do things defined within my gemified engine just by having the gem required in the gemfile?
So shortly after asking this I figured it out.
First step was taken from here: How to add a gems tasks to the main app
Then inside a task file
#lib/task/some_task.rake
Rake::Task['db:seed'].enhance ['my_seed_task']
#lib/tasks/my_seed_task.rake
task 'my_seed_task' do
...
end
And now when in the main app I run rake db:seed it runs whatever my_seed_task defines as a perquisite.
Try instead to extend Rails::Generators::Base.
This is the mechanism as given in the docs:
"Each public method in the generator is executed when a generator is invoked"
so
class DbGenerator < Rails::Generators::Base
source_root File.expand_path('../../../../db', __FILE__)
def copy_seeds_file
copy_file 'seeds.rb', 'db/seeds.rb'
end
def copy_seeds_dir
Dir.foreach seeds_dir do |file|
copy_file "seeds/#{file}", "db/seeds/#{file}" unless file.match /^\./
end
end
def seeds_dir
File.expand_path 'seeds', self.class.source_root
end
end
This approach will allow all gem-based seed data to copy into the app dir for the db:seed run
My app has a complicated set of dependencies -- some information comes from user input, some comes from remote sites via delayed_job -- expressible as a directed acyclic graph. I was all set to write a simple dependency mechanism to traverse the graph, but it occurs to me: Rake does this already, and I shouldn't re-invent the wheel.
Usually rake takes its declarations in a standalone rake file and is run from the command line. Instead, is it possible to write the declarations in a library or model file and run rake from within the app? (To make it clear: I would NOT be using rake here for long-running tasks, I just want it for its ability to define and traverse a dependency graph.)
This loads without error:
require 'rake'
class MyClass
task :clean do
puts 'cleaning'
end
end
... but it's not clear how to invoke it, or create tasks in namespaces, etc.
Depending on your environment:
From Rake:
Rake::Task#execute
http://rake.rubyforge.org/classes/Rake/Task.html
From term or cmd:
Windows:
system('start rake doc:app')
Linux:
system('rake doc:app')
Here's a discussion at ruby-forum:
https://www.ruby-forum.com/topic/196210
Here's a related StackOverflow question / answer using the system call:
http://www.stackoverflow.com/questions/3021805
I had a working (and working well) ImageScience install, that did some simple resizing to various dimensions of images, and then copying them to different directories. All very simple. This small and simple routine was in a rake task. Upon update to Rails 3, this rake task will still work (it does some AR inserts and audio encoding as well), but the image_science require fails with a message like this,
"require on /home//.ruby_inline/Inline_ImageScience_cdab.so failed"
I've ruled out a duff ImageScience install, as I can go into IRB and do some simple calls to ImageScience and make thumbnails. The remainder of the rake task works as well as it did before if I comment out any mention of requiring 'image_science' or the ImageScience routine.
the output from rake on failure is this,
/var/lib/gems/1.8/gems/RubyInline-3.8.6/lib/inline.rb:513:in `load'
/var/lib/gems/1.8/gems/RubyInline-3.8.6/lib/inline.rb:829:in `inline'
/var/lib/gems/1.8/gems/image_science-1.2.1/lib/image_science.rb:90
...
<active_support complaints >
...
/home/<user>/RailsApps/marlow/lib/tasks/flac_import.rake:2
...
<rails complaints>
...
/home/<user>/RailsApps/marlow/Rakefile:7
...
<standard complaints to end>
the Rakefile in the rails app root is a stock and standard Rails 3 Rakefile, like this,
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
require 'rake'
Marlow::Application.load_tasks
the last line is line 7.
I'm kind of stumped as to what's breaking it, and Google doesn't seem to shed anything. Does anyone know why RubyInline is complaining? Or why this once working Rake task is suddenly unhappy how ImageScience is being called? OS is Ubuntu 10.10, but it was all working prior to the Rails 3 upgrade.
Thanks in advance
This does seem to be the problem, but there is a simpler fix I found from perusing the comments at carlhuda issues 431
I had the same problem and it worked for me. Simply change the require method to be Kernel.require.
After that there's no need to pepper your code with require image_science statements.
There is a fix, but you'll need to jump through few hoops.
First delay image_science load:
gem 'image_science', :require => false
Then monkey patch ruby-inline (which image_science relies on). Place this code in config/initializers/ruby_inline_hack.rb:
class Inline::C
def load
require "#{so_name}"
#below is the original version which breaks
#require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
end
end
Then require 'image_science' wherever you're using it. And voila.
One note on aremave's answer:
It looks like the original code has a bug! It's not using short-cut-evaluation!
class Inline::C
def load
require "#{so_name}" || raise LoadError, "require on #{so_name} failed"
end
end
Notice the || , which will stop the evaluation of the logical expression if the first part is true.
If there is an 'or' in the same place, the second part of the expression will always be evaluated,
hence the error you're seeing...
as seen on bundler issue tracker, it worked for me.
Point your gem file to https://github.com/asynchrony/image_science We rebuilt image science without ruby inline.
I have a Rails app with some basic models. The website displays data retrieved from other sources. So I need to write a Ruby script that creates new instances in my database. I know I can do that with the test hooks, but I'm not sure that makes sense here.
I'm not sure what this task should look like, how I can invoke it, or where it should go in my source tree (lib\tasks?).
For example, here's my first try:
require 'active_record'
require '../app/models/mymodel.rb'
test = MyModel.new
test.name = 'test'
test.save
This fails because it can't get a connection to the database. This makes sense in a vague way to my newbie brain, since presumably Rails is doing all the magic work behind the scenes to set all that stuff up. So how do I set up my little script?
You can load the entire rails environment in any ruby script by simply requiring environment.rb:
require "#{ENV['RAILS_ROOT']}/config/environment"
This assumes the RAILS_ROOT environment variable is set, see my comment for other ways of doing this.
This has the added bonus of giving you all the nice classes and objects that you have in the rest of your rails code.
To kick off your processes it sounds like cron will do what you want, and I would also add a task to your capistrano recipe that would add your script to the crontab to periodically get the data from the external source and update your DB. This can easily be done with the cronedit gem.
The cron approach does have some drawbacks, mostly overhead and control, for other more sophisticated options see HowToRunBackgroundJobsInRails from the rails wiki.
I agree with the answer above but you have to include => :environment in your task or it will not load the Rails environment.
e.g.,
namespace :send do
namespace :trial do
namespace :expiry do
desc "Sends out emails to people who's accounts are about to expire"
task :warnings => :environment do
User.trial_about_to_expire.has_not_been_notified_of_trial_expiry.each do |user|
UserMailer.deliver_trial_expiring_warning(user)
user.notified_of_trial_expiry = true
user.save
end
end
end
end
end
I'd suggest creating custom rake tasks (lib/task/foo.rake). This give you easy access to most of the functionality of your rails app.
namespace :foo do
desc 'do something cool'
def something_cool
test = MyModel.new
test.name = 'test'
test.save
end
end
Then:
$ rake -T foo
rake foo:something_cool # do something cool
You can even run the tasks via a cronjob.
I wrote up a post about this a while back.
http://www.rawblock.com/2007/06/14/ruby-oracle-mac-os-x-pain-jruby-and-activerecord-jdbc-to-the-rescue/
You can open a connection in your scripts as such:
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:username => "root",
:host => "localhost",
:password => "******",
:database => "******"
)
I'm sure there is a more elegant way to do it, so that it grabs the info from your database.yml.
There are few steps to this and more details needed to really answer well.
You say your site retrieves data from other sources? How often? If it is semi-regularly you definitely want to look into background processing/messaging. If it is frequently you really want to avoid loading your rails environment every time your script runs since you will be paying too high a startup tax each time.
There are a multitude of options out there you will want to research. Reading about each of them, particularly reviews from people who post about why they made the choice they did, will give you a good feel for what questions you need to ask yourself before you make your choice. How big a job is loading the data? etc...
Off the top of my head these are some of the things you may want to look into
Script/Runner & Cron
Background/RB
Starling
Workling
MemcacheQ
Beanstalk
Background Job (Bj)
delayed_job (Dj)
Daemon Generator
Check out my answer in "A cron job for rails: best practices?".
It contains two examples for using cron to run Rake tasks and class methods (via script/runner). In both cases, Rails is loaded and you can use your models.
Nice Joyent write up of using rake to run rails tasks from a cron job - http://wiki.joyent.com/accelerators:kb:rails:cron
Easiest way to run ruby tasks that interact with rails app/models is to make Rails generate Rake tasks for you!! :)
Here's an example
run rails g task my_namespace my_task
This will generate a file called lib/tasks/my_namespace.rake which looks like:
namespace :my_namespace do
desc "TODO: Describe your task here"
task :my_task1 => :environment do
#write any ruby code here and also work with your models
puts User.find(1).name
end
end
Run this task with rake my_namespace:my_task
Watch your ruby code task that interacts with rails modal run!