Best practice for wrapping a third party service in a library - ruby-on-rails

I am interested in writing a library for a third party API I am using and I need some advice. The average use of the library will involve several api calls in one request. For example, one api call to grab a user from the third party service, and then another call to use that user to grab his/her photos. Each API call will get its own library method wrapper with additional logic to handle errors/timeouts, but my biggest question is whether the library should be made as a singleton that contains state or just as a series of class methods.
For example:
user_id = ThirdParty.get_user("abc#gmail.com")
photos = ThirdParty.get_photos(user_id)
OR
thirdpartyservice = ThirdPartyService.new("abc#gmail.com")
photos = thirdpartyservice.get_photos
These doesn't have to be the exact deseign of the library, but I just am confused about the pros/cons of each approach. Any help would be amazing!
Btw, I am using ruby!

I would have the library contain the state as this reduces the complexity of code on the user side(and that's what API are supposed to do, increase simplicity). With this approach, the user doesn't have to keep track of that user_id since the library is keeping state of that.
If the user really wanted their user_id (or any other data that the library stores), you can just create an attr_reader in your library to expose that data.
To add fleixiblity for the get_photos method, you can do something like:
class ThirdPartyService
def get_photos(user_id=#id_stored_in_library)
# do work
end
end
This way it defaults to the stored id, however it adds flexibility in that the user can specify the userid if he so chooses.

You need state (host, etc) and behavior based on that state so you should use objects, not one singleton object.
As mentioned, you should not name methods like get_photos, just photos.

I believe the best practice is using provider and services for the ability to not be tied down to a specific service provider. Instead of simply wrapping the library you may want to abstract it a bit and by default only allow a single service provider.
Although inheritance isn't really needed in a dynamic/duck typed language like Ruby this may help concrete the 'why'.
class MailProvider
def initialize(args); raise "Not Implemented"; end
def send_mail(args); raise "Not Implemented"; end
end
class SendGridService < MailProvider
def initialize(args)
# Use args here
end
def send_mail(args)
# Use SendGrid's API or Gem here
end
end
Then, in your code you can have something like this:
config.mail_provider = SendGridService.new({
username: ENV['SENDGRID_USERNAME'],
password: ENV['SENDGRID_PASSWORD']
})
config.mail_provider.send_mail({ subject: 'woot', message: 'Look ma, I did it!' })
Then six months later when your users love your Gem/library but want MailChimp support you can simply create a second service 'MailChimpService' and allow your clients to use the new provider.

Instead of get_ set_ methods will be better to use getters and setters. It is ruby standard (not sure, but get_ and set_ methods I saw only once in ruby code).
If you don't need to save some state between requests, make it static. But it depends on many factors. Can you tell us what API you need to wrap ?

Related

Where exactly should I fetch tweets? (controllers, models, helpers, ...)

Although experienced with another frameworks, I'm a rails newbie. I've come upon the main twitter gem and want to use it. I understand the code... but I don't understand where exactly I should work it, at all (I've read plenty of rails but lack of practical examples).
I want to fetch the tweet info (post, user, etc)
Save the tweet to the database using postgre
I have a 'publication' (about the tweet) and 'publication' model, amongst helpers and so on.
Could someone please walk me through on how to do this? I'm not asking for you to do my work. Just please explain me the thought process of rails via a lazy example because I'm not understanding how to work with the gem this way... Thank you very much for your help :)
max's comment is exactly right, but to help put it into context, let me illustrate how, in your situation, you can build and apply a service object.
First, consider what your service object will be doing, and pick a name for it (you'll often change this later, as you figure things out better). For example, TweetFetcher. Then, decide what information it needs before it can do its job. I'm not clear on which tweet(s) you want to fetch, from your question, but let's assume it just wants to fetch the latest tweet for a given handle. Then, your object can start out like so:
class TweetFetcher
def initialize(handle)
#handle = handle
end
end
Now, this file can go anywhere Rails will automatically load it. The lib/ folder is pretty standard (e.g. lib/tweet_fetcher.rb), but you may need to add lib to your autoload paths. Even simpler is to throw it into the app/models folder, though that's a bit confusing for future developers.
Next, make it do its job. You'll need to add a new method to your class which "calls" it; a standard name is call but you can pick what you'd like. At this point, I'd suggest you write tests for your service object, just like you would for a model, but I won't get into details on that. At the end of the process, your code will look like:
class TweetFetcher
def initialize(handle)
#handle = handle
end
def call
# not real code at all
tweet = Twitter::Client.fetch_latest_tweet(#handle)
Publication.create!(tweet_id: tweet.id)
end
end
Rock-solid. So, the next question is, when to call it? In general, I'd suggest calling service objects from your controllers. For example, say this is all supposed to happen when a user submits a form that they just entered a twitter handle into... say, POST /publications, which hits PublicationsController#create:
class PublicationsController < ApplicationController
def create
# validate params, w/e
#publication = TweetFetcher.new(params[:handle]).call
flash[:notice] = "aaaand done."
end
end
However, you could call your service object from anywhere -- that's the beauty of building them. It allows you to extract, encapsulate, and re-use code in any context. It also makes it way easier to test your code, as I think you'll find. Hope this helped, and good luck!

Rails - Force model to be created via factory method

I'm using Rails 4. I have a class, Cart, which needs to be accessed within my application.
I want it accessed using the factory pattern:
class CartFactory
def self.obtain_cart_for_user(user)
...
end
end
I need this approach because sometimes, I want to return an existing cart and sometimes create a new one (based upon the age of the cart, its contents, whether the products in it are still available etc).
This is easy enough.
However, I also want to make sure some other future programmer doesn't instantiate a cart directly, or fetch one by any other means, including via model associations, such as:
Cart.new(...)
user.carts.new(...)
Cart.find(id)
Cart.find_by_attribute(blah: blah)
Cart.where(...).first
Is there any way to prevent that?
Well, it's possible to make the constructor private:
private_class_method :new
And of course, you can try making the ActiveRecord query methods (.find, .where etc.) private as well. But to me that sounds like a good way to end up with erratic behaviour. If you were to go this route, make sure your app is thoroughly tested first.
Another route would be for Cart not to extend ActiveRecord::Base (which I'm assuming it does), and instead include only the parts you need, like ActiveRecord::Persistence. If you are willing to dive in deep, check out the parts that are included in the source for ActiveRecord::Base.
Edit: Still one option would be to make Cart itself private within a module that only exposes CartFactory. There's no built-in syntax for a "private class", but it's possible to achieve since Ruby classes are just regular objects. Again, no idea how well ActiveRecord would deal with that.
But lastly there is of course the question of whether you want to do this at all. In general, Ruby is not very good at protecting you from yourself. :) As expressed in the latter linked answer, documentation and trust go a long way.

Where do API calls go in a ruby on rails MVC framework project?

I have an application in Ruby on Rails with mvc framework. As of now, I have API calls in the controller but don't think this is the right place for them. What kind of file should all my API calls go in? Thanks
def getDetails(id)
api_response = HTTParty.get(base_uri, :query => {:DID => id, :DeveloperKey => devKey})
#json_hash = api_response.parsed_response
return #json_hash
end
API calls to external services (3rd party) are not specific to your app, as their service is available to everyone (in theory). It is my understanding that these sorts of features go in the lib/ directory because they are not app specific. Ideally you could then pull out the code from your lib in your project, and drop it into someone else's lib/ in another project and it would still work just fine.
Put the call in the lib/. If you want, you can create the a model from the returned data in your controller.
It would look something like this:
app/controller/
class YourController < ApplicationController
def getDetails
# keep in mind, api call may fail so you may want surround this with a begin/rescue
api_response = YourApiCall.new.get_details(params[:id])
# perhaps create a model
#model = SomeModel.new(fname: api_response[:first_name], lname: api_response[:last_name])
# etc...
end
end
lib/
require 'HTTParty'
Class YourApiCall
def get_details(id)
HTTParty.get(base_uri, :query => {:DID => id, :DeveloperKey => devKey})
#json_hash = api_response.parsed_response
return #json_hash
end
end
Very late to this one, but thought I'd add my 2p/2c.
I like to try to keep my controllers clean of anything apart from controller code, which I loosely define as program flow code based on request type and parameters. For example, choosing the correct template for the request type or choosing the correct method to call based on whether a user is logged in or not.
When it comes to calculating the responses, I don't like to litter the controller with lots of code that manipulates models and sets instance parameters. This is hard to test and even harder to re-use. I prefer to defer to another object and return a single value object to the template.
Sometimes I can defer to a model: maybe it's a simple look-up and I'm just sending a single model to the template, or an array of models.
Maybe I've implemented a useful method in a model to return an appropriate value or value object.
However sometimes I'm doing something that doesn't use a model, or that uses a several models, or that doesn't feel like it should actually be cluttering up the model. In this case, neither the controller nor a model is an appropriate place for the code.
The lib directory doesn't feel right either. I tend to treat the lib directory as somewhere that contains code that I haven't been bothered to turn into gems yet. If the code I'm writing only makes sense in the context of the application, it doesn't sit well.
So I turn to service objects. Under the 'app' folder I have a 'services' folder, which contains small, functional classes that encapsulate single chunks of site behaviour. (Or sometimes, coordinate several other services to provide a simple interface for the controller.)
This allows me to slim down my controllers AND my models, and makes a perfect place to put code that needs to contact an API.
If you wanted to go one step further you could wrap the API itself in a wrapper class (or set of classes) and keep those in the lib directory (for conversion to a gem at a later date perhaps). Then the service object would perform the task of calling the API wrapper with the appropriate values (passed from the controller) and responding with something that a template can interrogate cleanly.
Of course, you can go further than this and add more layers. A presentation layer, for example, could sit between the service object (providing generic values) and format data for a specific view. (Maybe you want to provide both a web page and an RSS feed and they need different date formats for example.)
But you get the idea.
By my coding style (and understanding of MVC), external calls would be placed in a "tableless" model. RailsCasts 193 talks a bit about this concept, and a less clunky syntax is supported in Rails 4. If you need to have any manipulation of the code, the model seems like the logical place to place these. Moving those methods into the controller would work, but could create problems as your app grows.
Another consideration with external API calls is actually storing those in a database, which would should definitely be in a model at that point, so (to me) it becomes clearer that these really should be in the model.

How do I log particular events from Rails to a log file?

Requirements
I would like to keep an audit log of particular events, such as:
User logged in successfully
User could not log in (with reason: wrong password, unconfirmed, etc.)
SuperUser modified another users details (with what they changed)
etc.
This log should include details such as:
The logged in user performing the action (based on the controller's current_user)
The record being modified (such as the other user record in the case of super users)
The IP address of request for that action
This log should also be stored in a file on the filesystem, not a database table so it can be ingested by another service on the machine at a later time.
Some possibilities
Here is a short list of the approaches I have considered so far:
ActiveRecord::Observer
Observers give a nice separated way of watching for these particular events.
I could then get the observer to append to a log file, but I'm not sure how easy I would be able to obtain the result of such a call (such as login failed or worked) and I would need to somehow call the controller method current_user to find out the logged in user and get the HTTP request to obtain the IP address.
An auditing gem (such as audited, auditable, paper_trail, etc.)
These gems have the convenience of knowing how to get access to the controller for the current user and IP address, but they all log to an audit table in the database. Auditable is particularly nice because it can audit any method call on an object, not just an AR callback, but I may need to patch it to write to a file instead of the database.. or something?
ActiveSupport::Notifications
I still need to read up on this, but I believe it offers low-level a way of subscribing to low-level events within rails. This might be too low-level for this situation, but I need to investigate further.
Log4r
It seems this will make a nice log file, but I don't think it has any way of watching for events. This would only be part of the problem.
Any advice?
Is there a best practices way of doing this? Can you recommend any gems or lessons learned from previous experience? Anything else I should consider?
Thanks to everyone for the responses.
Casper, I did decide to build something custom.
I see your point with writing to a local db anyway, but the requirement for this project is to dump log files so a more elaborate log parsing service can interrogate the files and even combine them with information from other sources.
In order to get logging from both models and controllers, I ended up making a module that I would include in both the observers and ApplicationController.
The module looks a little something like this:
module MyEventLogger
mattr_accessor :logged_current_user
mattr_accessor :logged_remote_ip
def log_event(message)
##logger ||= Logger.new(Rails.root.join('log', 'audit.log'))
##logger.info "#{Time.now} | #{logged_current_user}##{logged_remote_ip} | #{message}"
end
def logged_current_user
##logged_current_user || "SYSTEM"
end
def logged_remote_ip
##logged_remote_ip || "NO IP ADDRESS"
end
end
ApplicationController would have:
include MyEventLogger
before_filter :setup_logger
...
def setup_logger
MyEventLogger.logged_current_user = current_user
MyEventLogger.logged_ip_address = request.remote_ip
end
The observer would just have to include MyEventLogger and it would have access to the log_event method and the current user and ip address. For example:
class UserObserver < ActiveRecord::Observer
include MyEventLogger
def after_save(user)
log_event "The User #{user} was saved by #{logged_current_user}"
end
end
Some thoughts:
The auditing gems sound closest to what you want. If you look at their source code they are not that complex, and most seem to actually be built around Rails Observers. You could easily use their code as a base to create your own specialized versions.
If you look at he code of Audited for example you will see it's actually quite simple to store the current_user (if you decide to roll your own implementation):
https://github.com/collectiveidea/audited/blob/master/lib/audited/sweeper.rb
I don't think having the auditing data in a DB is necessarily bad. You might actually find that useful one day when you need to track down some complex problem from thousands of previous transactions. You can always create a simple Rake task to dump the data into a logfile format when needed.
However I will say that Log4r is quite nice, and I've used it myself in a couple of projects. But I did not have an audit-type need. Only basic logging for debugging and troubleshooting.
You COULD also consider combining something like your own Observer-type system with a Log4r driver instead of a DB driver, if that's what you want. Because it does sound a little bit like you might need to trigger logging events outside of what the Observer system provides anyway. Which means you're going to have to either implement extensions to existing gems, or use a gem as a base and extend it with your own functionality.
Anyway - I still see the DB approach as actually a benefit, and kind of cool to be able to run queries on your audit-trail. Stuff like that can't hurt (Log4r btw. supports custom "output drivers" also, so even that could be used to log into a DB).
Just sharing my personal experience with this:
I created something very similar to what you mention here. I used a table in the database, trapped the relevant post request in the application_controller, and passed the info to the model associated with my log table. The logic was quite trivial, and I had all the control I wanted. The only effort was in selecting/ rejecting particular transactions, and restructuring all useful params to fit nicely in a text field.
If you decide to take that route, I will be happy to share more details.
Good luck.
One way of doing that, is wherever you want to run a logged action, run it in a block
I really recommend looking at Chapter 18 of Eloquent Ruby by Russ Olsen http://books.google.com/books?id=-s2xL0pVsLUC&lpg=PA219&ots=l7I3oAK3M2&dq=eloquent%20ruby%20chapter%2018&pg=PA219#v=onepage&q&f=false and maybe 'Using Blocks' in Chapter 2 of Gregory Brown's Ruby Best Practices http://majesticseacreature.com/rbp-book/pdfs/rbp_1-0.pdf
e.g.
def with_logging(description)
begin
#logger.debug( "Starting #{description}" )
yield # this is when the code in the block executes
#logger.debug( "Completed #{description}" )
rescue
#logger.error( "#{description} failed!!")
raise
end
end
with_logging('code example') { puts "just printing something" }
Also: It might be worth looking into tools like graylog http://graylog2.org/about/gelf (and see http://arrrrcamp.be/videos/2011/lennart-koopmann---managing-the-logs-of-your-rails-applications/ ) or these posts http://openmymind.net/2012/4/4/You-Really-Should-Log-Client-Side-Error/ (javascript), https://github.com/TwP/logging , http://amon.cx/

How to choose a single model and persist that choice?

I have a simple model called Party with a corresponding table called parties. There's also a controller with all the usual CRUD actions and so on. This model is used in a website and only one admin user is allowed to edit the parties - everyone else is allowed to call GET actions (index, show). Nothing special so far.
Now I need to do the following: The admin would like to choose a single Party at a time for special presentation (the selected Party is showing up on the start page of the application). The most important thing is, that there's only ONE party at time selected.
How would you solve this problem? Boolean Flag in Party model? Save the selection (id of the party) somewhere outside the database? Implement a new model with a has_one relation to Party (seems like overkill to me)?
I hope my explanation is good enough to understand the issue.
A simple "front_page" attribute would suffice or another model like you mentioned, using the has_one relationship would be fine as well.
Using another model would allow you to maintain some more information, like how long should it remain on the front page (expiration date?) or how many times it was featured (assuming a party can be featured twice). It really depends on other requirements for your system.
You also might be able to get away with a simple implementation of the Singleton pattern as well. There's a quick description on the Rails Wiki of making an ActiveRecord object a Singleton (see below): http://wiki.rubyonrails.org/rails/pages/TipsAndTricks
Making a singleton ActiveRecord object
If you have a table with just one
entry, useful for keeping track of a
number sequence for databases without
sequences, you can use the singleton
module included with ruby like so:
require 'singleton'
class Master < ActiveRecord::Base
include Singleton
def initialize(args=nil) super(args) if record = Master.find(:first)
self.attributes = record.attributes end end def next_tracking_number increment!
(:current_tracking_number) current_tracking_number end def
self.next_tracking_number instance.next_tracking_number
end
end
Update:
This is a very poor code example (was copied and pasted from the Rails Wiki, which had no formatting). I would highly recommend the [Ruby Design Patterns] book which tackles many of the GoF design patterns in greater detail (while making them applicable to Ruby applications). But Google should return you some good resources for using the Singleton pattern in Ruby.2
I would go for the boolean flag and create nested singleton resource (promoted), which I would implement in PartiesController itself (set_promoted_party and get_promoted_party actions). For these I would create two new routes:
PUT /parties/promoted/:party_id # to set the promoted party
GET /parties/promoted/:party_id # to get the promoted_party
I would add a second model that had a has_one relationship in order to keep the app RESTful and simple. Also, this way, you can keep a history of special Parties, and track other meaningful information related to the special parties.
Personally I'm very strong on data integrity being enforced by my database so would probably add that extra table and enforce it as a foreign key constraint there.
It can seem like overkill, but is the only* solution that prevents data integrity issues.
Could you maybe add it as a field to the admin table/model - which would be an enforced foreign key to the party table?
*Another solution would be a database trigger that checks no other rows are the selected party but I tend to shy away from such solutions.
Keep it simple. Put a promoted_party.yml file in your config directory that the controllers write to and read from. The contents can be as simple as this:
---
party_id: 123
Done. If you need more integrity or fancier relationships later, implement that later, not now.
For deployments, just make sure the file is symlinked to a shared directory to survive application upgrades.

Resources