How to inform the models of the api version? - ruby

Currently, I use api-versions gem which nicely creates api routes and contains the version information in the request HEAD.
However, sometimes the models need to know the api version being used. Example, validations. Let's say presence of username needs to be validated on any version >= 2, but not on versions < 2.
In the Controller layer, it's easy to retrieve the api version by parsing out the appropriate request HEAD. But what's the best way to communicate that version number to the model?

How about defining a psudo-attribute in the model class with validators acting conditionally
class SomeModel < ActiveRecord::Base
attr_accessor :api_version
validates_presence_of :username, :if => :api_version > 2
...
Then from the controller, set the api_version on the model before saving etc
#someModel = SomeModel.new(params[:some_model])
#someModel.api_version = <<extracted from HEAD>>
#someModel.save!

Related

Adding ActiveRecord validations to PaperTrail's Version model?

I'm trying to add a validation to PaperTrail::Version which will prevent sensitive data from being stored in the versions table. The idea being you'll get lots of obvious errors if you forget to sanitize your has_paper_trail call within your model.
If I add a custom validator in config/initializers/paper_trail it works ... for a while. Then PaperTrail starts acting with its default behavior and my methods are undefined.
Example Code:
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version
# Ensure no sensitive values end up in the versions table
validate :prohibited_attributes
...
Try a custom version class. See documentation section 6.a. Custom Version Classes.
6.a. Custom Version Classes
You can specify custom version subclasses with the :class_name
option:
class PostVersion < PaperTrail::Version
# custom behaviour, e.g:
self.table_name = :post_versions
end
class Post < ActiveRecord::Base
has_paper_trail :class_name => 'PostVersion'
end
Using PaperTrail::Rails::Engine.eager_load! was a good idea. Not sure why that didn't work for you. Hopefully this is a workaround.

Rails - Contextual validations based on state

Let's say there is a post model like this:
class Post < ActiveRecord::Base
.......
.......
end
It has two attributes :title and :body.
Now a Post object can go through multiple stages: 'draft' -> 'published'.
Now while saving a post in drafts mode, the :title isn't required. But while saving it in published mode, it needs to have a presence validation on the title:
validates_presence_of :title
Now, what is the best way to do this in Rails? I think some implementation of a decorator pattern would be great, wherein in a controller, I would dynamically add validations to an active record object.
This is a simplified version of a bigger problem I have. In the actual case, there are a lot more validations including those done on associated objects.
If you are using state_machine for your state transitions, it supports just what you are looking for, with examples in the readme file.
Otherwise, all rails validations have optional if paramter. If, for example, your post has a published? method that returns whether it's in a published state, you could write validates_presence_of :title, if: :published? and have it do exactly what you need.

Rails custom routing via Model String Key (instead of id)

I am trying to set up an application where I can route via a String key instead of an id attribute. To illustrate this, please consider the following:
I have a class Foo which inherits from the ActiveRecord::Base and is implemented as follows:
class Foo < ActiveRecord::Base
attr_accessible :subject
Subject is a string type which exists in my database.
I have a controller which I implement as follows:
class SubjectController < ApplicationController
def index
#snip
end
As you can see, the SubjectController inherits from the ApplicationController.
Running rake routes gives me the standard (GET, POST, PUT, DELETE) routes for my subject. This is the expected behavior and I understand this functionality.
I want to know how I can extend the routes.rb file so that I can use a string url in order to access a subject. For example:
Instead of typing in localhost:3000/subject/1, I would like this /:id to resolve when I type in the url: localhost:3000/subject/grumpy-cat-says-hello
What does this implementation look like?
How should I setup my routes.rb file to accommodate this?
How should I configure my application to allow for this type of implementation?
Thank you in advance.
I've always used https://github.com/FriendlyId/friendly_id for this stuff.
If you prefer something simpler, this will do
class Foo < ActiveRecord::Base
def to_param
[id, subject.parameterize].join("-")
end
end
Then you can access your resource with: localhost:3000/foos/1-grumpy-cat-says-hello
Basically, since the name of the resource still starts with a number, it will be converted to a number by Rails and everything will work seamlessly.
This Gist goes in much greater detail about this topic :)

belongs_to a specific version

