Using "require 'my_module'" in ruby on rails - ruby-on-rails

Still learning RoR, sometimes I'll see an example use of a library in plain Ruby that has require 'my_library'.
When we're in Ruby on Rails, are we able to do require? Or does everything have to be included in the gemfile (which I guess both installs and requires the library).
In particular I'm trying to call a API:
class MyModel < ActiveRecord::Base
has_many :other_models
def get_score
require 'rest_client'
response = RestClient.post 'https://sender.blockspring.com/api/blocks/da289f59df228ac4e89cebdfc9aa41fd?api_key=fe5cf0d86af27c086ab5cd4d0eab6641', { items_as_json_string: '[{"time_since_post_in_hours": 5, "upvote_count": 22}, {"time_since_post_in_hours": 3, "upvote_count": 502}]' }
end
end

You absolutely can require things, but you generally don't need to when they are declared in the Gemfile. One particular case where you'd need to require them is if you told it so, such as (in your Gemfile):
gem 'something_very_specific', require: false
Now, you'll need to require it in the "very specific" place you intended to use it.

require is part of Ruby and not unique to Rails. You can require stuff in Rails and it is normally done at the top of the Class and not inside of methods. require simply runs the file to make sure the functionality you are trying to use is loaded.

Related

Use two gems in same namespace in Rails

