Been playing with Ruby on Rails for awhile and decided to take a look through the actual source. Grabbed the repo from GitHub and started looking around. Came across some code that I am not sure what it does or what it references.
I saw this code in actionmailer/test/abstract_unit.rb
root = File.expand_path('../../..', __FILE__)
begin
require "#{root}/vendor/gems/environment"
rescue LoadError
$:.unshift("#{root}/activesupport/lib")
$:.unshift("#{root}/actionpack/lib")
end
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
require 'rubygems'
require 'test/unit'
require 'action_mailer'
require 'action_mailer/test_case'
Can someone tell me what the $: (a.k.a. "the bling") is referencing?
$ identifies a global variable, as opposed to a local variable, #instance variable, or ##class variable.
Among the language-supplied global variables are $:, which is also identified by $LOAD_PATH
$: is the global variable used for looking up external files.
From http://www.zenspider.com/Languages/Ruby/QuickRef.html#18
$: Load path for scripts and binary modules by load or require.
I wanna note something weird about Ruby!
$ does indeed mean load path. And ; means "end line". But!
$; means field separator. Try running $;.to_s in your REPL and you'll see it return ",". That's not all! $ with other suffixes can mean many other things.
Why? Well, Perl of course!
To quote the Ruby Forum:
ruby comes with a set of predefined variables
$: = default search path (array of paths)
__FILE__ = current sourcefile
if i get it right (not 100% sure) this adds the lib path to this array
of search paths by going over the current file. which is not exactly the
best way, i would simply start with RAILS_ROOT (at least for a rails
project)
$:.unshift
is the same as
$LOAD_PATH.unshift
. You can also say:
$: <<
$LOAD_PATH <<
They are pretty common Ruby idioms to set a load path.
Related
I need to do in rails 4 supply some ip address to set a constraint on certain routes.
Is there a way to get this data from a config file without harcoding it into the routes file?
Im using a yaml file and initializer for app variables like:
APP_CONFIG = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
so normally I could do:
constraints(:ip => %w[APP_CONFIG['app_url']]) do
.. my routes..
end
This fails in the routes.rb is there a way to fix this?
The routes.rb file is a ruby file which is instantiated once, and loaded into memory.
You can just add the ruby code inside it and it will be executed once:
Rails.application.routes.draw do
app_config = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
constraints(:ip => %w[app_config['app_url']]) do
.. my routes..
end
end
This will instantiate the routes.rb file with the variable loaded from the yml and available throughout your rails routes app. You don't even need to use a env variable. Local variable seems a better idea.
You can also put logic inside and make it environment dependant:
if Rails.env.production?
app_config = YAML.load_file("#{Rails.root}/config/application.yml")[Rails.env]
constraints(:ip => %w[app_config['app_url']]) do
.. my routes..
end
else
.. my routes ...
end
Taking a look at the initialization process of rails (http://guides.rubyonrails.org/initialization.html). You'll see that routing is actually loaded quite early (and earlier than application.rb or other initializers). It has therefore not yet loaded this file.
A way round this would be to place this into your boot.rb:
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
#Now load app config:
require 'yaml'
APP_CONFIG = YAML.load_file(File.expand_path('../../config/application.yml', __FILE__))
I believe you are running into a load order issue. You could probably hack around this, but...
I would highly recommend using Figaro to solve this problem. It is a gem specifically designed for rails configuration and will work nicely with 12 factor app deployments (like Heroku): https://github.com/laserlemon/figaro
I am using Figaro in the app I am currently working on and was able to confirm access to the env variables within my routes file. I believe this gem will solve your current issue and other config issues you don't even know you have yet!
I'm writing some Rails code that I want to be both 1.8.7 and 1.9 compatible. In a couple different files, I do some CSV parsing, so I currently have the following requires line in each file:
if RUBY_VERSION < "1.9"
require "rubygems"
require "fastercsv"
CSV = FCSV
else
require "csv"
end
To avoid all the duplication, though, this block should probably live in a single place. Where's the best place to put it? Is application.rb the right place?
I'd put it in an initializer config/initializers/csv.rb. This is loaded into rails automatically, and does not mix into the load path (so you don't have to bother about the file name).
Just be aware that some loading mechanisms do not do a "require" but just run the file in the loader context, so if you get an exception, you may not deal with it in the usual way.
Is there a way to check what modules is required by a lua script
after I loaded it?
More of a hack than a real solution: if you control the hosting environment, you can always replace require with your version of it:
local require_original = require
function require(module)
-- do something with the module name
require_original(module)
end
Check out package.loaded table.
Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.
Now, I've used Rails enough to know what the rails command does, but how it does it interests me.
The bin/rails file (from github) is as follows:
#!/usr/bin/env ruby
begin
require "rails/cli"
rescue LoadError
railties_path = File.expand_path('../../railties/lib', __FILE__)
$:.unshift(railties_path)
require "rails/cli"
end
As far as I know (and please correct me if I'm wrong), require doesn't run code, just loads classes etc.
I could also not find the rails directory in the root of them gem, so I'm a little confused where that's hiding as well.
Thanks.
require does run code. This will include any code outside of any classes and modules in the file being required plus any executable code in classes and modules that is outside of method declarations. As neutrino has said, the ruby interpreter is running the code in the file being required in order to define the classes in the source. However this might be a bit clearer if you try it out with something that has an obvious side effect like a puts statement.
Try this as a simple example. Create a file hello.rb containing puts "Hello World" then go into irb:
irb(main):001:0> require 'hello'
Hello World
=> true
Next, try this example of a simple class with some executable code in its body. Create a file hello2.rb containing:
class Hello
puts "class Hello being defined"
end
then require this from irb:
irb(main):001:0> require 'hello2'
class Hello being defined
=> true
Going back to bin/rails, take a look at the source for rails/cli in Github to follow the chain of how it works.
All ruby code is executable code. Class/module/method definitions are also executed by the interpreter, so when you say that require loads classes, it's true because loading a class means executing its code :)
Here is the file you are looking for: cli.rb. It's in railties.