access rails app without using http - ruby-on-rails

Is it possible to interact with a rails(3) app without going though the http layer? I have some processes running locally on the server, which i'd like to be able to make some changes. Similar to what you can do in the rails console, but in a non-interactive way. I just would just call some ruby scripts to update some records in the database, rather than type them myself in the rails console.

Write these scripts you are talking about, connect via ssh and run them from the bash?!^^
ssh is the secure shell, sth. like telnet but encrytped with ssl if you are no linux user you may dont know about it...
Ahh damn and when you write thes scrips you must do this to get your applications environment:
require 'config/environment.rb'
After requireing the environment file you cann acces all your Models with the script!

If you are just trying to update a bunch of records in the DB, you don't need complete Rails installation. You can write a script and establish connection manually. Something like:
require 'rubygems'
require 'active_record'
class User < ActiveRecord::Base
establish_connection :adapter => "sqlite3", :database => "users.db"
set_table_name "users"
end
User.create(:name => 'a user', :email => 'user#example.com')
And call this from cron the way you want.

Try writing a custom rake task.
Place a file, say, task.rake in your lib/tasks directory. In that file, define a task:
task :mytask => :environment do
#code here
end
and call it from the command line:
> rake mytask

Related

Rake task failing to load :environment properly

I'm running a custom rake task...
namespace :import do
desc "Import terms of service as HTML from stdin"
task :terms => :environment do
html = STDIN.read
settings = ApplicationWideSetting.first
settings.terms_and_conditions = html
if settings.save
puts "Updated terms of service"
else
puts "There was an error updating terms of service"
end
end
end
The model ApplicationWideSetting is reported as undefined when running the task in the production environment. However, when running the task on other environments (ie. development, staging, test.) the task runs fine.
Running the process in rails console, in all environments, completes ok.
Does anyone know what's going on, things I could check?
note: I ran the task with
puts Rails.env
To check the shell environment var RAILS_ENV was getting set/read correctly. I've also tried both with and without the square brackets around the :environment dependency declaration.
additional info: Rails v3.2.14
further info: I've setup a completely fresh rails app, and the script works fine in any environment. Since the install in question is a real production environment, I'll have to setup another deploy and check it thoroughly. More info as I find it.
In a nutshell, Rails doesn't eager load models (or anything else) when running rake tasks on Production.
The simplest way to work with a model is to require it when you begin the rake task, and it should work as expected, in this case:
# explicitly require model
require 'application_wide_setting'
It's possible to eager load the entire rails app with:
Rails.application.eager_load!
However, you may have issues with some initializers (ie. devise)

capistrano, unix user, permissions

I don't really understand what model of unix accounts/permissions is intended with Capistrano.
Let's say I've got a rails app called Widget and I'll be deploying with passenger. In general, pre-capistrano, I want the entire ./widget directory to be owned by a user called 'widget'. And then by default default passenger will run the app process as user 'widget' too, because passenger runs as user that owns the file.
And the whole point of this is for that 'widget' account to have fairly limited permissions, right? Since a web app will be running under that account?
So since I want the files to be owned by 'widget', I tell cap
set :user, "widget"
But now when I run "cap deploy:setup", it wants to 'sudo' from that account. No way that 'widget' account gets sudo privileges, the whole point is keeping this account limited privs.
Okay, I can tell cap not to use sudo... but then it won't actually have privs to do what it needs, maybe.
I can find a workaround to this too. But I start thinking, why do I keep having to re-invent the wheel? I mistakenly thought the point of cap recipes was to give me some best practices here. Anyway... what do people actually do here?
Use one unix account for install, but then have cap somehow 'chown' it to something else? Use one unix account, but have something non-cap (puppet?) do enough setup so that account doesn't need to sudo to get things started? What? What am I missing?
You can avoid some of the headache by using Passenger most commonly with Nginx as your webserver.
Then to restart web services the unprivileged Widget user creates a file in his path and Passenger will automatically restart Nginx when it sees that file being present.
This is enabled via the following in your config/deploy.rb:
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{File.join(current_path,'tmp','restart.txt')}"
end
end
As for other privileged tasks for MySQL/DB administration your database.yml provides the credentials necessary to handle rake migration tasks.
So really the only time you would need something more privileged would be for system wide installation of gems, ruby, or rails updates, but a lot of that depends on how your production environment was setup/installed.
Given Passenger+Nginx and separate credentials for DB you can disable sudo and see if you encounter any errors during your Capistrano deploy process and then pickup from there.