I have an app that uses the LinkedIn gem but I need to move towards using the LinkedIn OAuth2 implementation which comes in a different gem.
I need to support the requests on oAuth1 in the initial gem for existing users, but I need to support OAuth2 for new users going forwards.
The problem is, both of these gems use the LinkedIn namespace for their module names, and depending on what order I include them in my Gemfile, one clobbers the other.
I tried adding require: false to the gemfile like so:
gem 'linkedin', require: false
gem 'linkedin-oauth2', require: false
But weirdly enough when I go to my console, the first one is still being required, where as the second one is not:
irb(main):001:0> require 'linkedin'
=> false
irb(main):002:0> require 'linkedin-oauth2'
=> true
Is this something to do with how require works? Is it possible to load just one of these gems each in separate lib files without clobbering each other?
EDIT
I figured out that I was requiring the linkedin in one of my spec files, which was causing it to be autoloaded, but that did not still fix the issue of things being clobbered.
When I have both gems installed and I run:
irb(main):001:0> require 'linkedin'
=> true
irb(main):002:0> ::LinkedIn::Client.new
NameError: uninitialized constant Api::QueryHelpers
from /Users/me/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/linkedin-1.1.0/lib/linked_in/client.rb:8:in `<class:Client>'
irb(main):004:0> require 'linkedin-oauth2'
=> true
But if I uninstall linkedin-oauth2 from my gemfile it works just fine:
irb(main):002:0> require 'linkedin'
=> true
irb(main):004:0> ::LinkedIn::Client.new
=> #<LinkedIn::Client:0x007f9eef6d72a8 #consumer_token=nil, #consumer_secret=nil, #consumer_options={}>
Why is that, especially since linkedin-oauth2 is not being required in the first example, yet the error occurs. Could it be because of how they require supporting files in the linkedin gem? Seems still like it shouldn't affect it.
I wouldn't recommend doing this, since it could have some weird side effects such as if linkedin-oauth2 references itself with ::LinkedIn, but a method to redefine a ruby constant is shown in this answer.
I would change it a little bit to prevent renaming a constant warning... Object.send(:remove_const, :Stripe) instead of the Stripe = Module.new as shown in the answer. So, an example (not tested) would be:
require 'linkedin-oauth2'
LinkedInOauth2 = LinkedIn
Object.send(:remove_const, :LinkedIn)
require 'linkedin'

Why use both require and include in Rails class?

I thought I understood the difference between require and include with the help of this this thread.
Then I stumbled on this piece of code from the github page of the bcrypt-ruby gem. Why are they using both methods? So first single models are going to be loaded but then again through include all the functions?
require 'bcrypt'
class User < ActiveRecord::Base
# users.password_hash in the database is a :string
include BCrypt
.....
require loads a ruby file, making the BCrypt module available to your ruby code. It does not, necessarily have to be in the same file as the class you're including the module in.
require can also be used to make a ruby class defined in that file available (for instance, other classes you've defined in your project). As it's in a gem, bcrypt is on the ruby path, if it's a file in your project you may need to reference the full path, or use require_relative.
include takes the code in the bCrypt module and includes it to your User class, providing User with the methods and attributes declared in the BCrypt module.
require loads the class.
include actually puts it inside the User class, e.g., including the module's methods as part of the definition of the User class.
The question you reference is pretty explicit about the difference.

Require a gem only in the context of a single class in Ruby

I have written a gem that uses a gem in one of its classes to overwrite Timeout. I only want to overwrite Timeout in this single instance, not globally in my gem and certainly not globally in anyone's project that uses my gem.
The problem I'm running into is when I include my gem in a rails project. It seems that the timeout gem gets instantiated from the get-go (at rails app load) and affects other parts of my Rails app that rely on the standard Timeout.
My question is this: how can I limit the timeout gem's influence to only the single class that I wish to use it in. I've already placed the require statement within the class definition and this didn't seem to help.
Place require: false in your Gemfile. E.g.:
gem 'name-of-gem', require: false
That way the gem won't be required automatically on app load, only when you explicitly call require 'name-of-gem' in your model.
(If that gives you errors you may be using an older version of Ruby, so you'll have to write :require => false instead of require: false)
I think you should fork this gem and add a namespace to Timeout module like this:
require "timeout"
module NewTimeout
module Timeout
#overwrited stuff
end
end
And then use it in your gem directly:
NewTimeout::Timeout.timeout()
Or include in your class:
class SomeClass
include NewTimeout::Timeout
end
And default Timeout module left unchanged
This is not possible without cooperation of the gem author.
When you require a file, the code in the file gets run. Period. If the code in the file monkeypatches a class or modifies the global namespace or whatever, that will happen. Period.
You can use load instead of require which allows you to evaluate the file within the context of an anonymous module. But, of course, using the namespace operator :: the code in the file can still get access to the global namespace and every module and class in it, by just doing something like class ::String; def length; 42 end end.
Now, if the gem author published his gem as a refinement, then you would at least be able to limit the effects to a single script body:
string_with_upper_reverse.rb:
module UpperReverse
def reverse
super.upcase
end
end
module StringWithUpperReverse
refine String do
prepend UpperReverse
end
end
puts 'Hello'.reverse
# olleH
using StringWithUpperReverse
puts 'Hello'.reverse
# OLLEH
test.rb:
puts 'Hello'.reverse
# olleH
require_relative 'string_with_upper_reverse'
puts 'Hello'.reverse
# olleH
using StringWithUpperReverse
puts 'Hello'.reverse
# OLLEH
require_relative 'required'
required.rb:
puts 'Hello'.reverse
# olleH
using StringWithUpperReverse
puts 'Hello'.reverse
# OLLEH
As you can see, neither requireing nor being required by a script which is using the refinement will mean that the refinement is visible inside your script. Only explicitly using the refinement inside a script body will make the refinement visible only from that point on and only within that one script body.
Note, however, that refinements are an experimental feature: their API may change without notice in future versions of Ruby, they may even be removed altogether.
Note also that the version of refinements that ships with Ruby 2.0.0 (and also current development versions of Ruby 2.1) can only be scoped to script bodies. The ability to be scoped to module and class bodies was removed shortly prior to the release of Ruby 2.0.0, together with the Module#using method.
And lastly, note that Object#send ignores refinements:
require_relative 'string_with_upper_reverse'
using StringWithUpperReverse
puts 'Hello'.reverse
# OLLEH
puts 'Hello'.send(:reverse)
# olleH

Extend a Model from a Rails Engine (not replace it)

I have a Rails app that uses a gem called ActsAsTaggableOnSteroids, which is a Rails Engine. Specifically, I'm using PavelNartov's fork of the gem. But nevermind that.
I need to add specific functionality to the Tag model, which is supplied by the engine.
But, according to my understanding of Rails engines and the magical loading functionality in Rails, if I put a file called "tag.rb" in my models directory, then it will completely replace the one from the Engine.
Ideally, I would be able to do something like:
class Tag < ActsAsTaggable::Tag
# my stuff
end
...but alas, that doesn't work because the model supplied by the engine is not namespaced.
So, I came up with this nightmare, which I put in app/models/tag.rb:
path = ActsAsTaggable::Engine.config.eager_load_paths.grep(/models/).first
require File.join(path, 'tag')
Tag.class_eval { include TagConcern }
But there has to be a better way! I feel like I'm missing something. I'd prefer not to add this strangeness to my app if possible.
Just require the file by looking up the path of the gem's model:
require File.join(Gem::Specification.find_by_name("bborn-acts_as_taggable_on_steroids").gem_dir, 'app/models/tag')
Tag.class_eval do
# ...
end

How to include a specific Rails method (or file) in a non-Rails Ruby project?

I would like to use the distance_of_time_in_words method in the date_helper.rb Rails file (see on Github) in an non-Rails Ruby project.
How can I include it? It requires other files, so how to include them?
I don't want to include all of Rails because that would slow down the development process.
Ruby 1.9.3
This method, distance_of_time_in_words is in actionpack/lib/action_view/helpers/date_helper.rb. So you should require 'action_view' and action_view/helpers to load this method. And the method is defined in module ActionView::Helpers::DateHelper, you can include it in your class. The method is an instance method.
require 'action_view'
require 'action_view/helpers'
class Klass
include ActionView::Helpers::DateHelper
end
c = Klass.new
c.distance_of_time_in_words( ...... )
If this is the only thing you want from it, then I'd just go take the source code and hack it to remove the dependencies (which appears to just be some I18n translations. To support the hack, you can probably translate this test suite.
Why would I do this instead of using the gem? Because it's just such an enormous dependency. It's so enormous that you actually notice it loading all that code. I'd rather rip out the method and hack it to work than depend on all of that (again, assuming this is the only thing you want from the lib).

Resources