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)
Related
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
i'm wondering how can i stop a Rinda ring server, besides killing its process.
i'v checked ring.rb shipped with my ruby 1.9.3, and found the RingServer lacks api to stop itself. It opens an UDPSocket in initialize(), but it dose not contain any code to close that socket.
anybody knows it? thanks ahead. :D
Rinda is part of Distributed Ruby (DRb), so if the goal were to just stop all Rinda and other DRb services, you could do:
DRb.stop_service
If you use that, then in your Rinda service code (the looping method), you need to rescue DRb::DRbConnError to avoid problems trying to write to the TupleSpace, according to: http://www.ruby-forum.com/topic/97023
Not a Rinda service, but here is a simple example I tested that stops a DRb service. It just uses DRb (no Rinda) in Ruby 1.9.3, modified slightly from the example here: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html
server.rb
#!/usr/local/bin/ruby
require 'drb/drb'
URI="druby://localhost:8787"
class StopAndGiveTimeServer
def get_current_time
DRb.stop_service
return Time.now
end
end
FRONT_OBJECT=StopAndGiveTimeServer.new
$SAFE = 1 # disable eval() and friends
DRb.start_service(URI, FRONT_OBJECT)
DRb.thread.join
client.rb
#!/usr/local/bin/ruby
require 'drb/drb'
SERVER_URI="druby://localhost:8787"
DRb.start_service
timeserver = DRbObject.new_with_uri(SERVER_URI)
puts timeserver.get_current_time
Update: It sounds like you want to monkey patch the ring server to close the socket.
Just create a way to get the existing socket via monkey patch:
module Rinda
class RingServer
attr_accessor :soc
end
end
Then you can keep the instance of the ringserver in an instance variable, e.g. #ringserver, and use it to access the socket to close it, set a new socket, etc. e.g.
def bind_to_different_port(port)
begin
#ringserver.soc.close
rescue => e
puts "#{e.message}\n\t#{e.backtrace.join("\n\t")}"
end
#ringserver.soc=UDPSocket.open
#ringserver.soc.bind('', port)
end
Or skip the attr_accessor and just add a method to RingServer and call a method or two on the RingServer to close, open, bind the socket.
To see how it uses the socket in Ruby 1.9.3: https://github.com/ruby/ruby/blob/v1_9_3_374/lib/rinda/ring.rb
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 %>
Something in Rails (ActiveRecord::Base.logger ?) puts all executed SQL into log/development.log.
I have a rails app, whose data is populated by several rake tasks. Often in development, I want to run the web app, and run several of these rake tasks simultaneously (they are long running tasks that talk to other systems and create data in a local database).
Annoyingly, they all log to the same file at the same time.
How/where should I change this? Can I do it from the command line? Where (if in a rake file) should I do it? Or should I create new environments for each rake task?
Is there documentation i should have read to answer this, where is it?
Thanks a bunch.
Depends on what you are really trying to log. In Rails 3.+ you can use the ActiveSupport::LogSubscriber mechanism to hook into Rails internals and log certain interests to other files.
If you really want to hijack the core Rails.logger instance for each of your rake tasks, than do just that.
# lib/tasks/foo.rake
desc "Prints Hello World"
task :helloworld do
Rails.logger = Logger.new("/path/to/hello-world.log")
# do something in your Rails stack that would write to "Rails.logger"
end
Not tested, but I think that might work.
That being said, I think subscribing to interesting events in ActiveRecord via ActiveSupport::LogSubscriber might be a cleaner approach
My current solution is
dec "update statistics"
task :update_stats => :environment do
root_path = Rails.configuration.root_path
env = Rails.configuration.environment
ActiveRecord::Base.logger = Logger.new(File.join(root_path, "log", "#{env}-stats.log"))
#code ...
end
It feels a bit hacky, but maybe that's just the way to do it.
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!