I am new to Rails and Ruby development but I am trying to create an object called Currency which takes in two params and does some calculations on them. I am using attr_accessor to set up the params and I put the file inside the lib directory.
Whenever I run rails console and try to do c = Currency.new(100, "CAD") I get the following error:
ArgumentError: wrong number of arguments (given 2, expected 0)
from (irb):5:in `initialize'
from (irb):5:in `new'
from (irb):5
I did make sure to include the file in application.rb. Here is a skeleton of my class:
class Currency
class << self
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def converted_value
convert_to_dm
end
private
def convert_to_dm
#input_value / USD_TO_DM
end
end
end
I have looked all over and I am stumped on what this issue may be. I have tried with and without an initialize method and I have tried creating a more basic version.
The problem here is that you are defining the method as a class method. And you are not defining the initialize method with those two params. Let's check the code below:
class Currency
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def initialize(input_value, currency_iso)
#input_value = input_value
#currency_iso = currency_iso
end
def converted_value
convert_to_dm
end
private
def convert_to_dm
input_value / USD_TO_DM
end
end
Also, due to you have already defined the attr_accessor you don't need to use the # when calling those attributes.
I found this post. It can help you to understand better the difference between class method and instance method.
Related
I tried to do some code refactor inside of my webhook manager class. The idea was to use Builder pattern (hope I didn't get the names wrong) in which one class would distribute assignments to the others. That's why I build a class with constant variable WEBHOOK_DEFINITIONS and, depending on the arguments provided, would trigger the appropriate ActiveJob's. Like below:
# frozen_string_literal: true
class ManageWebhookData
WEBHOOK_DEFINITIONS = {
default: IdentityChecks::IdentityCheckUpdaterJob,
#there will be much more
(...)
}.freeze.with_indifferent_access
def initialize(webhook, name)
#webhook = webhook
#name = name
end
def call
WEBHOOK_DEFINITIONS[type].perform_later(webhook)
end
attr_reader :webhook, :name
private
def type
WEBHOOK_DEFINITIONS.key(name.downcase) ? name.downcase : :default
end
end
I don't know why but Rails somehow call WEBHOOK_DEFINITIONS even when it's freeze because I'm getting an error:
NameError (uninitialized constant IdentityCheckUpdaterJob):
app/services/manage_webhook_data.rb:5:in `<class:ManageWebhookData>'
Why is this happening?
It's not rails, it's ruby.
Constants are resolving on class load, you could check it yourself in console
> class Foo
> BAR = puts("I'm constant")
> end
I'm constant
Besides that, I'm not sure when Indifferent hash is called on freezed hash, it will be freezed too. I'd write instead
WEBHOOK_DEFINITIONS = {
default: IdentityChecks::IdentityCheckUpdaterJob,
#there will be much more
(...)
}.with_indifferent_access.freeze
Hi there im trying to create a model for a runescape item. Using there api and httparty. Im having a number of issues. But this one is regarding the use of overriding the initialize method saying it has the wrong number of arguments.
class Item < ApplicationRecord
require 'json'
include HTTParty
base_uri 'http://services.runescape.com/m=itemdb_oldschool/'
attr_accessor :name, :description, :price, :icon_url
def initialize(name, description, price, icon_url)
super
self.name = name
self.description = description
self.price = price
self.icon_url = icon_url
end
def self.find(name)
response = get("/api/catalogue/detail.json?item=#{name}")
if response.success?
parsed = JSON.parse(response)
self.new(
parsed["item"]["name"],
parsed["item"]["description"],
parsed["item"]["current"]["price"],
parsed["item"]["icon_large"]
)
else
# this just raises the net/http response that was raised
raise response.response
end
end
end
So in rails console i run the following command to test it:
item_test = Item.find("227")
ArgumentError: wrong number of arguments (given 4, expected 0..1)
from /Users/jacksharville/.rvm/gems/ruby-2.3.1/gems/activerecord-5.0.1/lib/active_record/core.rb:312:in `initialize'
from /Users/jacksharville/Desktop/OSCRUDDY/app/models/item.rb:13:in `initialize'
from /Users/jacksharville/.rvm/gems/ruby-2.3.1/gems/activerecord-5.0.1/lib/active_record/inheritance.rb:65:in `new'
But the initialize takes 4 arguments. When reduce it to one then it says it requires 4. Which leaves me very confused.
Im not even sure that overriding the base initialise is the way forward to do something like this. So if you have a better idea please let me know i'm new to this.
In conclusion my question is why is my object not being created correctly? Secondly is this the right approach for creating the object?
The problem I see here is that you are trying to mixin HTTParty with an ActiveRecord subclass, when really you should have two separate classes. The Item class should be responsible for interfacing with your database. You should create another class, i.e. ItemResource with Httparty, which will have the responsibility of connecting to the Runescape resource and respond with a response. You should use this to grab the data from the resource, and create an Item record with that data. Single Responsibility Principle
One thing to keep in mind is that you should almost never be redefining initialize for any models that inherit from ApplicationRecord. Checkout the API
EDIT
This code should get you relatively close
#app/models/item.rb
class Item < ApplicationRecord; end
#app/models/item_resource.rb | app/resources/item_resource.rb or something similar
require 'json'
class ItemResource
include HTTParty
self.base_uri 'http://services.runescape.com/m=itemdb_oldschool/'
def find name
response = get("/api/catalogue/detail.json?item=#{name}")
if response.success?
parsed = JSON.parse(response)
Item.new(
name: parsed["item"]["name"],
description: parsed["item"]["description"],
price: parsed["item"]["current"]["price"],
icon_large: parsed["item"]["icon_large"]
)
else
raise response.response
end
end
end
#somewhere else
item = ItemResource.find 'Elysian Spirit Shield'
if item.save
#do stuff
else
#handle failure
end
I am attempting to use Rails Concerns (or even a bare Module mixin) to share methods across some of my models.
Given a simple model, I am storing some encoded data in one of the
fields:
class DataElement < ActiveRecord::Base
include EmbeddedData
ENCODED = %w(aliases)
end
I’ve then made a concern with the needed methods for managing the data:
module EmbeddedData
extend ActiveSupport::Concern
included do
after_find :decode_fields
before_save :encode_fields
#decoded = {}
end
def decoded(key, value = false)
#decoded[key][:value] if #decoded.has_key? key
end
def decode_fields
#decoded = {} if #decoded.nil?
ENCODED.each do |field|
if attributes[field]
#decoded[field] = {
value: JSON.parse(attributes[field]),
dirty: false
}
end
end
end
def encode_fields
ENCODED.each do |field|
if decoded[field] && decoded[field][:dirty]
attributes[field] = #decoded[field][:value].to_json
end
end
end
end
Given this setup, I get the error uninitialized constant EmbeddedData::ENCODED
If I change the reference to self::ENCODED in the Concern I get the error:
# is not a class/module
I've even tried making a method on the concern register_fields that I can then call from the model, but the model just throws an unknown method error.
Running out of ideas here and looking for help.
So it turns out the way to access the class constant is:
self.class::ENCODED
I have this error
ArgumentError (wrong number of arguments (1 for 0)):
lib/law/production.rb:20:in `clone'
lib/law/production.rb:20:in `clone_law'
lib/law/production.rb:11:in `initialize'
app/controllers/laws_controller.rb:86:in `new'
app/controllers/laws_controller.rb:86:in `prod_law'
app/controllers/laws_controller.rb:44:in `create'
when using this
module Law
class Production
attr_accessor :law
attr_accessor :creator
def initialize(law,current_user)
#law = law
#creator = current_user
clone_law
end
def current__user
User.find_by_authentication_token(session[:_csrf_token])
end
def clone_law
clone(#law)
end
end
end
where clone, create, prod_law are some methods
I assume Rails is expecting a hash but I don't understand why
Firstly, clone is a standard Ruby method.
Secondly, it expects no arguments at
all (as error message says), it should be called on the object you want to clone, like this:
#law.clone
I'd appreciate any help I can get with a somewhat strange phenonemon going on in my code. The controller's create method is (roughly) as follows:
def create
#session ||= Session.new
#session.date = params[:date]
#session.generate_string
#session.save
# etc
end
And the model:
class Session < ActiveRecord::Base # table is 'sessions' with 3 columns :id, :str, :date
include MyHelper
def generate_string(num_chars)
#str ||= ""
num_chars.to_i.times do
#str += some_method_in_MyHelper() # method returns a string
end
end
end
With some logging I found out that although the generate_string is working correctly, the resulting #session (in the controller) has the date set as expected but the value of str is a blank string. Sure enough, when the .save is hit, the database is told to insert a record consisting of a blank string and the correct date.
I found this Why do my changes to model instances not get saved sometimes in Rails 3? that suggests I should be using the "self" prefix instead of #. This seems to make the code work as expected, but seems strange because I thought self.xxx referred to the class, not the class instance. I'd be grateful if anyone could clarify what's going on - thanks!
self refers to the instance when used inside an instance method. It refers to the class outside an instance method, when it (self) is the class that's being defined.
# is an instance variable, which is different than an ActiveRecord column.
In order to store it in the str field to be saved to the database, you need to use self.str method. I think this is what you are looking for
class Session < ActiveRecord::Base # table is 'sessions' with 3 columns :id, :str, :date
include MyHelper
def generate_string(num_chars)
str = ""
num_chars.to_i.times do
str += some_method_in_MyHelper() # method returns a string
end
self.str = str # store it in attribute to be saved into db
end
end
Notice I removed the instance variable #str and changed it to local variable str instead because it seems like you want to generate a new string everytime this method is called? Also, this variable caching is useless
#session ||= Session.new
because instance variables only stick around for a single request. It should be
#session = Session.new