How do I call the Rails console's reload! command programmatically? - ruby-on-rails

When using the Rails console, there's a handy reload! function which reloads models and such. How do I call this from another part of my program?
Edit I've been asked for the use case a bit. It was that I had a long running JVM process running Jruby and a clojure repl. I could run RSpec tests from the REPL and could run arbitrary Ruby code from there too. But I couldn't figure out how to reload the Ruby classes so that I could edit the code and see it changed in the Ruby runtime. I no longer use this setup, principally because testing it was such a pain.
(I'm using Jruby and can access the Ruby VM programatically from my backend).

Have you tried touching restart.txt? Unfortunately, I have no experience with JRuby, but confirmed it works on my app.
FileUtils.touch('tmp/restart.txt')
You probably want to do something other than a Get request, and secure it behind some authentication.
I threw it in an Admin controller and added the route to config/routes.
# app/controllers/admin.rb
class AdminController < ApplicationController::Base
##time = Time.now # This value gets cached with the model.
def reboot
FileUtils.touch('tmp/restart.txt')
#restarted_time = ##time
end
end
# config/routes.rb
namespace :admin
get 'reboot'
end
# app/views/admin/reboot.html.erb
<%= #restarted_time.to_s %>

Related

Scope of Models(?) in Rails

I'm new to Ruby on Rails and am trying to access my site's database. I generated and set up a model and controller called Machine, and noticed that in places like the Machine view I could iterate through all the machines in my database simply using #machines.each. However, this doesn't appear to be universal, as when I created a new Ruby file directly in my project's outermost directory, both #machines.each and the attempted assignment #machines = Machine.all threw errors (a NoMethodError and NameError respectively). Here's an example of code I could try to run:
#machines = Machine.all
#machines.each do |machine|
puts machine.created_at
end
Perhaps I need some kind of import statement?
If you are writing a script in plain Ruby -- then yes, you'll have to import everything manually, establish a connection to the DB, etc.
The code would roughly look like this:
require 'active_support'
require 'active_record'
your_db_config = {
# your DB config goes here
}
ActiveSupport::Dependencies.autoload_paths += File.join(__dir__, "app/models")
ActiveRecord::Base.establish_connection(your_db_config)
machines = Machine.all
Consider creating a task if you want Rails to take care of all that and don't want to be doing all that stuff manually.
When you start a rails server (or a rails console) it preloads your Rails application so that your models, constants, etc. are automatically in scope. If you want to access your application's resources from a separate script you still need to load the app. The simplest way to do that is with the rails runner command, which loads your app and then executes a script. So if your script above is in lib/show_machines you'd run:
$ bin/rails runner lib/show_machines
If you like self-executing scripts you can also use runner as a 'shebang' line:
#!/usr/bin/env <your_project_path>/rails/runner
#machines = Machine.all
#machines.each do |machine|
puts machine.created_at
end

Rails 4 - Impressionist Inside of a Resque Background Job

I am using Rails 4 w/ the impressionist and resque gem.
I am using impressionist to log unique session hits on my article show page. Due to performance issues and no need to display hits to users (it is for admins only), I would like to move logging impressions off into the background.
Normally I would log an impression using impressionist(#article, unique: [:session_hash]) but to move it off into the bg via resque I am now doing something like this...
articles_controller:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id)
end
app/workers/impression_logger.rb:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id)
article = Article.find(article_id)
impressionist(article, unique: [:session_hash])
end
end
When I set it up like this, when resque tries to process the job, it is returning undefined method "impressionist" for ImpressionLogger:Class. What do you guys think the best way to go about this is? I am not sure how to include impressionist methods inside of my resque worker.
The issue
Your problem stems from the fact that it looks like Impressionist works on the controller level due to including a module with the impressionist method in an engine initializer on any instances of ActionController:
https://github.com/charlotte-ruby/impressionist/blob/master/lib/impressionist/engine.rb#L11
You're trying to call the impressionist method from a regular class being invoked in a Resque job, so it's not going to have that method defined.
Solution
It's kind of gross, but if you really want to use impressionist, we can delve into this... Looking at the actual implementation of the impressionist method found here, we see the following:
def impressionist(obj,message=nil,opts={})
if should_count_impression?(opts)
if obj.respond_to?("impressionable?")
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
else
# we could create an impression anyway. for classes, too. why not?
raise "#{obj.class.to_s} is not impressionable!"
end
end
end
Assuming that you'd be calling something like this manually (as you want to from a resque job) the key are these three lines:
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
The if wrapper only seems to be important if you want to implement this functionality. Which it looks like you do. The call to associative_create_statement seems to be pulling parameters based off of the controller name as well as parameters passed from Rack such as the useragent string and ip address (here). So, you'll have to resolve these values prior to invoking the Resque job.
What I would suggest at this point is implementing a Resque class that takes in two parameters, an article_id and the impression parameters that you want. The resque class would then just directly create the impression on the impressionable object. Your Resque class would become:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id, impression_params = {})
article = Article.find(article_id)
article.impressions.create(impression_params)
end
end
And your controller method would look something like this:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id, associative_create_statement({message: nil})) if unique_instance?(#article, [:session_hash])
end
Disclaimer
There's a fairly big disclaimer that comes with doing it this way though... the method associative_create_statement is marked protected and unique_instance? is marked private... so neither of these is part of the impressionist gem's public API, so this code might break between versions of the gem.
Is impressionist installed properly with bundler? If so Rails should be loading it into your environment. I would check whether you can access impressionist functionality elsewhere in your Rails code (i.e. without going through Resque) as the first step to debugging this.
How are you starting your resque workers? If you need your Rails environment loaded, try rake environment resque:work.
https://github.com/resque/resque/wiki/FAQ#how-do-i-ensure-my-rails-classesenvironment-is-loaded

Accessing a database through Rails model from a seperate Ruby script

I have a Rails application with a database (PostgreSQL using ActiveRecord)
In a separate directory on the same server I have a Ruby script.
How can I, from the Ruby script, reach the Rails database through one of my Rails models?
I have seen that it is possible to require ActiveRecord and pass in the database details, but from what I understand I would need to rebuild the model, which means a lot of repetition of validations, methods, etc. Instead I'd like to somehow use the model that's already in place.
I found a solution that has the behaviour I was looking for, and am posting it as an answer for anyone who comes across this question at a later date.
Setting ENV['RAILS_ENV'] = "production" and then simply requiring the environment.rb file (with the appropriate path) in the Ruby script solves the issue very nicely.
This is similar to the answer provided by #MurifoX, but saves you having to re-declare the DB connection.
Try using rails runner. According to: http://guides.rubyonrails.org/command_line.html#rails-runner
runner runs Ruby code in the context of Rails non-interactively. For
instance:
bin/rails runner "Model.long_running_method"
In my experience this works nicely to run scripts that rely on Active Record and more.
Add the -e switch to the command to force the use of your production database connection:
bin/rails runner -e staging "Model.long_running_method"
Add any other environment variables that may be required to complete the database connection. If you use the Devise gem, you will also need to pass in a secret to allow the Devise initializations to complete. Other gems may equally need some assistance to get started.
You can use require to pass the relative path to your model, and then instatiate it a use.
require '/path/to/your/model/inside/your/app'
model = Model.new
For the connection, if you are using postgresql, you can use the PGconn class from the pg gem like this:
#connection = PGconn.open(
:user => 'postgres',
:password => 'password',
:host => 'host',
:dbname => 'dbname')

