Why do I get "uninitialized constant" when creating custom hash class? - ruby-on-rails

In Rails, I'm trying to create a custom hash class, So I created a file in lib/core_ext/hash_with_variants.rb with this content
class VariantsHash < Hash
def []=(string, value)
all_variants(string).each do |variant|
super(variant, value)
end
end
def [](string)
all_variants(string).detect do |variant|
super(variant)
end
end
private
def all_variants(string)
downcase_string = string.downcase
string_length = string.length
variants = [downcase_string]
string_length.times do |i|
variants << downcase_string[0, i] + downcase_string[i + 1 , string_length]
end
variants
end
end
but when I try and initialize a new object using
VariantsHash.new
I get the error
NameError: uninitialized constant VariantsHash
What else do I need to be doing to get this right?

You need to require the file in your /config/application.rb.

Related

Calling model methods using a variable Rails

Rails 5.2
I have a Catalog model, with the following methods:
def current_items
....
end
def sales_items
....
end
There are more than a dozen methods, and I would like to do, in my helper, is to define an array
categories = ['current', 'sales', ....]
categories.each do |category|
m = "{category}_items"
#items = Catalog.m
...
end
But, I am getting an error:
undefined method 'm' for Catalog:Class
Any idea if this can be done?
Please try below code :-
class Catalog < ActiveRecord::Base
def self.current_items
puts "from current_items"
end
def self.sales_items
puts "from sales_items"
end
end
Then call using :-
if public
m = "#{category}_items"
#items = Catalog.public_send(m)
if private
m = "#{category}_items"
#items = Catalog.send(m)
thanks :)
Try public_send to call the public methods in class, use send if you want to call private methods as well but avoid using send if not needed
m = "#{category}_items"
Catalog.public_send(m)
Give it a try!

Neat way to get and set keys of json column in Rails

I have a model/table with a json column in it as follows
t.json :options, default: {}
The column can contain many keys within it, something like this
options = {"details" : {key1: "Value1", key2: "Value2"}}
I want to set and get these values easily. So i have made getters and setters for the same.
def key1
options['details']&.[]('key1')
end
def key1=(value)
options['details'] ||= {}
options['details']['key1'] ||=0
options['details']['key1'] += value
end
But this just adds lines to my code, and it does not scale when more details are added. Can you please suggest a clean and neat way of doing this?
Use dynamic method creation:
options['details'].default_proc = ->(_,_) {{}}
ALLOWED_KEYS = %i[key1 key2 key3]
ALLOWED_KEYS.each do |key|
define_method key do
options['details'][key] if options['details'].key?(key)
end
define_method "#{key}=" do |value|
(options['details'][key] ||= 0) += value
end
end
You can just pass the key as a parameter as well right?
def get_key key=:key1
options['details']&.[](key)
end
def set_key= value, key=:key1
options['details'] ||= {}
options['details'][key] ||=0
options['details'][key] += value
end
Simple & Short
Depending on re-usability you can choose different options. The short option is to simply define the methods using a loop in combination with #define_method.
class SomeModel < ApplicationRecord
option_accessors = ['key1', 'key2']
option_accessors.map(&:to_s).each do |accessor_name|
# ^ in case you provide symbols in option_accessors
# this can be left out if know this is not the case
define_method accessor_name do
options.dig('details', accessor_name)
end
define_method "#{accessor_name}=" do |value|
details = options['details'] ||= {}
details[accessor_name] ||= 0
details[accessor_name] += value
end
end
end
Writing a Module
Alternatively you could write a module that provide the above as helpers. A simple module could look something like this:
# app/model_helpers/option_details_attribute_accessors.rb
module OptionDetailsAttributeAccessors
def option_details_attr_reader(*accessors)
accessors.map(&:to_s).each do |accessor|
define_method accessor do
options.dig('details', accessor)
end
end
end
def option_details_attr_writer(*accessors)
accessors.map(&:to_s).each do |accessor|
define_method "#{accessor}=" do |value|
details = options['details'] ||= {}
details[accessor] ||= 0
details[accessor] += value
end
end
end
def option_details_attr_accessor(*accessors)
option_details_attr_reader(*accessors)
option_details_attr_writer(*accessors)
end
end
Now you can simply extend your class with these helpers and easily add readers/writers.
class SomeModel < ApplicationRecord
extend OptionDetailsAttributeAccessors
option_details_attr_accessor :key1, :key2
end
If anything is unclear simply ask away in the comments.

How to stub a method that sets an object attribute in a Rails test?