I need to store the specific version of a model with an order. I'm planning to use a versioning gem like paper_trail or vestal_versions. I'd like the correct version automatically loaded with the order.
Ideally, I'd simply store the object with order.update_attributes(:stuff => bought_stuff) and the order would remember the version of the stuff so that subsequent loads would make order.reload.stuff still be the object as it was when the order was saved.
Is there a gem that would provide such a functionality? I couldn't find one.
Otherwise, how can I achieve that with ActiveRecord and a versioning gem?
Actually, I could achieve almost what I want with PaperTrail and this :
class Stuff < ActiveRecord::Base
has_paper_trail
end
class Order < ActiveRecord::Base
belongs_to :stuff
def stuff_with_version
stuff_without_version.version_at(created_at) if stuff_without_version
end
alias_method_chain :stuff, :version
end
Not sure this is necessarily the best design for you, but you could use paper_trail for this. Simply add the macro method 'has_paper_trail' at the top of your model class and any time an instance changes, a serialised copy of it is created in a table called "versions" along with a polymorphic relationship back to the actual model.
Supposing you want to relate a particular version of a 'product' to an order, start by adding a relationship to the versions table - i.e. a migration that adds a 'version_id' to your order, and then set up the relationship as follows:
class Order
belongs_to :version
def product
version
end
def product=(p)
version=p.versions.last
end
end
class Product
has_paper_trail
end
Using this, when you add a product to an order, it will relate the order to the latest version of the product instead. When you retrieve the product, it will pull out the version; i.e. the product as it was when you created the order. Getting the relationship to work the other way around (i.e. relating products back to orders) might be more complicated, but this is a start.

Single Table Inheritance and routing in Ruby on Rails 3.0

I am having some trouble getting routing to play nicely with Single Table Inheritance in my Ruby on Rails application. I am using Ruby 1.9.2 and Rails 3.0.6. This is in development so the back-end is SQLite3, in case that makes a difference.
Let's say I have two products, widgets and sprockets. My application keeps track of bug numbers and support case tickets for both the products but the bugs and support tickets themselves are stored in other systems. There are two separate teams that work on these two products.
I have implemented Single Table Inheritance for the two types of bug records because the validation rules for widget bug numbers and sprockets bug numbers are different (the two teams use different bug tracking systems) and there is a possibility that I will have to add further products to the application that behave wildly differently. Using STI gives me the flexibility to implement additional methods and properties as required.
The widgets team only cares about widgets information and the sprockets team only cares about sprockets information. There is a third team that needs to be able to view the information on both the widgets and the sprockets. The widgets team will access the application using the path /widgets and the sprockets team will access the application using the path /sprockets. I set this up in routes.rb using namespaces:
resources :bugs
namespace "widgets" do
resources :bugs
end
namespace "sprockets" do
resources :bugs
end
I have set up the following models which work as expected when I fire up irb and use WidgetBug.create() or SprocketBug.create():
bug.rb
class Bug < ActiveRecord::Base
end
widget_bug.rb
class WidgetBug < Bug
# Some validation rules
end
sprocket_bug.rb
class SprocketBug < Bug
# Some different validation rules
end
I used scaffolding to create the controller and the view for the bug object, then modified the controller to try to generalize it so it could be used with both widgets bugs and sprockets bugs. For example, the index method looks like this:
def index
# The scaffold code lists all bugs, which is not what we want
# #bugs = Bug.all
# Only return bugs of the subclass we're looking for
#bugs = eval("#{params[:controller].classify}.all")
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #bugs }
end
end
I then used create() to populate the database with several bugs of each type. Unfortunately when I browse to /widgets/bugs, the bugs for both products appear. After some debugging, I determined that the classify call is returning Widgets::Bugs or Sprockets::Bugs, so when I call all on it, it appears to be running against the superclass instead of running against the subclass.
I've reviewed the routing documentation and done quite a bit of searching on Google but I'm still at a loss as to how I can change the routing or the controller to make this work correctly. Any help would be greatly appreciated.
Checkout this post: STI, one controller
Routes
resources :widgets, :controller => "bugs", :type => "Widget"
resources :sprockets, :controller => "bugs", :type => "Sprocket"
# And I don't know if you need this
resources :bugs, :type => "Bug"
Controller
def index
#bugs = params[:type].constantize.all
end
UPD
namespace "widgets" do
resources :bugs, :type => "Widget"
end
namespace "sprockets" do
resources :bugs, :type => "Sprocket"
end
I wrote a blog post on STI in Rails 3 that discusses some common pitfalls and proper work arounds, including the problem you mention. http://www.christopherbloom.com/2012/02/01/notes-on-sti-in-rails-3-0/

Resources