0

When an activerecord for a "category" gets created in my rails app, I need to send the data immediately to an external system via a rest api. Currently I have the rest client api call in the after_commit callback of my "category" model.

Is this the best practice in general or is there a better pattern to use?

If so, how do I prevent the api call from executing each time I seed the database for my specs?

class Category < ActiveRecord::Base

    attr_accessible ............

    ....more stuff....

    after_commit :api_post, on: :create

    def api_post

        ms = RestClient.new()

        ms.post :category, self.to_json(:root => true)

    end

end
phil
  • 127
  • 2
  • 11

2 Answers2

0

Don't send it unless you're in production:

def api_post

    if Rails.env.production?
        ms = RestClient.new()
        ms.post :category, self.to_json(:root => true)
    end

end

This will skip it for development and test environments. The if check can be moved around as desired (perhaps to be around the after_commit instead, if it suits).

Nick Veys
  • 23,458
  • 4
  • 47
  • 64
  • Thanks Nick! This would work great in general (although as a matter of general practice conditioning out code like this per environment could introduce blowback). I'll go with it for now. However, what do you think my rspec example would look like for this callback to define the expectation that the api call went through correctly? Should I omit the example at the model level and only do an example at the lower level for the RestClient itself? Thnx! – phil Sep 13 '13 at 15:36
  • A more comprehensive test would mock out the RestClient object. Then you both avoid the actual API call, and verify that it will happen. [Here's a question/answer doing just that](http://stackoverflow.com/questions/14267373/stubbing-restclient-response-in-rspec). – Nick Veys Sep 13 '13 at 16:22
  • Yes, next step to mock out the RestClient. Thanks so much for the followup and reference to mocking a restclient. This brings up another question (which I'll post as a new question) of how would one fully test and exercise the external api itself to be sure it works considering we are mocking it out for the internal app integration tests? – phil Sep 13 '13 at 19:19
  • Short answer, you don't. Long answer, it's a service, it should have its own tests, if you tested it your tests would be a nightmare. Realistically, it's an interface boundary, so you need to test your code for successful paths, and failure paths, and mock objects let you do both. If you mean how do you test to make sure you are using properly, that would be something you'd have to smoke out in an integration or maybe staging type environment, but basically, write tests carefully and you'll get 95% of the way. – Nick Veys Sep 15 '13 at 01:53
  • Got it.. Thanks for clarifying! Setting expectations for the service itself would not be in scope for "test driven development" of this particular app but would be in scope for TDD on the other side by the external API developers. Perhaps somewhere in between there is a QA benefit for a stand alone test suite against the API not associated with TDD/BDD. Thnx for helping to hash this out! – phil Sep 16 '13 at 14:20
0

My first thought is that you should probably do those api calls in controllers rather than in models, since it's request related (and controllers are all about handling requests, after all). But this does not answer your question.

I assume you want to use that seeding in production, else @Nick answer is correct.

You could pass an environment variable when running the seed task :

SEEDING=true rake db:seed

Then, you can use it in your model :

def api_post
  unless ENV[ 'SEEDING' ].present?
    ms = RestClient.new()
    ms.post :category, self.to_json(:root => true)
  end
end

Certainly not the most elegant thing, though ...

kik
  • 7,867
  • 2
  • 31
  • 32
  • Yes, thought about putting the api calls in the controller but the FMSC principle (fat model skinny controller) leads me into the model. I suppose one could argue the MVC question both ways. In any case at the controller level I can avoid the issue with seeding the db. Wonder what the trade offs are between your approach with new environment variable vs. using existing rails env variable. (All things being equal, would tend to lean in the direction of fewer new things to support.) Thanks! – phil Sep 13 '13 at 15:48
  • I was expecting FMSC comment, that's why I mentioned the request handling role of controllers ;) In my own apps, I usually use context classes, which encapsulate validations and callbacks, so the problem is avoided (and I can have validations on specific situation - validating a user from admin is not the same as validating self from frontend). I don't like fat controllers, but I don't like fat models either :) (especially when codebase becomes several years old) – kik Sep 13 '13 at 16:20
  • Thanks for mentioning context classes. Will look into context classes as an option to trim down models without bloating controllers. Am seeing a number of "best" practices floating around on this challenge. – phil Sep 13 '13 at 19:29
  • Best practices are yet to emerge, here. Rails have long time evangelized against fat controllers, but we now see long term applications being totally bloated on models. The core team response to this is to encourage concerns, in rails-4. DCI was an other answer, but had performance issues due to the dependency graph being constantly changed. Using only context classes is something that works well for me, but we don't have a good and standardize answer yet. – kik Sep 13 '13 at 21:07
  • Thank you so much for the detailed background on this! Let's see what happens. – phil Sep 16 '13 at 19:16