How do I use "gets" on a rake task? - ruby-on-rails

I get an error whenever I try to use the function gets within a rake task. Is there a way to make it work?
The error says, "no such file or directory - (rake task name)"

The problem is that Kernel#gets (which is what you're calling if you just use gets by itself) assumes you're pulling from a file named by the arguments passed to Rake. That means gets tries to return the content of a file called [rake-task-here], which almost certainly doesn't exist.
Try STDIN.gets.

I don't think that you should be using gets in a rake task, if you need to get input from the command line you probably should pass it in as a parameter, but if you post some code that is not working then I am sure you will get a better answer.

Related

Can I detect if a rake task was triggered on the command line (as opposed to with cron, etc.)?

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.

Writing multiple Rake tasks

I've written two Rspec tests each which invoke the same Rake task. The second task never gets run, as invoke only triggers once so I need to reenable. My issue is that I can't get the rake task to run, here is the command I'm using:
Rake::Task["product:delete"].reenable(product.id)
I get a run time error for this command:
Don't know how to build task 'product:delete[1]' Did you mean? product:delete
Anybody know how I should write this? I'm confused because in isolation I get it to pass by running:
Rake.application.invoke_task("product:delete[#{product.id}]"
You shouldn't need to pass an argument to reenable (it doesn't take one)
You should however be passing your argument to invoke (not invoke_task) rather than specifying it as part of the task name.
E.g.
Rake::Task['product:delete'].reenable
Rake::Task['product:delete'].invoke(product.id)
You could streamline it a little more perhaps by saving the task in a variable:
t = Rake::Task['product:delete']
t.renable
t.invoke(product.id)
P.S. It looks a lot like that error "Don't know how to build task 'product:delete[1]' Did you mean? product:delete" has actually come from you trying to invoke the task with the argument in the task name rather than from the reenable. Possibly as a result of trying a lot of different things.

send object from application_rake to a model

I have a task in application_rake. The lines that matter are:
family = Family.find(114)
SysMailer.deliver_feedback_memo(emails_str,"escorter", family)
In the SysMailer model i try to write the name of the family like this:
puts family.name
name is an attribute of family (for sure!!!)
But when I run:
rake the_task
I get an error:
rake aborted!
missing attribute: name
Why? is there a problem to send an object from rake to a model?
UPDATE:
After struggling with the issue, I tried not to pass the object, but pass only the family.id to the model like this:
SysMailer.deliver_feedback_memo(emails_str,"escorter", **family.id**)
(The asterisks are just to show the change).
Then in the model I created a method that takes this argument:
def some_method(emails, type, **family_id**)
family = Family.find(family_id)
puts family.name
And it works.
So my question remains. Is there a problem to pass an object with rake?
There definitely shouldn't be a problem passing objects via method calls -- even if from a rake task.
If the family.name statement was actually what was failing I'd expect you'd see a message more like:
undefined method :name for #<Family>
So it sounds like you may be looking at the wrong line of code (i.e. it may be the fault of something else in the code). A missing attribute error sounds more like a mass-assignment problem.
Have you tried running the rake task with the --trace option?
rake the_task --trace
This may help identify the problem, either way.

How would I force Kernel#gets to use STDIN#gets in a rake task?

So I'm trying to DRY up a Rake task which runs a script that takes user input, and I've run into the same problem as this poster - by default, just calling gets assumes that the rake argument (in this case, db:seed) is a file from which it should read, which of course doesn't exist. I got around this by just calling STDIN.gets, which works fine, but I'd love to be able to just use gets the way I can use puts (Rake seems to have no issue with STDOUT by default) - as a static method.
Is there any way to force Kernel#gets to read from STDIN within Rake? (Or more generally, is there any way to force Kernel#gets to read from STDIN when it is ostensibly passed a command line argument?) Or would that be a bad practice?
(Answered by #the-tin-man in this comment)
There's really very little advantage to be gained in DRYness from forcing Kernel#gets to read from STDIN in any context, including Rake. Although it can be done (by modifying $stdin), it shouldn't be done because it would just be brittle and hacky.

Rails - how to strip forward slash characters from data in database?

I have an app that I've converted over from another cms. The old URLs were being stored in the database like so:
/this-is-an-old-permalink/
And I need them to be like this:
this-is-an-old-permalink
Note the absence of forward slashes. What is the easiest way to go about removing them?
I'm not necessarily looking for the exact code to do it (although that'd be nice!) -- I'm asking also as a Rails newb: What is the best method to go about doing things like this? I've only really worked with Rails in setting up a model, controller, views and outputting data. I haven't had to do any processing like this. Would it go in the model? Any help is appreciated!
edit
Do I need to get all records, loop through them, do regex on that one field and then save it?
Since you're likely only going to write this once, your best bet is to create a script for it within lib, or to write a migration for it. I recommend the latter, because it will then be executed automatically with rake db:migrate if you restore from your old backup at a later date. You can then use all your standard Model processing tricks (like you would use on a Controller) within the migration without exposing the substitution code to a Controller.
EDIT:
You can add the following to a new file within lib/tasks to create a new rake task for this called db:substitute_slashes:
namespace :db do
desc "Remove slashes from old-style URLs"
task :substitute_slashes => :environment do
Modelname.find(:all).each do |obj|
obj.fieldname.gsub!(/regex here/,'')
obj.save!
end
end
end
The exclamation on the end of save! means it will throw an exception if the resulting object fails validation, which is a good thing to check for in this case.
You would then be able to execute this with the command rake db:substitute_slashes.

Resources