Is there a way to stop Rails' built-in server from listening on 0.0.0.0 by default?

I do a lot of web development on untrusted networks (coffeeshops, the neighbors' open wifi, DEF CON), and I get twitchy when random, assuredly buggy software (my Rails app under development, say) binds a port on 0.0.0.0 and starts taking requests from all comers. I know that I can specify the address of binding with the -b option to the server, but I'd like to change the default globally so it always runs that way unless I tell it otherwise. Of course I can also run some kind of firewall which will block the connection, but better not to listen in the first place. Is there a '.railsrc' file or similar -- at least a per-project settings file, but preferably some global settings file -- which I can use to force the server to only bind to 127.0.0.1 by default?
Use the --binding=ip parameter:
rails s --binding=127.0.0.1
https://github.com/rails/rails/blob/master/railties/lib/rails/commands/server.rb
You can update the /script/rails file in you rails app to reflect the following:
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
# START NEW CODE
require "rails/commands/server"
module Rails
class Server
def default_options
super.merge({
:Host => 'my-host.com',
:Port => 3000,
:environment => (ENV['RAILS_ENV'] || "development").dup,
:daemonize => false,
:debugger => false,
:pid => File.expand_path("tmp/pids/server.pid"),
:config => File.expand_path("config.ru")
})
end
end
end
# END NEW CODE
require 'rails/commands'
This will bind the rails app to my-host.com when it starts up. You can still override the options from the command line.
I am not sure why this is not reflected in the Rails::Server API docs. You can have a look at https://github.com/rails/rails/blob/master/railties/lib/rails/commands/server.rb to see the server implementation.
Note that in Rails 4, the /script/rails file has been moved to /bin/rails.
There's no way to change it globally, you'll have to use -b.
rails s -b <ip address>

Executing Ruby script in an Rails application

I can run the following commands in the console for my Rails application and import CSV file into my database.
require 'csv'
# row will be an array with the fields in the order they appear in the file
CSV.open('myfile.csv', 'r') do |row|
# assuming the fields in the CSV file are in order npa, nxxFrom, nxxTo, trnk
# create and save a Trunk model for each row
Trunk.create!(:npa => row[0], :nxxFrom => row[1], :nxxTo => row[2], :trnk => row[3])
end
However I'd like to facilitate the process by just creating a script for it. The problem is I don't know how to write a script that is application specific. In order for the above commands to run, I need to launch console in the application folder by the following:
ruby script/console
So simply copy/pasting the commands into an .rb file and executing won't work.
Any help will always be appreciated :)
Why not use script/runner "<code or filename here>? to run the script? The runner script executes the script in the give application context without having a console.
You could make it a rake task which inherits from environment. Place it into lib/tasks/your_task.rake:
task :your_task => :environment do
# code goes here
end
Then run it using rake your_task. It can be called anything you want.
You need to invoke the rails environment in the script.
Try adding this at the top of you file:
RAILS_ENV = ARGV[0] || "production"
require File.join(File.dirname(__FILE__), *%w[.. config environment])
# assumes script is placed one level below the root of the application
# second parameter is the relative path to the environment directory of your rails app

RoR environment in Ruby standalone script

I want to run a standalone ruby script in which I need my RoR environment to be used. Specifically, I need my models extending ActionMailer and ActiveRecord. I also need to read the database configuration from my database.yml.
How do I go about it?
The easiest way is to change the shebang of your script from :
#!/usr/bin/ruby
to
#!/path/to/your/rails/script/runner
Et voilĂ , your script will be run with the full rails environment loaded. You can also run your script as ./my_script -e production to have it run with the production database.
Check out this thread:
How do I run Ruby tasks that use my Rails models?
Essentially it boils down to:
require "#{ENV['RAILS_ROOT']}/config/environment.rb"
Have fun!
I think the best way to do this is to make it a rake task.
# lib/tasks/mystuff.rake
desc 'do my stuff'
task :my_stuff => [:environment] do
# do my stuff
end
The [:environment] stanza loads the rails environment.

Resources