Authenticate User from within rails /lib module

I want to authenticate Users when they try to fire up a TCP connection in my Rails app. Here's the current code I have, it's very simplistic but should give you an idea of what I want to do.
TcpServer.rb
module TcpServer
def receive_data(data)
(#buf ||= '') << data
if line = #buf.slice!(/(.+)\r?\n/)
commands = data.split(";")
case commands[0]
when /start/i
if !User.authenticate(commands[1],commands[2])
close_connection
puts "Subscription invalid."
else
put "Subscription validated."
end
end
end
end
EventMachine::run do
host = "localhost"
port = "5587"
EventMachine::start_server host, port, TcpServer
puts "TcpServer started # #{host}:#{port}"
end
end
What do I need to require or include in order to access my User model from that module? Or is this just a completely incorrect way to do it? If so, what do you suggest?
The issue is I wasn't running it with Rails.
I was running it with:
ruby lib/TcpServer.rb
rather than:
script/runner lib/TcpServer.rb
No includes or requires needed, Rails did it automagically.
Dir.glob(Rails.root.join('app/models/*.rb')).each { |file| require file }
The above will get all models loaded if you need them (you can just add 'user.rb' to the statement if needed, in the comment above you may need to specify the path and not include the ".rb" part -> "require 'user'").
You should make a decision as to whether you think this type of integration server should be part of the running Rails app or potentially another "application" that is part of the same code base. You could keep the core internals here and start your EM server with a custom rake task and load the Rails env through that rake task.
namespace :tcp do
task :start, :needs => :environment do
# server load and start here
end
end
If I am going to open a new means of execution then I prefer to keep these running in separate processes to keep any errors from causing both to go down together. (I would look at Resque jobs/workers as a good example of how to keep code in the same Rails app without forcing them to run in the same process)

Quickly debug helper methods in script\console

How would I quickly debug helper methods in script\console. I'm talking about making changes and then debugging, over and over again.
This is a lot easier with Model methods, since all I have to do is use
reload!
to test the updated code, whereas to test a helper method, I have to do something like this
foo = ActionView::Base.new
foo.extend YourHelperModule
each time to I want to test a change.
What does reload! do? and can I modify it to add the above lines of code?
I don't think you can do that without hacking Rails. However, there's a workaround - debugging helper method in rails debugger:
1) gem install ruby-debug
2) ruby script/server --debugger
3) place <% debugger %> into some view and open that page in browser
4) server window "turns into" console, where you can debug helper methods
5) 'return' command ends the debugging
If you modify the helper method and run the debugger again, you will get recent version of the method.
More info about debugger is here: http://railscasts.com/episodes/54-debugging-with-ruby-debug
I would suggest not using script console and writing tests in either Test::Unit or rspec instead. Google should get you pointed in the right direction there is a ton of information out there.
If you're doing something "again and again" then you should be automating it. Assuming you know what your helper function should do then as mentioned elsewhere you should be able to write a test (or tests) for it.
Here's a sample that tests application_helper. It lives in my test/unit directory:
require 'test_helper'
class ApplicationHelperTest < ActiveSupport::TestCase
include ApplicationHelper
test "number_as_pct shows 2dp as default" do
assert_equal "1.10%", number_as_pct(0.011)
end
test "number_as_pct shows more dp when required" do
assert_equal "1.1000%", number_as_pct(0.011, :precision => 4)
end
end

Resources