I am trying to get programm started where i cant read in a csv File an it prints the data out on a pdf-File. Now i have a problem.
Heres is my Code:
------------------------------------
require_relative 'friends'
class List
attr_accessor :list_name, :list
def initialize(list_name)
#list_name = list_name
#list = []
end
def list_name
#list_name
end
def liste
#list
end
def wert(place)
#list[place].to_s
end
def list_length
#list.length
end
def print_list
#list.each do |freunde|
"#{freunde.name},#{freunde.age}"
end
end
def add_friend(a_friend)
#list.push(a_friend)
end
def load_friend(from_file)
File.readlines(from_file).each do |line|
add_friend(Freunde.from_csv(line))
end
end
end
-------------------------------------------
require_relative 'list'
class Friends
attr_accessor :name,:age
def initialize(name, age)
#name = name
#age = age
end
def self.from_csv(string)
name, age = string.split(',')
Freunde.new(name,age)
end
def friends
#name
end
end
-------------------------------------------
require 'prawn'
require_relative 'list'
require_relative 'friends'
class Generating
include Prawn::View
def initialize
#document = Prawn::Document.new(:page_size => "A4")
#fontpath = File.expand_path("../data/fonts", __FILE__)
liste1 = Listen.new("Friendslist")
liste1.load_friend("test.csv")
print_list
save
end
def print_friends
font("#{#fontpath}/Arial.ttf") do
font_size 11
text_box("#{liste1.print_list}", :at => [15,405], :height => 50,
:width => 250)
end
end
def save
self.render_file "Hello.pdf"
end
end
---------------------------------------------
When i now create a new generating-Object:
gen = Generating.new
then it fails the whole programm because the error says method unknow (print_list). Am i submitting the wrong object for the method(print_list), or am using the text output methods of prawn wrong?
print_list is an instance method of List class, and you call it on self object, which is there an instance of Generating. It should be:
liste1 = Listen.new("Friendslist")
liste1.load_friend("test.csv")
#⇓⇓⇓⇓⇓⇓
liste1.print_list
Related
Item class
class Item
def initialize(options = {})
#name = options[:name]
#code = options[:code]
#category = options[:category]
#size = options[:size]
end
attr_accessor :name, :code, :category, :size
end
Music class
class Music < Item
def initialize(options = {})
super
#singer = options[:singer]
#duration = options[:duration]
end
attr_accessor :singer, :duration
end
Movie class
def initialize(options = {})
super
#director = options[:director]
#main_actor = options[:main_actor]
#main_actress = options[:main_actress]
end
attr_accessor :director, :main_actor, :main_actress
end
class Catalog
attr_reader :items_list
def initialize
#items_list = Array.new
end
def add(item)
#items_list.push item
end
def remove(code)
#items_list.delete_if { |i| i.code == code }
end
def show(code)
# comming soon
end
def list
#items_list.each do |array|
array.each { |key, value| puts "#{key} => #{value}" }
end
end
end
catalog1 = Catalog.new
music1 = Music.new(name: "Venom", code: 1, category: :music, size: 1234, singer: "Some singer", duration: 195)
music2 = Music.new(name: "Champion of Death", code: 2, category: :music, size: 1234, singer: "Some singer", duration: 195)
catalog1.add(music1)
catalog1.add(music2)
ruby version 2.6.0
list method is not working. I got undefined method `each' for <#Music:0x0000562e8ebe9d18>.
How can I list all keys and values in another way? Like:
name - "Venom"
code - 1
category - music.
I was thinking about it, but also I got a Movie class and that method gonna be too long
You push instances of Music into #items_list. That means #items_list.each do not return an array, but instances of Music and that Musik instances do not respond do each nor they have keys and values.
I suggest adding an instance method to your Music class that returns the expected output. For example a to_s method like this:
def to_s
"name \"#{name}\" code - #{code} category - #{category}"
end
and to change the list method in your Catalog to something like this:
def list
#items_list.each do |music|
puts music.to_s
end
end
Or when you want to return the values an array of hashed then add a to_h method to Music like this:
def to_h
{ name: name, code: code, category: category }
end
and call it like this:
def list
#items_list.map do |music|
music.to_h
end
end
I have a file that uses a DSL for complex configuration, part of that larger DSL is a name/value settings DSL.
Whenever I use a setting called name, I get an error.
Failure/Error: name 'SomeName'
ArgumentError:
wrong number of arguments (given 1, expected 0)
I'm looking for a solution to this edge-case.
Any instance of the SettingsDsl can have a unique name to identify itself, this is an optional parameter for the initializer.
example
main_dsl = MainDsl.new do
settings do
rails_port 3000
end
settings :key_values do
rails_port 3000
end
end
This would create two instances of SettingsDsl, one with a name of :settings and the other with a name of :key_values
This is the code for MainDsl and SettingsDsl
class MainDsl
attr_reader :meta_data
def initialize(&block)
#meta_data = HashWithIndifferentAccess.new
if block_given?
self.instance_eval(&block)
end
end
def settings(name = nil,&block)
settings = SettingsDsl.new(#meta_data, name, &block)
puts setting.name
settings
end
end
class SettingsDsl
attr_reader :name
def initialize(data, name = nil, &block)
#data = data
#name = name ||= :settings
#data[name] = {}
self.instance_eval(&block) if block_given?
end
def method_missing(key, *args, &block)
#data[#name][key] = args[0]
end
def get_value(key)
#data[#name][key]
end
end
All works well until I use an internal key/value pair called name
I use method_missing to find new keys and store those values into a Hash, but in the case of name, there is already an attr_reader and this uses a slightly different signature and causes an argument error.
main_dsl = MainDsl.new do
settings do
rails_port 3000
name 'SomeName' # Intercept the error for this and store 'SomeName' into #data[#name]['name']
another_property 'Something Else'
end
settings :more do
hello 'world'
end
end
Failure/Error: name 'SomeName'
ArgumentError:
wrong number of arguments (given 1, expected 0)
The problem happens because internally there is a name attribute for the settings group, e.g.
SettingsDsl.new('name_goes_here')
# and gets stored in #name
# and is accessible via attr_reader :name
# which creates a read-only method called name. e.g.
SettingsDsl.new('name_goes_here').name
I would like to intercept the ArgumentError for this one method call and handle this edge-case appropriately
Final output could then look like
{
'settings': {
'rails_port': 3000,
'name': 'SomeName',
'another_property': 'Something Else'
},
'more': {
'hello': 'world'
}
}
You can get rid of the attr_reader :name and implement the edge-case logic yourself:
def name(*args)
args.empty? ? #name : #data[#name][:name] = args[0]
end
Here is a complete example:
stackoverflow-57540225.rb:
require 'active_support/hash_with_indifferent_access'
require 'json'
class MainDsl
attr_reader :meta_data
def initialize(&block)
#meta_data = HashWithIndifferentAccess.new
if block_given?
self.instance_eval(&block)
end
end
def settings(name = nil,&block)
settings = SettingsDsl.new(#meta_data, name, &block)
end
end
class SettingsDsl
def initialize(data, name = nil, &block)
#data = data
#name = name ||= :settings
#data[name] = HashWithIndifferentAccess.new
self.instance_eval(&block) if block_given?
end
def name(*args)
args.empty? ? #name : #data[#name][:name] = args[0]
end
def method_missing(key, *args, &block)
#data[#name][key] = args[0]
end
def get_value(key)
#data[#name][key]
end
end
main_dsl = MainDsl.new do
settings do
rails_port 3000
name 'SomeName'
another_property 'Something Else'
end
settings :more do
hello 'world'
end
end
puts main_dsl.meta_data.to_json
Result:
$ ruby stackoverflow-57540225.rb | jq .
{
"settings": {
"rails_port": 3000,
"name": "SomeName",
"another_property": "Something Else"
},
"more": {
"hello": "world"
}
}
Let see the code first that will help what I want to achieve:
class PostalInfo
attr_reader :name, :code
def initialize (id, name, code)
#id = id
#name = name
#code = code
end
def method_missing(method, *args, &blk)
if method.to_s == "#{name}"
return code
else
super
end
end
end
pi1 = PostalInfo.new(1, 'united_states', 'US')
pi2 = PostalInfo.new(2, 'united_kingdom', 'UK')
So when I run below code, it gives output as:
pi1.united_states => 'US'
pi2.united_kingdom => 'UK'
its fine upto here, but I also want to do something like
PostalInfo.united_states => 'US'
PostalInfo.united_kingdom => 'UK'
how to do that, thanks in advance
This sets up a class attribute to hold the data, and whenever an instance is initialized, it adds to that data structure, and uses a similar class-level method_missing.
class PostalInfo
attr_reader :name, :code
##postal_info = {}
def self.method_missing(method, *args, &blk)
name = method.to_s
if ##postal_info[name]
##postal_info[name]
else
super
end
end
def initialize (id, name, code)
#id = id
#name = name
#code = code
##postal_info[#name] = #code
end
def method_missing(method, *args, &blk)
if method.to_s == "#{name}"
return code
else
super
end
end
end
pi1 = PostalInfo.new(1, 'united_states', 'US')
pi2 = PostalInfo.new(2, 'united_kingdom', 'UK')
PostalInfo.united_states #=> 'US'
PostalInfo.united_kingdom #=> 'UK'
I will say, this seems like a weird design, and I'd normally recommend avoiding using mutable state with class methods, and method_missing wherever possible.
You can write something like this:
class PostalInfo
POSTAL_HASH = {
united_states: 'US',
united_kingdom: 'UK',
}.freeze
def self.method_missing(method, *args, &blk)
POSTAL_HASH[method] || super
end
end
Skipping missing method might result in better performance:
class PostalInfo
POSTAL_HASH = {
united_states: 'US',
united_kingdom: 'UK',
}.freeze
class << self
POSTAL_HASH.each do |name, code|
define_method(name) do
code
end
end
end
end
With one exception, you need to mimic the code in the first part of your answer in the class' singleton class. The difference concerns the initialisation of the instance variables. Rather than using PostalInfo::new and PostalInfo#initialize, you need to create a class method for doing that (which I've called add_country_data). Note that as the class' instance variable id is not used I've not included it in the code.
class PostalInfo
class << self
attr_reader :country_data
def add_country_data(name, code)
(#country_data ||= {})[name] = code
end
def add_country_data(name, code)
#country_data[name] = code
end
def method_missing(method, *args, &blk)
return country_data[method.to_s] if country_data.key?(method.to_s)
super
end
end
end
PostalInfo.add_country_data('united_states', 'US')
PostalInfo.add_country_data('united_kingdom', 'UK')
PostalInfo.united_states
#=> "US"
PostalInfo.united_kingdom
#=> "UK"
PostalInfo.france
#=> NoMethodError (undefined method `france' for PostalInfo:Class)
Though this meets your requirement, I would be inclined to construct the class in a more conventional way:
class PostalInfo
attr_reader :name, :code
#instances = []
def initialize(name, code)
#name = name
#code = code
self.class.instances << self
end
singleton_class.public_send(:attr_reader, :instances)
end
us = PostalInfo.new('united_states', 'US')
uk = PostalInfo.new('united_kingdom', 'UK')
us.code
#=> "US"
uk.code
#=> "UK"
PostalInfo.instances
#=> [#<PostalInfo:0x00005c1f24c5ccf0 #name="united_states", #code="US">,
# #<PostalInfo:0x00005c1f24c71858 #name="united_kingdom", #code="UK">]
I'm having issues generating a Signature via a Ruby class. When I go into my docker container, I'm able to see that all the instance variables in the initialize method are nil expect the #api_key variable.
I have the following class
require 'openssl'
require 'base64'
module SeamlessGov
class Form
include HTTParty
attr_accessor :form_id
base_uri "https://nycopp.seamlessdocs.com/api"
def initialize()
#api_key = ENV['SEAMLESS_GOV_API_KEY']
#signature = generate_signature
#form_id = ENV['SEAMLESS_GOV_FORM_ID']
#timestamp = Time.now.to_i.to_s
end
def relative_uri
"/form/#{#form_id}/elements"
end
def create_form
self.class.get(relative_uri, headers: generate_headers)
end
private
def generate_signature
OpenSSL::HMAC.hexdigest('sha256', ENV['SEAMLESS_GOV_SECRET'], "GET+#{relative_uri}+#{#timestamp}")
binding.pry
end
def generate_headers
{
"Authorization" => "HMAC-SHA256 api_key='#{#api_key}' signature='#{#timestamp}'",
Net::HTTP::ImmutableHeaderKey.new('AuthDate') => "#{#timestamp}"
}
end
end
end
As you see, from the binding.pry in the generate_signature method I'm able to see the instance variables:
The relative_uri method needed to generate the signature doesn't load the #form_id variable in the string.
Here is the controller:
class FormsController < ApplicationController
def display_form
#form = SeamlessGov::Form.new().create_form
end
end
Work around net/http headers case sensitivity:
lib/net_http
require 'net/http'
class Net::HTTP::ImmutableHeaderKey
attr_reader :key
def initialize(key)
#key = key
end
def downcase
self
end
def capitalize
self
end
def split(*)
[self]
end
def hash
key.hash
end
def eql?(other)
key.eql? other.key.eql?
end
def to_s
def self.to_s
key
end
self
end
end
If I call create_form this is the output:
{"error"=>true,
"error_log"=>
[{"error_code"=>"missing_date_headers",
"error_message"=>"Request is missing date headers",
"error_description"=>
"{\"Host\":\"nycopp.seamlessdocs.com\",\"Connection\":\"close\",\"X-Real-IP\":\"71.249.243.7\",\"X-Forwarded-For\":\"71.249.243.7\",\"X-Forwarded-Host\":\"nycopp.seamlessdocs.com\",\"X-Forwarded-Port\":\"443\",\"X-Forwarded-Proto\":\"https\",\"X-Original-URI\":\"\\/api\\/form\\/\\/elements\",\"X-Scheme\":\"https\",\"Authorization\":\"HMAC-SHA256 api_key='h123xxxxxxxxxx' signature=''\",\"AuthDate\":\"\"}"},
{"error_code"=>"external_auth_error", "error_message"=>"Date header is missing or timestamp out of bounds"}]}
What is the issue?
The mistake is in the order of operations/calculations.
def initialize()
#api_key = ENV['SEAMLESS_GOV_API_KEY']
#signature = generate_signature # <= at this point, neither form_id nor timestamp are set. but api key is.
#form_id = ENV['SEAMLESS_GOV_FORM_ID']
#timestamp = Time.now.to_i.to_s
end
I am getting an error when running this code. The Following is the output:
L
Bicycle#Ex3.rb:32:in `spares': private method `select' called for nil:NilClass (NoMethodError)
from Bicycle#Ex3.rb:10:in `spares'
from Bicycle#Ex3.rb:111:in `<main>'
Here is the code:
class Bicycle
attr_reader :size, :parts
def initialize(args={})
#size = args[:size]
#parts = args[:parts]
end
def spares
parts.spares # return an array
end
def lead_days
1
end
#...
end
class Parts
attr_reader :parts
def initialize(args={})
#parts = parts
end
def size
parts.size
end
def spares
parts.select{|part| part.needs_spare}
end
end
class Part
attr_reader :name, :description, :needs_spare
def initialize(args)
#name = args[:name]
#description = args[:description]
#needs_spare = args.fetch(:needs_spare, true)
end
end
class RoadBikeParts < Parts
attr_reader :tape_color
def post_initialize(args)
#tape_color = args[:tape_color]
end
def local_spares
{tape_color: tape_color}
end
def default_tire_size
'23'
end
end
class MountainBikeParts < Parts
attr_reader :front_shock, :rear_shock
def post_initialize(args)
#front_shock = args[:front_shock]
#rear_shock = args[:rear_shock]
end
def local_spares
{ rear_shock: rear_shock}
end
def default_tire_size
'2.1'
end
end
chain = Part.new(
name: 'chain',
description: '10 speed')
road_tire = Part.new(
name: 'tape_size',
description: '23')
tape = Part.new(
name: 'tape_color',
description: 'red')
mountain_tire = Part.new(
name: 'tire_size',
description: '2.1')
rear_shock = Part.new(
name: 'rear_shock',
description: 'Fox')
front_shock = Part.new(
name: 'front_shock',
description: 'Manitou',
needs_spare: false)
road_bike_part = Parts.new([chain, road_tire, tape])
road_bike = Bicycle.new(
size: 'L',
parts: Parts.new([chain,
road_tire,
tape]))
puts road_bike.size
#puts road_bike.parts.size
puts road_bike.spares.size
It is clear this line --> puts road_bike.spares.size is given the error NoMethodError, however, I am not sure how I can make a work around to correct this issue for this example. The spares method is returning an array of Part objects, however it seems my problem lies in the fact the spares method .select is private from the calling object.
Any advice to revise this code would be great. Thanks.
What's happening here is that Parts#parts is nil. You're getting the error on this line:
# parts is nil
parts.select{|part| part.needs_spare}
In the initializer of Parts, its parts attribute does not get assigned properly:
def initialize(args={})
#parts = parts
end
So when being initialized, it assigns #parts with the value of parts. But since parts is not a local variable there, it calls the Parts#parts method, which returns nil.
If you change the initializer to the following:
def initialize(parts)
#parts = parts
end
You'll be able to run the code. But subclasses of Parts seem to expect a Hash in the initializer, rather than an Array like their super class does though.