undefined method `distance_of_time_in_words' for Object - ruby-on-rails

I am using Rails 5.2.2 There are many empty(nil) fields in my DB and created a custom method to use distance_of_time_in_words in my model without errors.
def my_distance_of_time_in_words
if self.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(self.accounts.first.updated_at,Time.now).titleize
end
end
And i am passing my object from view using :
<%= #customer.my_distance_of_time_in_words %>
It was working well and i restarted my PC and it says :
undefined method `distance_of_time_in_words' for #<Customer:0x00007f43b98601d8>
This is odd because as i said it was working as i expected.But it's not working now.

The date helpers are not available in your model by default, so you need to explicitly include them.
class Customer < ApplicationRecord
include ActionView::Helpers::DateHelper
def my_distance_of_time_in_words
if self.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(self.accounts.first.updated_at,Time.now).titleize
end
end
end
However a better approach would be to use a helper method to accomplish what you need, this way you won't need to explicitly include ActionView::Helpers::DateHelper since it is already available to you there:
module CustomersHelper
def my_distance_of_time_in_words(customer)
if customer.accounts.blank?
"No Record Avaliable"
else
distance_of_time_in_words(customer.accounts.first.updated_at,Time.now).titleize
end
end
end

Related

Ruby - Check if controller defined

I am using Solidus with Ruby on Rails to create a webshop and I have multiple modules for that webshop.
So, I defined a me controller into an module called 'solidus_jwt_auth' with the followin code:
module Spree
module Api
class MeController < Spree::Api::BaseController
def index
...
end
def orders
...
end
def addresses
...
end
end
end
end
I want to extend this in another module called 'solidus_prescriptions' so I created a decorator for this with the following code me_decorator:
if defined? Spree::Api::MeController.class
Spree::Api::MeController.class_eval do
def prescriptions
...
end
def create_prescription
...
end
private
def prescription_params
params.require(:prescription).permit(
*Spree::CustomerPrescription.permitted_attributes
)
end
end
end
And for this I wrote unit tests in solidus_prescription module and integration tests in webshop. The unit tests are working fine, but the integration tests are giving the following error:
Error:
MeEndpointsTest#test_me/prescriptions_post_endpoint_throws_an_error_when_wrong_params:
AbstractController::ActionNotFound: The action 'create_prescription' could not be found for Spree::Api::MeController
test/integration/me_endpoints_test.rb:68:in `block in '
Which means that he can not find the MeController defined in another module. How can I make the check if the MeController is defined since the code bellow does not help me with anything:
if defined? Spree::Api::MeController.class
end
This worked in the end:
def class_defined?(klass)
Object.const_get(klass)
rescue
false
end
if class_defined? 'Spree::Api::MeController'
....
end
if defined? should do exactly what you want it to do in theory. The problem is you're checking if defined? Spree::Api::MeController.class. The #class of your class is Class. So what you're really getting is if defined? Class which will always be true!
This issue is most likely not that the conditional is failing but that it's never getting read. Rails lazy loads most of the code you write, meaning the file is not read until it's called somewhere in execution.
The decorator module should just contain the methods you want to add, without the conditionals or the use of class_eval. Then in the original class you can include it.
module Spree
module Api
class MeController < Spree::Api::BaseController
include MeDecorator
end
end
end
If for any reason you're not certain MeDecorator will be defined, don't use defined?, because defined? MeDecorator will not actually go looking for it if it's not defined and load the necessary file. It will return nil if the constant has no value. Just rescue a NameError
module Spree
module Api
class MeController < Spree::Api::BaseController
begin
include MeDecorator
rescue NameError => e
logger.error e
end
end
end
end

Undefined local variable or method when accessing from controller to helper methods

I created a controller which called 'internal releases'.
I want to check that the multi-select objects contains at least one selection each.
In my controller I have:
class InternalReleasesController < ApplicationController
def show
if params[:run].nil?
logger.error "Attempt to get trend result without going through the internal_releases_trend_selection_url"
flash[:no_arguments] = 'You have tried accessing trend results without selecting parameters.'
redirect_to internal_releases_trend_selection_url
else
all_options = Array.new(params[:run][:category_id])
missing_selections = validate_arguments params[:run]
all_options = Array.[]params[:run][:category_id]
logger.debug "all_options is: #{all_options.class}"
end
end
end
I created a simple helper method:
module InternalReleasesHelper
def validate_arguments multiselect_hash
answer = Array.new
multiselect_arr.each do |key, val_arr|
if val_arr.length==1 # therefore, no selection made in this multiselect- the first arg will always be ""
answer << key
end
end
answer
end
end
For some reason I get:
undefined method `validate_arguments' for #<InternalReleasesController:0x007faf08bf9f78>
What might cause this?
Include helper module InternalReleasesHelper into InternalReleasesController class
class InternalReleasesController
include InternalReleasesHelper
end
Helper's method are just available into Views by default, so you should include your helper into controller:
Navigate on internal_releases_controller.rb file and insert following:
include InternalReleasesHelper

