What all things can you access when you require a gem? - ruby-on-rails

Let us take Rails gem for example
Before require 'rails'
irb(main):001:0> Rails
NameError: uninitialized constant Rails
irb(main):002:0> ActionPack
NameError: uninitialized constant ActionPack
After require 'rails'
irb(main):005:0> require 'rails'
true
irb(main):007:0> Rails.constants
[:Railtie, :Rack, :Initializable, :Configuration, :WelcomeController, :InfoController, :VERSION, :Paths, :Info, :MailersController, :Application, :Engine]
irb(main):009:0> ActionPack.methods-Object.methods
[:gem_version, :version, :initialize_copy]
So now the ActionPack module is available in the console, what other modules or class (if any) are available and how to know about those modules?

Thats up to the gem developer. To see how your Name- and Objectspace changed there should be many clever ways.
I answer shortly how to find out which new Module (-Constants) there are:
initial_modules = Module.constants
# -> [:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, ...]
require 'rails'
Module.constants - initial_modules # all NEW constants
# -> [:Pathname, :OpenSSL, :Digest, :SecureRandom, :Concurrent, :I18n, :CGI, :ActiveSupport, :Set, :SortedSet, :Timeout, :TimeoutError, :Logger, :LoggerSilence, :DateAndTime, :Psych, :StringScanner, :ScanError, :Date, :DateTime, :YAML, :Base64, :TSort, :Rails, :BigDecimal, :BigMath, :TZInfo, :ThreadSafe, :Threadsafe, :SynchronizedDelegator, :JSON, :OpenStruct, :Singleton, :Mutex_m, :ActionPack, :Rack, :ActionDispatch, :Mime]
As you see, this also comes with a lot of new stuff that is required (but not provided) by Rails, like Date, OpenSSL, etc.pp.
Actually, requirements can happen at runtime, too, such that this lists grows as you use the code.
Hope that helps a bit.

Related

Ruby on Rails: Configure API in initializer in development

I have a Ruby on Rails app with an API in lib. Files in lib are autoloaded, and the API is configured in an initializer.
# lib/my_api.rb
module MyApi
extend Configuration
end
# lib/my_api/configuration.rb
module MyApi
module Configuration
attr_accessor :my_setting
def configure
yield self
end
end
end
# config/initializers/my_api.rb
MyApi.configure do |config|
config.my_setting = 'foo'
end
This works in production, but in development the API gets configured when the server is started. After I change some code, the configuration is lost and there are errors because the settings are nil:
irb(main):001:0> MyApi.my_setting
=> "foo"
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> MyApi.my_setting
=> nil
My guess is that in development, the classes are reloaded, but the initializer is not, which means it only gets configured once after starting the server.
Right now I'm duplicating my configuration in lib/my_api.rb, but that's very hacky.
What's a clean solution for this problem?
module MyApi
module Configuration
mattr_accessor :my_setting
def configure
yield self
end
end
end
mattr_accessor is an ActiveSupport macro for creating module level accessors.
Well, until someone comes up with a better solution, I've come up with two workarounds. I went with 2.
Don't autoload the lib directory (meaning don't autoload the API). That means having to restart the server when the API code changes, but solves the problem. That's why configuring gems like this works -- because they aren't autoloaded.
Manually reload the initializer in development at the end of lib/my_api.rb:
load Rails.root.join('config/initializers/smart_meter.rb') if Rails.env.development?
The MyApi constant will be replaced by a new one when Rails autoloads classes. The configuration is still available on the old object:
Loading development environment (Rails 4.2.0)
irb: warn: can't alias context from irb_context.
irb(main):001:0> MyApi.my_setting
=> "foo"
irb(main):002:0> OldMyApi = MyApi
=> MyApi
irb(main):003:0> reload!
Reloading...
=> true
irb(main):004:0> MyApi.my_setting
=> nil
irb(main):005:0> OldMyApi.my_setting
=> "foo"
irb(main):006:0> load Rails.root.join('config/initializers/smart_meter.rb')
=> true
irb(main):007:0> MyApi.my_setting
=> "foo"

What changed in Ruby?

