Adding User Input to a hash? Ruby - ruby-on-rails

I am trying to create some simple programs as trying to learn Ruby and then move on to rails, I am just playing about to try and get used to the flow of how different types of code work variables, loops etc.
I am trying to create a simple book system were I already have 3 books in my hash and then I want to list the books already in the library in the console and then I want to be able to add new books and then also loop through and display the new list to the console.
require 'rubygems'
class GetDetailsFromUser
books = {
Eagle_Eye: 1,
Eage_Eye1: 2,
Eagle_Eye2: 3
}
books.each do |i|
puts i
end
while true
add = gets.chomp
break if add.empty?
books << add
end
puts 'New list is below'
books.each do |i|
puts i
end
end
Were am I going wrong? I manage to print out the hash to the console, however, I get an error message
undefined method '<<' for {:Eagle_Eye=>1,...
Why is the method undefined? books << add? This should add a new book to the book hash table?

Add your second number with it. Here is a working example I wrote for you
Live Demo - VISIT THIS LINK
books = {
Eagle_Eye: 1,
Eage_Eye1: 2,
Eagle_Eye2: 3
}
books.each do |i|
puts i
end
while true
puts "What book would you like to add?"
add = gets.chomp
if add.empty? == true
puts "Error - You did not enter a title for a book"
break
else
books.max_by do |book,id|
#list_number = id
end
books[add.to_sym]=#list_number
break
end
end
puts 'New list is below'
books.each do |i|
puts i
end

refactored version of #ExecutiveCloser
books = {
Eagle_Eye: 1,
Eage_Eye1: 2,
Eagle_Eye2: 3
}
books.each do |i|
puts i
end
add = ""
while add.empty? do
puts "What book would you like to add?"
add = gets.chomp
books[add.to_sym] = books.size + 1 unless add.empty?
end
puts 'New list is below'
books.each do |i|
puts i
end
https://repl.it/CDK2/3

Ruby has a documentation site. E. g. Hash documentation clearly states, that there is no method << defined on the hash instance.
<< method is defined on Array.
Why would you expect “books << add should add a new book to the book hash table”?
There is another glitch within your code: you execute everything on class definition level, which makes absolutely no sense in this case.
Since it is unclear what do you want to achieve, I am unable to provide a ready-to-go-with solution.

Related

How to create an instance of a class in Rails, giving each new instance 5 has_many through relationships

I want to make an instance of my Test class get five practice questions through a join class when it is initialised. If a test is an "exam" then it should just get 5 exam questions without a join class. (the question types have different models)
So far It doesn't behave the way I expect
self.practice_questions = []
it makes 5 join classes every time, but the array of self.practice_questions stays empty.
def get_questions
puts "ASDASDASDASDSAD"
array = []
if self.for_practice
puts "ASDASDASOASKODKSAOKDASODKOASKDSAOKDOASKDOASK"
PracticeQuestion.sort_for_selection[0...5].each do |question|
array << question
question.use_practice_question
end
elsif for_practice === false
puts self.exam_questions
if self.exam_questions.length ===0
grab 5 unused exam type questions
ExamQuestion.unused[0...5].each do |question|
puts "grabbing question #{question.title}"
question.test = self
question.use_question
end
end
puts "hello"
puts self.practice_questions.length
self.practice_questions ||= array
self.save
puts self.practice_questions.length
self.practice_questions.each {|question| puts question.title}
end
self.practice_questions ||= array will only assign the array if self.practice_questions is false or nil, are you sure it's one of those?
If practice_questions is a has_many, try one of this:
array.each do |el|
self.practice_questions << el
end
or:
self.practice_questions_ids = array.map(&:id)
https://guides.rubyonrails.org/association_basics.html#has-many-association-reference

Elegant way to test that new objects are created

I'm building a very basic test to check if an object is created in a relationship. Website has_many languages. I've the method add_language that receives an array of ids and adds them.
This is some of my tests:
describe '#add_languages' do
subject{ build(:website) }
it 'return nil if no language is added' do
expect(subject.add_languages []).to be_nil
end
it 'adds one single language' do
languages_ids = [Language.last.id]
original_count = subject.languages.count
subject.add_languages languages_ids
expect(subject.languages.count).to eq(original_count + languages_ids.count)
end
it 'adds multiple languages' do
languages_ids = Language.limit(3).pluck :id
original_count = subject.languages.count
subject.add_languages languages_ids
expect(subject.languages.count).to eq(original_count + languages_ids.count)
end
end
What I don't like is
1) The code is duplicated
2) I don't like using eq for this, but not sure how to use other matchers
What would be an elegant way to write those tests?
Two quick ways you can do this: add a helper method, or add a loop.
Helper Method
it "adds on single language" do
check_creates_languages([Language.last.id])
end
it "Adds multiple languages" do
check_creates_languages(...)
end
def check_creates_languages(language_ids)
original_count = ... # etc
end
Loop
[1,3].each do |language_count|
it "can add #{language_count} language(s)" do
original_count = Language.last(language_count).count
# etc
end
end
Also, there's nothing wrong with using eq, that's what it's there for. What else would you want to use? You could also use be.
https://github.com/rspec/rspec-expectations
Simply you can do:
expect{subject.add_languages languages_ids}.to change{Language.count}.by(1)
More or less (based on your use case).

Creating objects/hashes and storing them in an array

