Rails 3 Custom Class Question - ruby-on-rails-3

I am trying to create a separate model class for image uploading, which was previously in the object's controller. I also want to make it agnostic, so I can upload images using one class from multiple objects.
In my original object's model class, I have the following now:
class Object < ActiveRecord::Base
after_save :photo_store
delegate :has_photo?, :photo, :photo_path, :store_photo, :photo_filename, :to => :photo_store
def photo_store
PhotoStore.new(self)
end
end
Then, the PhotoStore class looks like this:
class PhotoStore
attr_reader :object
def initialize(object)
#object = object
end
def photo=(file_data)
unless file_data.blank?
#file_data = file_data
self.extension = file_data.original_filename.split('.').last.downcase
end
end
PHOTO_STORE = File.join RAILS_ROOT, 'public', 'photo_store'
def photo_filename
File.join PHOTO_STORE, "#{id}.#{extension}"
end
def photo_path
"/photo_store/#{id}.#{extension}"
end
def has_photo?
File.exists? photo_filename
end
private
def store_photo
if #file_data
FileUtils.mkdir_p PHOTO_STORE
File.open(photo_filename, 'wb') do |f|
f.write(#file_data.read)
end
end
end
end
However, this throws the error below when I try and use the has_photo? method in the object's view.
undefined local variable or method `id' for #
Do I need to put some other type of relationship in place between the Object and PhotoStore?
And a separate question: What's the best way to make this agnostic? Since it uses just the ID of the object, I could just include the Object's name in the filename, but is that the best way to do it?
Thanks!

Because at File.join PHOTO_STORE, "#{id}.#{extension}" you call method PhotoStore#id, but it does not exists.
You should do that
File.join PHOTO_STORE, "#{#object.id}.#{#object.extension}"

Related

How to get Child class in inherited parent method in Ruby?

I am writing classes for a SQL Database in Ruby, a few of the query methods are common throughout all my classes so I want to move these methods into a parent class. I can't figure out how to get the original class call to show up in the parent class. Since my classes are the names of my tables, I need them to have access to them in my parent method. I've tried using self.class but that just returns Class, not the actual class I'm using the method on.
Here is the code I want to move into the parent class:
require_relative 'questions_databse.rb'
class User
attr_accessor :id, :fname, :lname
def self.find_by_id(id)
user = QuestionDatabase.instance.execute(<<-SQL, id)
SELECT
*
FROM
users
WHERE
users.id = ?
SQL
user.map { |user| User.new(user) }.first
end
Now what I need to do is something like this:
require_relative 'questions_databse.rb'
require_relative 'modelbase.rb'
class User < ModelBase
attr_accessor :id, :fname, :lname
# no self.find_by_id(id)
Then I want the parent class to do something like this:
require 'active_support/inflector'
class ModelBase
def self.find_by_id(id)
object = QuestionDatabase.instance.execute(<<-SQL, id)
SELECT
*
FROM
#{self.chid_class.name.tableize}
WHERE
#{self.chid_class.name.tableize}.id = ?
SQL
object.map { |object| self.child_class.new(object) }.first
end
end
I have 4 other table classes that I use this method on, so I need to be able to have the code tell exactly what class called it so that the SQL query will run properly.
require 'active_support/inflector'
class ModelBase
def self.find_by_id(id)
p self.class.name
end
end
User.find_by_id(1) #=> "Class"
That is result when I use the self.class.name
I am learning how to code right now and this is the problem that the lesson is giving me. I know there may be easier ways to do this, but I probably haven't learned those yet.
In a class method like your find_by_id:
class ModelBase
def self.find_by_id(id)
p self.class.name
end
end
self is the class itself (so self.class is Class) so you want to look at self.name:
class ModelBase
def self.find_by_id(id)
p name
end
end

Validating Child Object with ActiveModel Validations

I have two plain Ruby classes, Account and Contact. I am using Simple Form's simple_form_for and simple_fields_for to create nested attributes. I am looking to fulfill the following validation requirements:
An associated Contact must exist for the new Account
The associated Contact must be valid (i.e., account.contact.valid?)
It looks like ActiveModel no longer includes the validates_associated method, as using that method results in an undefined method error. I considered requiring ActiveRecord::Validations, but this led down a stretch of various errors (e.g., undefined method `marked_for_destruction?')
I also considered defining validate on the Account class and calling valid? on the associated object, but that only prevented the form from submitting if there was also an error on the parent object.
validate do |account|
account.contact.valid?
# required for form to fail
errors.add(:base, "some error")
end
Is there something I'm not aware of to solve this? Thanks.
I recently (7 years after this question has been asked!) faced the same issue and solved it by implementing the AssociatedValidator based on the ActiveRecord one.
I simply included it in config/initializers folder:
module ActiveModel
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Array(value).reject { |r| valid_object?(r) }.any?
record.errors.add(attribute, :invalid, **options.merge(value: value))
end
end
private
def valid_object?(record)
record.valid?
end
end
module ClassMethods
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
now you can use validates_associated in ActiveModel too.
class Person
include Virtus
include ActiveModel::Model
attribute :address, Address, :default => Address.new
validate :address_valid
private
def address_valid
errors.add(:base, 'address is not valid') unless address.valid?
end
end
class Address
include Virtus::ValueObject
include ActiveModel::Validations
attribute :line_1, String
attribute :line_2, String
validates :line_1, :presence => true
validates :line_2, :presence => true
end
The errors show up in the form if you pass an object to simple_fields_for:
= form.simple_fields_for person.address do |af|
= af.input :line_1
Another option is overriding valid?:
def valid?
super & address.valid?
end
Note its & not && so the conditions are not short circuited if the first returns false.

Helpers in controller - Rails 3

I migrated from rails 2.x to 3.x. Now when calling a controller method throws
undefined method `my_helper_method' for nil:NilClass
MyController.rb
class MyController < ApplicationController
def foo
#template.my_helper_method
end
end
MyControllerHelper.rb
class MyControllerHelper
def my_helper_method
puts "Hello"
end
end
ApplicationController
class ApplicationController < ActionController::Base
helper :all
end
How to get this working?
This is actually answered in another SO post: Rails 3: #template variable inside controllers is nil
Essentially, you can replace #template with view_context
#template is an object, in your case nil. If this object doesn't has the method (my_helper_method) in it, you cannot call it (especially not if it is nil).
Methods defined in helpers are called like regular methods. But not in controllers, they are called in views. Your helper :all just makes all helpers available to the views.
So, in your view: my_helper_method :arg1, :arg2
IF you need a method for your object (#template), you need to give your object this method.
Example:
class Template < ActiveRecord::Base
def my_helper_method
# do something on a template instance
end
end
class MyController < ApplicationController
def foo
#template = Template.first
#template.my_helper_method # which actually isn't a helper
end
end
What helpers do:
module MyHelper
def helper_method_for_template(what)
end
end
# in your view
helper_method_for_template(#template)
Mixing in a helper (be aware of having a mess in your code when mixing view helpers with views and models)
class Template < ActiveRecord::Base
include MyHelper
# Now, there is #template.helper_method_for_template(what) in here.
# This can get messy when you are making your helpers available to your
# views AND use them here. So why not just write the code in here where it belongs
# and leave helpers to the views?
end

how to access a method in a model from a controller in rails

I want to put logic in the model rather than controller.
class UsersController < ApplicationController
def somemethod
d = User.methodinmodel
end
end
class User < ActiveRecord::Base
def methodinmodel
"retuns foo"
end
end
I get an error that there is no methodinmodel for the User model.
Why?
If you want to be able to call methodinmodel on the User class in general rather than a specific user, you need to make it a class method using self:
class User < ActiveRecord::Base
def self.methodinmodel
"returns foo"
end
end
Your current method definition would only work if you called it on a user:
#user = User.create!
#user.methodinmodel # Works.
User.methodinmodel # Doesn't work.
Using the new implementation using self would allow you to call it like:
User.methodinmodel # Works.

Ruby kind_of? and is_a? returning false for a subclass

I'm trying to take advantage of the ruby methods kind_of? and is_a?. As I understand it they are synonyms of one another.
I have an object of class Child. A call to Child.ancestors gives back an array list this [Child, #<Module>, Parent, ...]. However a call to Child.new.is_a?(Parent) or Child.new.kind_of?(Parent) returns false.
Calling Child.ancestors[2].new.is_a?(Parent) also returns false. I can't seem to figure out why this would be considering calling Parent.new.is_a?(Parent) returns true as it clearly should.
These classes all eventually descend from ActiveResource::Base if that has anything to do with it.
class Parent < ActiveRecord::Base
include MyModule
def self.my_method(obj)
if obj.is_a?(Parent)
puts 'Hello'
end
end
end
class Child < Parent
def my_method
self.class.my_method(self)
end
end
a = Child.new
a.my_method
class Parent
def self.my_method(obj)
if obj.is_a?(Parent)
puts 'IS A PARENT'
else
puts 'IS NOT A PARENT'
end
end
end
class Child < Parent
def my_method
self.class.my_method(self)
end
end
a = Child.new
a.my_method
Note I dropped the AR and included module, but the above prints out "IS A PARENT" for me. Ruby 1.8.7 on osx.