I'm building a Rails spec test that has a Struct called temp_coverage, like this:
temp_coverage = Struct.new(:paydays) do
def calculate_costs
50
end
end
And in my spec, I call a method using the temp_coverage, but I'm getting an error since the code I am testing is doing the following:
temp_coverage.req_subscriber_election_amount = subscriber_election_amount
And I am getting an error:
NoMethodError: undefined method `req_subscriber_election_amount=' for < struct paydays=12 >
How can I stub out the setting of an attribute on a struct in my spec?
Is something like this you're looking for?
temp_coverage = double('temp_coverage', paydays: nil)
allow(temp_coverage).to receive(:calculate_costs).and_return(50)
allow(temp_coverage).to receive(:req_subscriber_election_amount=) do |argument|
temp_coverage.instance_variable_set(:#req_subscriber_election_amount, argument)
end
# Example:
temp_coverage.req_subscriber_election_amount = 123
puts temp_coverage.instance_variable_get(:#req_subscriber_election_amount)
# => 123
puts temp_coverage.paydays
# => nil
puts temp_coverage.calculate_costs
# => 50
I found a way to do this by using a named Struct. So once I named my Struct:
temp_coverage = Struct.new('CoverageClass', :paydays) do
def calculate_costs
50
end
end
I could then do the following:
Struct::CoverageClass.any_instance.stubs(:req_subscriber_election_amount).returns(25)

extract `.for()` into a method

I have a class like this one:
class House
def bricks
Brick.for(#house_plan).where(size: 5)
end
def wood
Wood.for(#house_plan).where(size: 5)
end
end
My goal is to extract the call for(self).where(size: 5):
What I tried first was:
class House
def bricks
Brick.match_material
end
def wood
Wood.match_material
end
def match_material
for(#house_plan).where(size: 5)
end
end
But then I got this error:
syntax error, unexpected '\n', expecting :: or '[' or '.'
Then I changed my code to:
def match_material
.for(#house_plan).where(size: 5)
end
And now when I do:
house = House.new(HousePlan.new)
house.bricks
I get this error:
formal argument cannot be an instance variable
In this line: for(#house_plan).where(size: 5)
What do I wrong?
Your approach isn't right, remember match_material method will always be called in the context of your self. I would do it this way:
def bricks
match_material(Brick)
end
def wood
match_material(Wood)
end
def match_material(klass)
klass.for(#house_plan).where(size: 5)
end
Just out of curiosity:
def bricks
klazz = Kernel.const_get(__callee__[0...-1].capitalize)
klazz.for(#house_plan).where(size: 5)
end
alias :woods :bricks
NB: In this approach aliased methods are to be named consistently(bricks, woods.) Please don’t use it in production unless you understand what you are doing.

Hypertable migration causing problem with rails 2.3.8

I am using Hypertable db with the front end of HyperRecord. There were some bugs that i fixed in it. But now migrations get stuck me. When ever i do migrate it shows the error:
rake aborted!
undefined method `select_rows' for #<ActiveRecord::ConnectionAdapters::HypertableAdapter:0xb6f791c4>
.rvm/gems/ruby-1.8.7-p352#r2.3.8/gems/activerecord-2.3.8/lib/active_record/connection_adapters/abstract/database_statements.rb:27:in `select_values'
When i look into the code or ruby on rails actice_record. It shows.
# Returns an array of arrays containing the field values.
# Order is the same as that returned by +columns+.
def select_rows(sql, name = nil)
end
undef_method :select_rows
I tried to remove these functions by adding a fix in initializes.
module ActiveRecord
module ConnectionAdapters
class HypertableAdapter
def select_rows(sql, name = nil)
end
end
end
end
Then it came with the error Nil value occurred while accepting array or hash. To fix it i added new method to the fix code.
module ActiveRecord
module ConnectionAdapters
class HypertableAdapter
def select_rows(sql, name = nil)
end
def select_values(sql, name = nil)
result = select_rows(sql, name)
result.map { |v| v[0] } unless result.nil?
end
end
end
end
then it came with the error:
rake aborted!
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map
/.rvm/gems/ruby-1.8.7-p352#r2.3.8/gems/activerecord-2.3.8/lib/active_record/migration.rb:421:in `get_all_versions'
Does any one have an idea, what is going on with it?
This code removes all the errors. But now migrations runs well, but do not roll back.
module ActiveRecord
module ConnectionAdapters
class HypertableAdapter
def select_rows(sql, name = nil)
result = execute(sql)
rows = []
result.cells.each { |row| rows << row }
rows
end
def select_values(sql, name = nil)
result = select_rows(sql, name)
result.map { |v| v[0] } unless result.nil?
end
end
end
end
When i checked into schema file it shows the following error:
# Could not dump table "teams" because of following StandardError
# Unknown type '' for column 'ROW'

Resources