NameError: undefined local variable or method `desired_preferences'

I have created a module with a method
module Adding_preferences
def desired_preferences
#preference = %w(motabilitySpecialist newCars bodyshop filter8 filter7).each do |selection|
#browser.label(:for, selection ).click
end
end
end
I have included this module into a class:
class Pages
include Adding_preferences
attr_accessor :browser, :preference
def initialize
#browser = Watir::Browser.new :ff
end
end
World do
Pages.new
end
I am calling this method in a Cucumber scenario
When /^I select a desired preference$/ do
desired_preferences
end
But at runtime I receive an error, "NameError: undefined local variable or method `desired_preferences'". Where am i going wrong?
When you include a module to a class you can use this method in the instance methods of this class. You cant call the included method in a View that displays the data from the model that includes the module. For me it looks like you just dont use the desired_preferences method in an instance method.
Please show us the peace of code you try to call the method if this still doesnt help you out.
// The naming of the Module is not conventional. You should call it module AddingPreferences isntead ofmodule Adding_preferences and the file should be named adding_preferences.rb then try to include AddingPreferences
It's a good idea for you to spend some time getting more familiar with Ruby's Class/Module/Object/Method inheritance model, because the way you're structuring your code there is a little bit messy.
However, a simple thing to try (and I'm not going to guarantee that it will work flawlessly) is the following modifications:
Assign your instantiated Pages class to a class instance variable:
World do
#page = Pages.new
end
...and then use that instance variable in your step definition...
When /^I select a desired preference$/ do
#page.desired_preferences
end
I hope that helps!

Ruby - How to access module's methods?

I'm installing a forum using the Forem gem. There's an option that allows avatar personalization, since it's possible to login with Facebook. You just specify your method in the User model and that's it.
# Forem initializer
Forem.avatar_user_method = 'forem_avatar'
# User model
def forem_avatar
unless self.user_pic.empty?
self.user_pic
end
end
But I want a fallback on Gravatar for normal, non-facebook accounts. I've found the method on Forem and in theory, I need to call the avatar_url method:
# User model
def forem_avatar
unless self.user_pic.empty?
self.user_pic
else
Forem::PostsHelper.avatar_url self.email
end
end
However, Forem isn't an instance, but a module and I can't call it nor create a new instance. The easy way is to copy the lines of that method, but that's not the point. Is there a way to do it?
Thanks
Update
Both answers are correct, but when I call the method either way, there's this undefined local variable or method 'request' error, which is the last line of the original avatar_url.
Is there a way to globalize that object like in PHP? Do I have to manually pass it that argument?
perhaps reopen the module like this:
module Forem
module PostsHelper
module_function :avatar_url
end
end
then call Forem::PostsHelper.avatar_url
if avatar_url call other module methods, you'll have to "open" them too via module_function
or just include Forem::PostsHelper in your class and use avatar_url directly, without Forem::PostsHelper namespace
If you want to be able to use those methods in the user class, include them and use
class User < ActiveRecord::Base
include Forem::PostsHelper
def forem_avatar
return user_pic if user_pic.present?
avatar_url email
end
end
Another way would be to set the Forem.avatar_user_method dynamically since the Forem code checks it it exists before using it and defaults to avatar_url if it does not.
class User < ActiveRecord::Base
# This is run after both User.find and User.new
after_initialize :set_avatar_user_method
# Only set avatar_user_method when pic is present
def set_avatar_user_method
unless self.user_pic.empty?
Forem.avatar_user_method = 'forem_avatar'
end
end
def forem_avatar
self.user_pic
end
end
This way you dont pollute your model with unnecessary methods from Forem and don't monkey patch Forem itself.

Legacy table with column named "class" in Rails

I've got a legacy table that my rails application shares with another application. It has a column called "class". The first time I reference any attribute in that model, I get an error. Subsequent references to attributes work. Is there a good workaround for this, or should I just go modify the other application that uses this table (ugh)?
>> Member::Ssg.find(:first)
=> #<Member::Ssg ssg_key: #<BigDecimal:10b169688,'0.253E3',4(8)>, org_id: 2, academic_year: 2006, class: true, next_due_date: "2011-06-01", submitted_date: "2006-02-13", notes: nil, owner_id: "1">
>> Member::Ssg.find(:first).notes
NoMethodError: undefined method `generated_methods' for true:TrueClass
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
from (irb):2
>> Member::Ssg.find(:first).notes
=> nil
SOLUTION:
I went with a combination of the Bellmyer solution and adding the code below to my model
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
NOTE: Please see the updated solution at the end of this answer. Leaving the original outdated solution for historic reasons.
This has come up often enough (legacy column names interfering with ruby/rails) that I might just make a plugin out of this. Here's how you can fix it right away, though. Create this file in your app:
# lib/bellmyer/create_alias.rb
module Bellmyer
module CreateAlias
def self.included(base)
base.extend CreateAliasMethods
end
module CreateAliasMethods
def create_alias old_name, new_name
define_method new_name.to_s do
self.read_attribute old_name.to_s
end
define_method new_name.to_s + "=" do |value|
self.write_attribute old_name.to_s, value
end
end
end
end
end
And now, in your model:
class Member < ActiveRecord::Base
include Bellmyer::CreateAlias
create_alias 'class', 'class_name'
end
The first parameter to create_alias is the old method name, and the second parameter is the new name you want to call it, that won't interfere with rails. It basically uses the read_attribute and write_attribute methods to interact with the column instead of the ruby methods that get defined by ActiveRecord. Just be sure to use the new name for the field everywhere, like so:
member.class_name = 'helper'
This works with ruby 1.8, but I haven't tested with ruby 1.9 yet. I hope this helps!
UPDATE: I've found a better solution that works in Rails 3, the safe_attributes gem. I've written a blog post explaining how to use it, with example code snippets, and a full sample app you can download from github and play around with. Here's the link:
Legacy Database Column Names in Rails 3
The following works in Rails 6.0.2.2
class ReasonCode < ApplicationRecord
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
def as_json(options={})
add_class = attributes.keys.include?('class')
if add_class
if options[:only]
add_class = Array(options[:only]).map(&:to_s).include?('class')
elsif Array(options[:except])
add_class = Array(options[:except]).map(&:to_s).exclude?('class')
end
end
options[:except] = Array(options[:except])
options[:except].push('class')
json = super(options)
json['class'] = attributes['class'] if add_class
json
end
end
Adapted from this answer https://www.ruby-forum.com/t/activerecord-column-with-reserved-name-class/125705/2. The as_json method was added because rendering the record as json gave a SystemStackError (stack level too deep). I followed the serialization code in the Rails repo to only render the class attribute if specified in the as_json options.

Resources