The following spec passes fine in Ruby 2.1.5 but fails in 2.2.0 and I can't tell what that's all about:
# job.rb
class Job < ActiveRecord::Base
validates :link, :url => true
end
# job_spec.rb
require 'rails_helper'
describe Job do
describe "#create" do
["blah", "http://", " "].each do |bad_link|
it {
should_not allow_value(bad_link).for(:link)
}
end
end
end
fail log looks like this:
1) Job#create should not allow link to be set to "http://"
Failure/Error: should_not allow_value(bad_link).for(:link)
Expected errors when link is set to "http://",
got no errors
# ./spec/models/job_spec.rb:14:in `block (4 levels) in <top (required)>'
I find the only way to for that spec to pass with Ruby 2.2.0 is to include the validates_url gem in my project!!
Does anyone know this is about?
Maybe my solution isn't ideal, but it works.
Replace validates_url gem by validates gem. It has UrlValidator (written by me), which is well tested.
gem 'validates' # in Gemfile
validates :link, :url => true # you needn't to change something. Just remove validates_url from your Gemfile
P.S. It's a strange way - to test functionality of gem. Functionality should be tested in gem already.
P.P.S. I'm strongly recommend you to move to ruby 2.2.1 (or 2.2.2) instead of 2.2.0, because of 2.2.0 has a lot of bugs

Error using Log4r

I have a problem using Log4r.
uninitialized constant Log4r::Logger::RootLogger
I tried this, but still got an error:
>> require "log4r"
=> true
>> Log4r::DEBUG
NameError: uninitialized constant Log4r::DEBUG
>> Log4r::Logger.root
=> uninitialized constant Log4r::Logger::RootLogger
from /var/lib/gems/1.9.1/gems/log4r-1.1.11/lib/log4r/staticlogger.rb:5:in `root'
from (irb):5
from /usr/bin/irb:12:in `<main>'
Your problem with Log4r::Logger.root is version depending (the actual version 1.1.11 has this problem).
You may use the previous log4r-version 1.1.10:
gem 'log4r', '<=1.1.10' #or '= 1.1.10'
require 'log4r'
Log4r::Logger.root
log4r defines the constants like Log4r::DEBUG with the creation of the first logger.
You need a Log4r::Logger.new('dummy') before you have access to the level constants.
require 'log4r'
p defined? Log4r::INFO #false
Log4r::Logger.new('dummy')
p defined? Log4r::INFO #constant -> is defined
Some background:
There is a constant Log4r::Log4rConfig::LogLevels defining the different levels. The level-constants are defined, when the first logger is created. You may define them also with Log4r.define_levels(*Log4r::Log4rConfig::LogLevels)
This technique allows it to create loggers with different logging levels.
AlthoughLog4r::Logger.root no longer enables the constants, the code included later on in the answer you referred to does introduce the constants:
Log4r.define_levels(*Log4r::Log4rConfig::LogLevels)
per the following:
MacbookAir1:so1 palfvin$ irb
2.0.0p247 :001 > require 'log4r'
=> true
2.0.0p247 :002 > Log4r.define_levels(*Log4r::Log4rConfig::LogLevels)
=> 5
2.0.0p247 :003 > Log4r::DEBUG
=> 1
2.0.0p247 :004 >

Ruby gem for resolving pluralization in English

This is strange question in sense that I don't think there is answer for it but here it goes.
I am looking for gem which would allow me to get root word from pluralized word.
categories => category
people => person
apples => apple
Trick is I need it in ruby and not in ROR so solution should be independent from ActiveRecord which probably has this mechanism built in. Thanks. There is also paper on this subject that I found http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html/ in case somebody is interested in building the gem :)
This behaviour is defined in ActiveSupport, which you can include on its own without having to require Rails completely.
>> require 'rubygems'
=> true
>> require 'active_support/core_ext/string/inflections'
=> true
>> "categories".singularize
=> "category"
You can require just the inflections from ActiveSupport, leaving out the rest of rails, like this:
require 'active_support/core_ext/string/inflections'
If that doesn't work for you, check out the inflections gem.
You could always install just the active_support gem, and require just the inflector, that way you don't load all of rails. Many parts of rails can be used independently.
$ irb
1.9.3-p194 :001 > require 'active_support/inflector'
=> true
1.9.3-p194 :002 > ActiveSupport::Inflector.singularize('inflections')
=> "inflection"