I'm trying to create a function that validates data stored in my database. Say I have a table Foo. For each item in Foo i call a function say Bar which validates it by using a set of checks. If the data is not correct, I store the item id along with a description of the reason of failure in a hash. The hash is pushed on to an array.
ErrorList = []
MyHash = Hash.new {|h,k| h[k]=[]}
Foo.each do |f|
unless f.valid?
MyHash["foo_id"] = f.id
MyHash["description"] = "blah blah blah"
ErrorList.push MyHash
end
end
At the end of execution, all entries in the array are the same since the hash entries are overwritten. Is there a way I can use this hash to store different id's and description in my array. Otherwise, is there a way to use objects instead to overcome this issue?
I'm using rails version 2.3.5
First MyHash shouldn't be camel case, but you can just do...
myhash = {}
Foo.each do |f|
unless f.valid?
myhash[f.id] = "blah blah blah"
end
end
Assuming all the id's are unique, you only need this hash. This will set the key value pair to: id: blah blah blah
I believe you could write the above using reject and map:
error_list = foos.reject(&:valid?).map do |f|
{ "foo_id" => f.id, "description" => "blah blah blah" }
end
This will create a list of hashes, each with a "foo_id" and "description" of all the invalid foos.
To make your code work, you need to make sure you are creating a new hash for each element in the list, and not re-using the hash you already created:
ErrorList = []
Foo.each do |f|
unless f.valid?
my_hash = Hash.new {|h,k| h[k]=[]}
my_hash["foo_id"] = f.id
my_hash["description"] = "blah blah blah"
ErrorList.push my_hash
end
end
It was Matz himself who pointed out that, over time, "object-oriented" became such a common term, that we tend to underestimate its power. However old-fashioned it may sound, Ruby is an OO language, and you should write OO programs with it. There are many possible approaches. One possible example would be
class ValidatedTable < Array
def self.new array=[]
array.each_with_object new do |e, o| o << e end unless array.empty?
super
end
def << element
fail TypeError, "blah blah blah" unless element.valid?
super
end
end
And now, with this ValidatedTable whose #<< method complains about invalid inputs, we can easily achieve your objective:
validated_table, error_list = Foo.each_with_object [ ValidatedTable.new, [] ] do |e, o|
begin
o[0] << e
rescue TypeError => msg
e[1] << { id: e.id, description: msg.to_s }
end
end
The distinctive aspect of this solution is that it creates a special subclass of array, ValidatedTable, which, at any moment, is guaranteed to only contain valid elements. Attempts to push invalid elements into it raise an error, which can be rescued and used to produce an error list.

ordering in rails based on the count of a word present in a single column

I have a model named Vendor. I have three different models associated with it.
Testimonial
Service
Works
I have to look up each of the tables and list the vendors(limit 5) which have the word "example" most number of times in a column in one of these models. How do I do this in rails?
I have something like this
Vendor.joins(:testimonials).where("testimonials.content LIKE ?","%example%")
I need to find the vendors who has the maximum number of occurrences of the word "example"
I hope I got you right now:
a=[]
vendors.each do |v|
c=0
c=v.testimonial.scan(/example/).count
if c>0
a=a.concat([{vendor: v, counts: c}])
end
In Ruby you can count the occurrence of a substring in a string in this way, for example
s = "this is a string with an example, example, example"
s.scan(/example/).count # => 3
This is how I ended up doing this. Not sure if my question was asked correctly. I made this with the help of a previous answer by #user3383458
vendor = Hash.new
Vendor.all.each do |v|
testi_word_count = 0
service_word_count = 0
title_word_count = 0
v.testimonials.each do |testi|
testi_word_count += testi.content.scan(/#{word_to_search}/).count
Rails.logger.info "testi_word_count"
Rails.logger.info testi_word_count
end
v.services.each do |service|
service_word_count += service.name.scan(/#{word_to_search}/).count
Rails.logger.info "service_word_count"
Rails.logger.info service_word_count
end
v.works.each do |work|
title_word_count += work.title.scan(/#{word_to_search}/).count
Rails.logger.info "title_word_count"
Rails.logger.info title_word_count
end
vendor[v]=testi_word_count + service_word_count + title_word_count
end

Trying to relate two lists

I have two Model level functions that populate two arrays, my_favorite_brands and my_favorites. They are defined as follows:
def self.my_favorite_brands
brand_list = Array.new
Favorite.my_favorites.each do |favorite|
brand_list.push(favorite.style.shoe.brand)
end
brand_list.uniq
end
And here is my_favorites:
def self.my_favorites
Favorite.where(:user => current_user)
end
I want to print out each Brand in my_favorite_brands and while doing so, for each Brand print out all of it's associated Favorites in my_favorites. The relation between the two models Brand and Favorite is the following. Brand has many Shoes which has many Styles. Favorite belongs to Style and it belongs to User. Here is some probably non-functional pseudo-ish (in that it doesn't really work) code that emulates what I want to do.
#Controller stuff
#fav_brands = Brand.my_favorite_brands
#fav_favorites = Favorite.my_favorites
#in the view
favorites_by_brand = Array.new
#fav_brands.each do |brand|
favorites_by_brand = #fav_favorites.map do |favorite|
unless favorite.style.shoe.brand == brand
#fav_favorites.delete("favorite")
end
favorites_by_brand.each do |favorite|
puts favorite.style
end
end
I am trying to create a complete list of favorites where favorite.style.shoe.brand is equal to the current brand I am iterating over, so that I can display the styles by Brand.
I figured out a way to accomplish what I want to accomplish, although it's probably not Ideal .
styles_of_favorites = #fav_favorites.map {|favorite| favorite.style}
#fav_brands.each do |brand|
brand.shoes.each do |shoe|
shoe.style.each do | style|
if styles_of_favorites.include?(style)
puts style
end
end
end
end

Resources