gem namespaces within rails class - ruby-on-rails

I'm brand new to rails (and ruby) and having a a lot of trouble with accessing different namespaces. Specifically, I can't access the namespace of the flickraw gem from within a controller class:
class ImageSourcesController < ApplicationController
def show
list = flickr.photos.getRecent
...
end
end
Calling this method, I get the response:
undefined local variable or method `flickr' for #<ImageSourcesController:0x00000005006658>
I am using bundler, which I thought ensured that the methods of all gems in the gemfile are required by rails.
EDIT: I'm stupid, turns out I just needed to reset the server!

It is a good idea to create an initializer for flickraw:
# config/initializers/flickraw.rb
FlickRaw.api_key= ENV['FLICKR_API_KEY']
FlickRaw.shared_secret= ENV['FLICKR_API_SECRET']
If you are creating a open source app you may want to use ENV variables to store your API key and shared secret. The dotenv gem is a really nice tool for that.
You also seem to be confused about namespaces in Ruby. Ruby doesn't actually have namespaces in the same way as for example PHP which has a special keyword and namespace accessors.
Ruby has modules which act as both namespaces (grouping classes, constants etc.) and traits. Foo::Bar.create() is an example of accessing a class method on a "namespaced" class.
module Foo
class Bar
def create
end
end
end
Your flickraw example is simply accessing nested properties (which does'nt really have anything to do with namespaces):
flickr.photos.getRecent
Your taking the object flickr (which flickraw creates when we require flickraw) and sending it the message photos which returns a FlickRaw::Flickr::Photos instance.
We then send the message getRecent to flickr.photos

Related

Where is a good place to initialize an API?

I wanted to use this api: https://github.com/coinbase/coinbase-ruby and the first step is to initialize the API, like this:
coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
I was wondering what the best place to put this code is, and how would I access it if I put it "there"? I want this variable (coinbase) to be accessible ANYWHERE in the application.
Thanks!
The answer to this question really depends on your use case and your approach. My geral recommendation, however, is to create a Service Object (in the DDD sense) (see the section named "Domain Objects Should Not Know Anything About Infrastructure Underneath" in that link), that handles all communication with the Coinbase API. And then, within this service object, you can simply initialize the Coinbase::Client object once for however many times you call into it. Here's an example:
# app/services/coinbase_service.rb
class CoinbaseService
cattr_reader :coinbase_client, instance_accessor: false do
Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
end
def self.do_something
coinbase_client.do_something_in_their_api
end
def self.do_something_else
coinbase_client.do_something_else_in_their_api
end
end
So then you might do, e.g.:
# From MyController#action_1
if CoinbaseService.do_something
# ...
else
# ...
end
Or:
# From MyModel
def do_something
CoinbaseService.do_something_else
end
To get the service object working, you may need to add app/services to your autoload paths in application.rb file. I normally just add this:
# config/application.rb
config.autoload_paths += %W(#{config.root}/app)
I find this Service Object approach to be very beneficial organizationally, more efficient (only 1 invocation of the new Coinbase client needed), easier to test (easy to mock-out calls to Coinbase::Client), and simply joyful :).
One way to go about having a global variable can be done as similar as initializing redis in a Rails application by creating an initializer in config/initializers/coinbase.rb with:
$coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
Now, you can access $coinbase anywhere in the application!
In the file config/initializers/coinbase.rb
Rails.application.config.after_initialize do
CoinbaseClient = Coinbase::Client.new(
Rails.application.credentials.coinbase[:api_key],
Rails.application.credentials.coinbase[:api_secret])
end
In place of the encrypted credentials, you could also use environment variables: ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET']
The above will make the constant CoinbaseClient available everywhere in your app. It will also ensure all your gems are loaded before the client is initialized.
Note: I am using Rails 6.1.4.4, and Ruby 2.7.5

How do I make functions in a gem available to Sinatra views?

The question here asks how to extract Rails view helper functions into a gem, and the accept answer is pretty good.
I am wondering - how to do the same for Sinatra? I'm making a gem that has a bunch of helper functions defined in a module, and I'd like to make these functions available to Sinatra views. But whatever I try, I cannot seem to access the functions, I just get a undefined local variable or method error.
So far, my gem structure looks like this (other stuff like gemspec omitted):
cool_gem/
lib/
cool_gem/
helper_functions.rb
sinatra.rb
cool_gem.rb
In cool_gem.rb, I have:
if defined?(Sinatra) and Sinatra.respond_to? :register
require 'cool_gem/sinatra'
end
In helper_functions.rb, I have:
module CoolGem
module HelperFunctions
def heading_tag(text)
"<h1>#{text}</h1>"
end
# + many more functions
end
end
In sinatra.rb, I have:
require 'cool_gem/helper_functions'
module CoolGem
module Sinatra
module MyHelpers
include CoolGem::HelperFunctions
end
def self.registered(app)
app.helpers MyHelpers
end
end
end
This doesn't work. Where am I going wrong?
(And in case you're wondering, yes, I need the helper functions in a separate file. I plan to make the gem compatible with Rails as well, so I want to keep the functions isolated/de-coupled if possible).
You’re mainly just missing the call to Sinatra.register (in cool_gem/sinatra.rb):
require 'sinatra/base'
require 'cool_gem/helper_functions'
module CoolGem
# you could just put this directly in the CoolGem module if you wanted,
# rather than have a Sinatra sub-module
module Sinatra
def self.registered(app)
#no need to create another module here
app.helpers CoolGem::HelperFunctions
end
end
end
# this is what you're missing:
Sinatra.register CoolGem::Sinatra
Now any classic style Sinatra app that requires cool_gem will have the helpers available. If you use the modular style you’ll also need to call register CoolGem::Sinatra inside the Sinatra::Base subclass.
In this case, if you are just providing some helper methods, an easier way might be to just use the helpers method (again in cool_gem/sinatra.rb):
require 'sinatra/base'
require 'cool_gem/helper_functions'
Sinatra.helpers CoolGem::HelperFunctions
Now the methods will be available in classic style apps, and modular style apps will need to call helpers CoolGem::HelperFunctions. This is a bit simpler, but if you are adding methods to the DSL context you will need to use registered as above.

Convention For Gem with Common Name

I recently authored a gem called 'setting' (found here). The extends ActiveRecord with a module named 'Setting'. I understand that gems are supposed to use the namespace they are named, however when testing this caused collisions with ActiveRecord models with the same name (a Setting model). Does a standard exist for creating a private module namespace? I don't need users of the gem to ever access the module outside the extension in ActiveRecord. Do I have any options outside of picking a less common name?
Since you're writing an Active Record extension, you could place your module inside the ActiveRecord namespace:
module ActiveRecord
module Setting
end
end
Other than that, no, there's no practical namespace solution for gems with very common names.

Finding out where methods are defined in Ruby/Rails (as opposed to Java)

I am just getting started with Ruby on Rails. Coming from the Java world, one thing that I am wondering is how do Ruby/Rails developers find out where methods are actually defined.
I am used to just clicking on the method in Eclipse to find where is is defined even in third party libraries (supposing I have the source code).
A concrete example: I am trying to find out how the Authlogic gem apparently changes the constructor of my User class to require an additional parameter (called :password_confirmation) even though the User class doesn't even inherit from anything related to Authlogic.
Probably I am just overlooking something really obvious here (or maybe I still can't wrap my head around the whole "convention over configuration" thing ;-))
It's slightly difficult to quickly find the method location for dynamic languages like Ruby.
You can use object.methods or object.instance_methods to quickly find out the methods.
If you are using Ruby 1.9, you can do something like this:
object.method(:method_name).source_location
For more information on source_location - click here
The Pry gem is designed precisely for this kind of explorative use-case.
Pry is an interactive shell that lets you navigate your way around a program's source-code using shell-like commands such as cd and ls.
You can pull the documentation for any method you encounter and even view the source code, including the native C code in some cases (with the pry-doc plugin). You can even jump directly to the file/line where a particular method is defined with the edit-method command. The show-method and show-doc commands also display the precise location of the method they're acting on.
Watch the railscast screencast for more information.
Here are some examples below:
pry(main)> show-doc OpenStruct#initialize
From: /Users/john/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/ostruct.rb # line 46:
Number of lines: 11
visibility: private
signature: initialize(hash=?)
Create a new OpenStruct object. The optional hash, if given, will
generate attributes and values. For example.
require 'ostruct'
hash = { "country" => "Australia", :population => 20_000_000 }
data = OpenStruct.new(hash)
p data # -> <OpenStruct country="Australia" population=20000000>
By default, the resulting OpenStruct object will have no attributes.
pry(main)>
You can also look up sourcecode with the show-method command:
pry(main)> show-method OpenStruct#initialize
From: /Users/john/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/ostruct.rb # line 46:
Number of lines: 9
def initialize(hash=nil)
#table = {}
if hash
for k,v in hash
#table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
pry(main)>
See http://pry.github.com for more information :)
None of people advising Pry gem mentionned the method called find-method, which is probably what author was looking for.
Here's the example:
pry(main)> find-method current_user
Devise::Controllers::Helpers
Devise::Controllers::Helpers#current_user
WebsocketRails::ConnectionAdapters::Base
WebsocketRails::ConnectionAdapters::Base#current_user_responds_to?
Then, you can browse the method code by following #banister's tips.
You could use something like pry. See its railscast also.
There are several ways to change an existing class. E.g. if you want to modify the String class write:
class String
def my_custom_method
puts "hello!"
end
end
But there are other options like mixing in modules or adding/modifying methods by using meta-programming.
Anyhow, having some object you can always:
puts obj.methods.inspect
Either do it in your code or use the debugger.
The other option is to read the code. In particular you should read the gem's unit tests (./spec, ...). There are quite a lot of authors stating that unit tests make documentation obsolete.
In Ruby you can also add both class and instance methods to a given class by using mixins.
Essentially if you have a module you can add its methods to a given class using both include and extend class methods. A brief example on how those works is the following
Module A
def foo
"foo"
end
end
Module B
def bar
"bar"
end
end
Class YourClass
include A
extend B
end
p YourClass.new.foo # gives "foo" because the foo method is added as instance method
p YourClass.bar # gives "baz" because the bar method is added as class method
Because Ruby is a dynamic language, these statements can be used everywhere. So to come to your question there is no need to extend an authlogic class to get its methods. Many plugins uses this instruction when loaded
ActiveRecord::Base.send :include, ModuleName
In this way they tell to every AR object to include some plugin defined module and you get all the methods in AR objects.
Another technique used by many acts_as plugins is to include their modules only when the acts_as call is used in the base class.
Other useful references
What is the difference between include and extend in Ruby?
A quick tutorial about mixins

Some question about classes in plugin

I am using Ruby on Rails 3 and and I am trying to implement a new plugin. In order to learn, I am viewing inside and I am studying some popular plugins.
What I choosed is WillPaginate and in a its file there is something like this:
module WillPaginate
class << self
...
end
end
if defined? Rails
WillPaginate.enable_activerecord if defined? ActiveRecord
WillPaginate.enable_actionpack if defined? ActionController
end
I would like to know
Why the if defined? Rails statement is outside the module statement? When will be run istructions inside that?
What means and how can\should I use class << self?
module WillPaginate defines Ruby name scope and groups these methods so they can be later included with one call into some class. The if defined? Rails is outside the module because the code inside that if might include the whole module into some ActiveRecord class. And the if is executed exactly at the time when will_paginate.rb file is loaded.
All methods in that block are class methods. So later it is possible to make calls like YourModelClass.foo.
The if defined? Rails block is evaluated at load time, ie during require 'will_paginate'. That allows will_paginate to be used with or without Rails.
The class << self section is a way to define a group of methods on the WillPaginate module without having to define them all as def self.method_name. Either way works (except for a few edge cases I can't remember now), so it's mostly just a style choice.

Resources