Rails 3 - Custom validator - ruby-on-rails

I'm trying to make a custom validator work on my app.
I have already configured my config.autoload.paths and it is loading fine.
The problem is with the validator itself.
Result of binding pry
instance variables: #attributes #options #with
locals: _ _dir_ _ex_ _file_ _in_ _out_ _pry_ attribute record value
[12] pry(#<FileCountValidator>)> value
=> [#<Attachment id: 60, description: nil, file: "cache_600_1__img_948867_5770137d84a6c79ac825886938e...", attachable_type: "Post", attachable_id: 15, created_at: "2012-03-10 14:50:54", updated_at: "2012-03-10 14:50:54">,
#<Attachment id: 61, description: nil, file: "cache_600_1__img_948867_90f64e01b9c871ec656a884e015...", attachable_type: "Post", attachable_id: 15, created_at: "2012-03-10 14:50:54", updated_at: "2012-03-10 14:50:54">,
#<Attachment id: 62, description: nil, file: "cache_600_1__img_948867_85eda3946c27fa90566403ac941...", attachable_type: "Post", attachable_id: 15, created_at: "2012-03-10 14:50:54", updated_at: "2012-03-10 14:50:54">,
#<Attachment id: nil, description: nil, file: nil, attachable_type: "Post", attachable_id: 15, created_at: nil, updated_at: nil>]
[13] pry(#<FileCountValidator>)> value > #with
TypeError: compared with non class/module
from /home/kleber/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.1/lib/active_record/relation/delegation.rb:20:in `>'
[14] pry(#<FileCountValidator>)> value.size > #with
=> true
[15] pry(#<FileCountValidator>)> value.size
=> 4
[16] pry(#<FileCountValidator>)> #with
=> 3
[17] pry(#<FileCountValidator>)>
So, I trying to make this comparisson exactly like I did on the pry debug console.
def validate_each(record, attribute, value)
#binding.pry
record.errors.add(attribute,"#{#with} attachments per post only. #{attribute['file'].size} detected.") if value.size > #with
end
But doing this, return me the error:
NoMethodError (undefined method `size' for nil:NilClass):
lib/validators/file_count_validator.rb:11:in `validate_each'
app/controllers/posts_controller.rb:61:in `block in update'
app/controllers/posts_controller.rb:60:in `update'
Are there any way to catch the value before it get enters the validate_each method?

Sorry, but the value being passed seems to be correct. value is meant to be the value of that attribute for that record, i.e. record.send(attribute) should be equal to the value.
Calling validates :attachments, :photo_count => 2 does not send 2 to the validate_each method as argument value. You could do :photo_count => true, which is what I generally do, or even :photo_count => "foo". The photo_count in your validates statement serves to provide that the validator is to be called by passing the value(i.e. 2 or true or "foo" for stated examples) embedded in the options hash.
Here's a way without passing a limit to the validator.
Create a Constants class and define MAX_ATTACHMENTS constant. I usually have it at models/constants.rb.
class Constants
MAX_ATTACHMENTS = 1
end
Then, in the validator, you could do
class PhotoCountValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute,"#{Constants::MAX_ATTACHMENTS} attachments per post only.") if record.send(attribute).size > Constants::MAX_ATTACHMENTS
end
end
Another way that passes a parameter to a validator:
validates :attachments, :photo_count => 3 #desired_limit
Override the initialize method for PhotoCountValidator class, and initialize a class variable :
def initialize(options)
##max_objects = options[:with] # the options hash will be as {:attributes=>[:attachments], :with=>3}, where :with contains the desired limit passed
super
end
Then within the validate_each method :
record.errors.add(attribute,"#{##max_objects} attachments per post only.") if record.send(attribute).size > ##max_objects

Related

Rails new objects are nil

I'm playing with rails again and found this behavior, when i create a new instance of a Post model with some attributes it tells me that all attributes are nil, why it is happening?
Loading development environment (Rails 4.0.0)
2.0.0-p451 :001 > a = Post.new(title: "Rails", content: "Rails Post")
=> #<Post id: nil, title: nil, content: nil, author: nil, rating: nil, created_at: nil, updated_at: nil>
2.0.0-p451 :002 > a.title
=> "Rails"
2.0.0-p451 :004 > a.content
=> "Rails Post"
2.0.0-p451 :005 > a.inspect
=> "#<Post id: nil, title: nil, content: nil, author: nil, rating: nil, created_at: nil, updated_at: nil>"
2.0.0-p451 :006 > a.errors.messages
=> {}
2.0.0-p451 :007 > a.valid?
=> true
class Post < ActiveRecord::Base
attr_accessor :title, :content, :author, :rating
end
You are defining attr_accessor for all your properties, which is a shortcut for defining getters and setters for an instance variable of the same name like so:
def content
#content
end
def content=(new_content)
#content = new_content
end
Rails will also auto-generate you methods with these names, for every database field that your model has. These methods will conflict with each other.
When you call post.content = 'foo', instead of calling the Rails-generated method that will internally set your model's content attribute to 'foo', you're calling the attr_accessor-defined method which will set the instance variable #content to 'foo'.
The output of inspect is iterating over the Rails-defined model attributes, not the instance variables.
Did you actually mean to declare these attributes as attr_accessible instead of attr_accessor?

Why can't I destroy this variable in Ruby?

In the rails console, I do this:
input = Input.create :name => "foo"
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
Input.all
=> [#<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">]
input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
input.destroy
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
> Input.all
=> []
> input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
> input.reload
ActiveRecord::RecordNotFound: Couldn't find Input with id=8
> input
=> #<Input id: 8, name: "foo", created_at: "2013-05-07 11:45:17", updated_at: "2013-05-07 11:45:17">
What I'd really expect to see is something like:
> input
=> nil
The object is deleted from the database but the variable still exists and is still trying to point to it. What's going on?
The input variable stores a reference to the instance in memory. Destroying the record will remove the row from the database. Calling input.reload (docs) raises an exception when attempting to find the record but doesn't set the value of your variable to nil on your behalf.
This behavior can be useful in the span of a DELETE request in which you want to display information about the object you removed. For example:
class WidgetsController < ApplicationController
def destroy
#widget = Widget.find(params[:id])
#widget.destroy
respond_with #widget, notice: "You successfully removed #{#widget.name}"
end
end
The destroy method makes the SQL call to the database and destroys the row in the table that contains it. It does still allow you to manipulate the object in the application as long as it’s still in scope (i.e) the callbacks and
filters are allowed even after destroying the object.
It is better to use "delete" if we don't want the callbacks to be triggered or if we want better performance
you can use input.delete

Why am I getting a "SystemStackError: stack level too deep" in my rails3 beta4 model

I'm getting the following error: SystemStackError: stack level too deep when executing the following code in rails3 beta4 under ruby 1.9.2-rc1:
ruby-1.9.2-rc1 > f = Forum.all.first
=> #<Forum id: 1, title: "Forum 1", description: "Description 1", content: "Content 1", parent_id: nil, user_id: 1, forum_type: "forum", created_at: "2010-07-17 04:39:41", updated_at: "2010-07-17 04:39:41", icon_file_name: nil, icon_content_type: nil, icon_file_size: nil, icon_updated_at: nil>
ruby-1.9.2-rc1 > f.children
=> [#<Forum id: 2, title: "Thread 2", description: "Description 2", content: "Content 2", parent_id: 1, user_id: 1, forum_type: "thread", created_at: "2010-07-17 04:40:17", updated_at: "2010-07-17 04:40:17", icon_file_name: nil, icon_content_type: nil, icon_file_size: nil, icon_updated_at: nil>]
ruby-1.9.2-rc1 > f.forum_type = "thread"
=> "thread"
ruby-1.9.2-rc1 > f.save
SystemStackError: stack level too deep
from /Users/emilkampp/.rvm/rubies/ruby-1.9.2-rc1/lib/ruby/1.9.1/irb/workspace.rb:80
Maybe IRB bug!!
ruby-1.9.2-rc1 >
And that is caused by the following code:
# Before and after filters
#
before_update :update_all_nested_objects, :if => :forum_type_changed?
protected
# Checks if the +forum_type+ has been changed
#
def forum_type_changed?
self.forum_type_changed?
end
# Updates all nested upjects if the +forum_type+ has been changed
#
# This will trigger the +update_all_nested_objects+ method on all touched children, thus
# starting a chain-reaction all the way through the forum-tree.
#
# NOTE: This is where the error is triggered, since this is the only non-tested looping code, and my test-
# cases hasn't changed since this was added.
#
def update_all_nested_objects
children.each do |child|
child.forum_type = child_type
child.save
end
end
So, what's going on. I have been checking around a little, but nobody seems to have the same problem. Either it's not the same ruby version, thus the workflow.rb file is different, or the stack level to deep is caused by something else.
Any help will be greatly apreciated!
Best regards
// Emil
You are calling same method in method itself
def forum_type_changed?
self.forum_type_changed? #This will never ending process hence it gives error
end
I think you have same method name and column name that causing problem change your method name then
def check_forum_type_changed?
self.forum_type_changed? #method name and column name are different
end

Error with "to_sql" on Rails 3 Beta 4

I'm testing Rails 3 beta 4 on Ruby 1.9.2-head, and when I start a
console and do:
Game.first.to_sql
I get this error:
ArgumentError: wrong number of arguments (0 for 1)
I know it can find the Game record, because when I type:
Game.first
it returns:
=> #<Game id: 1, name: "Galaga", created_at: "2010-06-19 11:02:37",
updated_at: "2010-06-19 11:02:37">
What am I missing? I just want to make the to_sql work in a very simple
case.
.
When you run Game.first you are returning a Game object, not a ActiveRecord::Relation object as you are expecting.
To do what you're trying to do, you'll need to do:
Game.limit(1).to_sql
This lets you run it without to_sql and return the object as you expected it, although it will be in an array, which then you can run .first on it like you wanted anyways.
irb(main):004:0> Game.limit(1).to_sql
=> "SELECT `games`.* FROM `games` LIMIT 1"
irb(main):005:0> Game.limit(1).class
=> ActiveRecord::Relation
irb(main):006:0> Game.limit(1)
=> [#<Game id: 1, name: "Galaga", created_at: "2010-06-19 11:02:37", updated_at: "2010-06-19 11:02:37">]
irb(main):007:0> Game.limit(1).first
=> #<Game id: 1, name: "Galaga", created_at: "2010-06-19 11:02:37", updated_at: "2010-06-19 11:02:37">
When you dig into the source, when you run .first on an ActiveRecord::Relation it runs the following (which is the same as I showed you):
def find_first
if loaded?
#records.first
else
#first ||= limit(1).to_a[0]
end
end

Rails: class' object_id changes after I make a request

I really can't explain this behavior, notice how after I make a request the class' object id has changed, and therefore my is_a? evaluation returns false.
any ideas? I'm not even sure how to debug this. Also, this isn't related to making a request from the command line. The same behavior is exhibited on the web server as well, it's just easier to explain from the command line.
staging$ RAILS_ENV=staging script/console
Loading staging environment (Rails 2.3.2)
>> c = CartItem.new
=> #<CartItem id: nil, order_id: nil, order_source: nil, date: nil, user_id: nil, created_at: nil, updated_at: nil, paid: nil, payment_id: nil, values: nil, cart_description: nil, type: nil, price: nil, email: nil, error: nil>
>> c.class.object_id
=> 70151495336400
>> CartItem.object_id
=> 70151495336400
>> c.is_a? CartItem
=> true
>> app = ActionController::Integration::Session.new
=> #<ActionController::Integration::Session:0x7f9ad5c55db0 .... >
>> app.get("site/favorite")
=> 200
>> c.class.object_id
=> 70151495336400
>> CartItem.object_id
=> 70151496019760
>> c.is_a? CartItem
=> false
>> c.class
=> CartItem(id: integer, order_id: string, order_source: string, date: date, user_id: integer, created_at: datetime, updated_at: datetime, paid: boolean, payment_id: integer, values: text, cart_description: string, type: string, price: integer, email: string, error: string)
Every new context (request/response cycle) will regenerate the object IDs. You may want to use responds_to?, instead of is_a?.
Since the ActionController::Integration module is used for integration testing, getting a url reloads your classes, therefore redefining the CartItem identifier. You now basically have two CartItem classes, one hanging around on your stack without an identifier pointing to it anymore (the "old one") and one referenced by the CartItem identifier.

Resources