Rails validation of non-models - ruby-on-rails

I was just wondering what the recommended approach would be for validating a form that is not based on a model. I agree that generally all validation should be done in models but there are situations where a particular form might not have a corresponding model(s). For example, an arbitrary search form.
Based on my current research, there are two main ways of doing it as far as I can see,
Validate in the controller.
Is it possible to utilise the Rails validations in the controller? I really would prefer to use them over my own custom code.
Create an arbitrary class eg. called SearchForm and include the ActiveRecord::Validations
Should the class be stored with the models (even though it isn't really)?
The posts that recommended this approach indicated you have to stub out a bunch of methods for ActiveRecord such as save, save!, update_attribute, etc. This strikes me as not very Rubyesque at all!
Any other suggestions?

Absolutely #2. ActiveModel::Validations API is what you're looking for
class ArbitrarySearch
include ActiveModel::Validations
attr_accessor :query
validate :query, :presence
end
As for where this should go, yes, it should go in app/models. If you're like me and think the mix of models extending ActiveRecord::Base and those that don't coexisting within the same directory smells funny, consider adding the following to your config/application.rb file
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
Now, you can organize your model files in whatever way you like. For me I have
- app
|
|- models
|
|- table
|- tableless
|- observer
and I drop classes like your ArbitrarySearch class into app/models/tableless/arbitrary_search.rb.
There is a great gem to get this and other common functionality you use within ActiveRecord models into generic non-table-based model classes called active_attr.
ActiveAttr is a set of modules that makes it easy to create plain old ruby models with functionality found in ORMs, like ActiveRecord, without reinventing the wheel. Think of ActiveAttr as the stuff ActiveModel left out.

Related

How can I separate the validations from the model

I have some very big models that I must migrate to the latest version of Rails. These models have quite a bunch of validations(User has aprox. 50 validations).
Is it possible to move all these validations in another file? Say app/models/validations/user_validations.rb. And if it is can someone provide an example, please?
You can use concerns for this:
# app/models/validations/user_validations.rb
require 'active_support/concern'
module UserValidations
extend ActiveSupport::Concern
included do
validates :password, presence: true
end
end
# app/models/user.rb
class User
include UserValidations
end
You may need/want to namespace your concerns depending on your autoload path configuration:
# app/models/validations/user.rb
require 'active_support/concern'
module Validations
module User
...
# app/models/user.rb
class User
include Validations::User
From a style perspective you may want to think about why you have so many validations. Shunting them into a module will slim down the model file, but effectively the class still carries all that code around with it. you're effectively sweeping the problem under the carpet.
Are you using a lot of different forms with different validation requirements? If so, you can use form objects (which include ActiveModel functionality) to encapsulate the validations and processing needed for each form, taking the strain off the models.
Do your models have an insane number of fields? Maybe your user object needs to be composed from smaller objects like a profile, address, avatar, etc.
Of course, this is outside the scope of a version migration!
If you can't or don't want to use ActiveRecord concerns (which have some dependency management code that you may not want to carry around), you can use the excellent and tiny plug-in 'augmentations' or the derived gem:
https://github.com/chemica/augmentations-gem
This uses a very similar syntax and far less code. It also doesn't use the term 'concerns', which can mean something else in the OO terminology for different languages/frameworks.

Rails ActiveRecord override default find

I'm building an API wrapper that will query objects from a third-party API and build them into objects to be used in my Rails environment. To do that, I'm building a set of models that use ActiveRecord (for some of its functionality) but are not database backed. I would like to be able to make a call like this:
obj = MyModel.find(1)
And have the code be something like this:
def MyModel.find id
# check for object in cache
# check for object in db
# grab object from API
# return object
end
Am I going to do something horribly wrong if I override the default find method? Am I approaching this in totally the wrong way?
If you are not using a database, then you do not need ActiveRecord. The entire purpose of ActiveRecord is to give you a mapping to a relational database.
I think what you want is for a class to implement certain pieces of what ActiveRecord provides, and Rails 3 has made those pieces into classes that you can include into regular 'ol classes on an as-needed basis. Look at this article for more details: http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
For instance, if you only want validations on a class, you can use include ActiveModel::Validations and then you'll get all of the nice error handling and .valid? and validates presence: true kind of behavior you're used to.
I would also suggest the railscast by Ryan Bates: http://railscasts.com/episodes/219-active-model which goes into more detail.

How to create a model in rails

I am using the parseresource gem and it says I need to create a model.
Create a model:
class Post < ParseResource
fields :title, :author, :body
validates_presence_of :title
end
I only know how to generate a mode and it always inherits ActiveRecord::Base. What should I type into my command line to create this model?
I think what you are looking for is:
rails generate model Post title:string author:string blob:text
Then change the inherits from ActiveRecord to inherits from ParseResource in the post.rb file created.
class Post < ActiveRecord::Base
becomes
class Post < ParseResource
I don't really have enough details about the model or ParseResource for a better answer.
I do hope that helps though.
If you are new to Ruby and/or Rails I suggest running through an introduction to rails.
http://guides.rubyonrails.org/getting_started.html
or
http://ruby.railstutorial.org/ruby-on-rails-tutorial-book
Cheers!
In this case it looks like you only need to create a single file in your /app/models directory named after the model you're trying to create.
The normal model generation code that comes with Rails and it's generators don't apply here since this gem is specifically intended to only serve as a 'wrapper' to the Parse.com API.
From the project's home page:
ParseResource makes it easy to interact with Parse.com's REST API. It adheres to the ActiveRecord pattern. ParceResource is fully ActiveModel compliant, meaning you can use validations and Rails forms.
It looks like all you do is create a single file descending from ParseResource and then interact with it as if it were a normal ActiveRecord model.
Looks pretty straightforward, though I'd caution that the author says right in the docs:
ParseResource is brand new. Test coverage is decent.
This is my first gem. Be afraid.
Be careful and make sure you report any issues you find through the projects issues page in Github.
Unless the parseresource gem includes a custom generator, I think you will have to generate a standard ActiveRecord model and then edit it to inherit from ParseResource.
As another option you could just create the model from scratch by creating a post.rb file under app/models and putting your model code in there (any decent text editor will work just fine). Remember there is nothing forcing you to use a generator, they are just there to make your life easier.
rails generate model followed by your model name, then the fields with respective datatype and size.
Below is one exhaustive example.
rails generate model MyModel some_id:integer{20} some_name:string{255}
some_text:text some_int:integer{1} some_deci:decimal{10,2}
You can also have other data types such as boolean, date, time, datetime, float, binary.

Refactoring validation methods and callbacks

I am using Ruby on Rails 3.0.7 and I have tree classes what behavior is almost the same (and also the code in them model files). All those have a name and a description attribute, run same validation methods and for both there is a before_save callback that maintains data consistent providing the same functions.
I would like to refactor validation methods and callbacks in a separated class\model (I think I have to locate them related files in the \lib folder of my application).
What I have to do to make that? What code I have to add in my classes and what in the refactoring class\model?
Well, you could just make a super class from which your three models inherit. I tend to put the abstract base class in app/models alongside the models themselves.
# app/models/thing.rb
class Thing < ActiveRecord::Base
# common code goes here, such as
before_save ...
validates_length_of :foo
end
# app/models/red_thing.rb
class RedThing < Thing
# methods specific to RedThing go here
end
# app/models/blue_thing.rb
class BlueThing < Thing
# methods specific to BlueThing go here
end
If your Things have many differences such that it doesn't make sense to group them like this, you'd want to use a module instead, which is a only bit more complicated.
Rails guides has info here:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#creating-custom-validation-methods

pros/cons of a ::Base class (rather than acts_as_foo)

Rather than an acts_as_foo syntax**, I'm toying with the idea of extending ActiveSensor::Base < ActiveRecord::Base and then extending this base class with all the errors/validations/class methods/whizbangs.
Proposed: #active_sensor.rb gem
module ActiveSensor
include ActiveRecord
autoload :VERSION, 'active_sensor/version'
autoload :ActiveSensorError, 'active_sensor/base'
autoload :Base, 'active_sensor/base'
autoload :Validations, 'active_sensor/validations'
end
Pros:
I think it looks cleaner.
Architecture and class build-up emulates ActiveRecord
I have a lot of subclassing going on... want to check whether a component is hardware using :is_a? method.
Cons:
holding an extra inheritance class layer in memory... never used independently
not very conventional (only seen 1 other rails plugin do this)
Any advice? Is creating a new ::Base class just dumb? If so, why?
** The acts_as_foo pattern is common for Rails gems. This class method is added to every AR object, which loads and extends class and instance methods when loading the class.
I think you've already answered yourself. Quite simply, if you extend Base then you add the functionality to every Active Record class in your system, whereas if you use acts_as_foo - you can add it only to the models that require it.
Obviously form this, it's better to use acts_as_foo if you only want the functionality for some of your models... but if you want it in every model, then feel free to extend Base.

Resources