Trying to load my seed data before cucumber tests in Rails3 / Capybara with factory_girl

I have some seed data (for price ranges) that is the same in prod, dev, test and doesn't change. I need that data in my test db to run my cuke tests.
I am load my seed data into test DB before the scenario loads, but it's failing.
I have the following in my features/support/env.rb file
# from http://www.andhapp.com/blog/2009/11/07/using-factory_girl-with-cucumber/
Before do
require 'factory_girl_rails'
# Dir.glob(File.join(File.dirname(__FILE__), '../../spec/factories/*.rb')).each {|f| require f }
Dir.glob(File.join(File.dirname(__FILE__), '../../db/seeds.rb')).each {|f| require f }
end
Which loads the following file:
# wipe out all previous data
Price.delete_all #is there a factory way of doing this?
# set defaults
Factory.define :price do |price|
price.id 1
price.price_range "$100"
end
# insert seed data
#price = Factory(:price, :id => 1, :price_range => "$100 - $500")
#price = Factory(:price, :id => 2, :price_range => "$500 - $1,000")
#price = Factory(:price, :id => 3, :price_range => "$1,000 - $1,000")
#price = Factory(:price, :id => 4, :price_range => "$10,000 - $100,000")
I get the following error message:
Factory already defined: price (Factory::DuplicateDefinitionError)
/Library/Ruby/Gems/1.8/gems/factory_girl-1.3.3/lib/factory_girl/factory.rb:61:in `define'
/Applications/MAMP/htdocs/rails_testing/feedbackd/features/support/../../db/seeds.rb:16
/Library/Ruby/Gems/1.8/gems/activesupport-3.0.3/lib/active_support/dependencies.rb:239:in `require'
/Library/Ruby/Gems/1.8/gems/activesupport-3.0.3/lib/active_support/dependencies.rb:239:in `require'
/Library/Ruby/Gems/1.8/gems/activesupport-3.0.3/lib/active_support/dependencies.rb:227:in `load_dependency'
/Library/Ruby/Gems/1.8/gems/activesupport-3.0.3/lib/active_support/dependencies.rb:239:in `require'
/Applications/MAMP/htdocs/rails_testing/feedbackd/features/support/env.rb:92
/Applications/MAMP/htdocs/rails_testing/feedbackd/features/support/env.rb:92:in `each'
/Applications/MAMP/htdocs/rails_testing/feedbackd/features/support/env.rb:92:in `Before'
Any thoughts?
You can only call Factory.define :price once, and I would probably not put it in the file that it's in right now. Do you have a factories folder? It usually lives in spec/factories. In there I would create the file price.rb, and define your factory once, there. factory_girl should automatically load all of those definitions up for you once.
If you're using Rails3 and you have factory_girl_rails in your Gemfile, then you don't even need that require 'factory_girl_rails', it does it for you.
Also if you have a new-ish version of cucumber the installer should automatically have added this section for you in env.rb:
if defined?(ActiveRecord::Base)
begin
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
rescue LoadError => ignore_if_database_cleaner_not_present
end
end
DatabaseCleaner is a good way to do the truncation instead of using your Price.delete_all
The last thing is seeds.rb - It's a good concept and something very similar to what we do in one of our apps at work.
But keep in mind, everything in the features/support directory is automatically required by cucumber, so you don't need to have that Dir.glob nonsense.
With factory_girl we don't use the default rails seeds file because it's not really applicable (in our opinion).
I would just add a file named anything (ours is named db_setup.rb) that looks something like this:
Before do
# Truncates the DB before each Scenario,
# make sure you've added database_cleaner to your Gemfile.
DatabaseCleaner.clean
Factory(:price, :attr1 => 'blah'...)
# More factories here etc
end
Take a look at hooks:
https://github.com/aslakhellesoy/cucumber/wiki/Hooks
You can use tagged hooks to only load up specific seed data, much like you would only execute specific tags in cucumber.
You can also employ the heavy handed rails way and do a rake db:test:clone.

Resources