undefined method with googlebooks - ruby

I'm building a library in sinatra, using postresql as database and a 'googlebooks' gem to find the informations I want.
This is my code in main.rb
get '/list' do
#books = GoogleBooks.search(params[:title])
erb :list
end
get '/info/:isbn' do
#this is to get the info from my database if there is any
if book = Book.find_by(isbn: params[:isbn])
#book_title = book.title
#book_authors = book.author
#book_year = book.year
#book_page_count = book.page_count
#book_id = book.id
#book_isbn = book.isbn
else
#use the gem to look for the data
text = GoogleBooks.search(params[:isbn])
#book_title = text.title
#book_author = text.authors
#book_year = text.published_date
#book_cover = text.image_link(:zoom => 2)
#book_page_count = text.page_count
#book_notes = text.description
#and then store it into the database
book = Book.new
book.title = #book_title
book.authors = #book_authors
book.publish_date = #book_year
book.image_link = #book_cover
book.page_count = #book_page_count
book.description = #book_notes
book.isbn = params[:isbn]
book.save
#book_id = book.id
end
erb :info
end
This is my erb file :list
<div class='list_by_title'>
<% #books.each do |text| %>
<ul>
<li class='list_by_title'>
<%= text.title %> (Authour: <%= text.authors %>)
</li>
</ul>
<%end%>
</div>
The list part works.. I'm able to have a page with a list of titles... the problem is when I try to call the data from the params isbn, I always have this error:
NoMethodError at /info/0596554877
undefined method `title' for #<GoogleBooks::Response:0x007ff07a430e28>
Any idea for a possible solution?

As per documentation, GoogleBooks::Response is an enumerator. So you need to iterate over it by using each, and invoke the title method on individual objects retrieved from enumerator.
result = GoogleBooks.search(params[:isbn])
result.each do |text|
#book_title = text.title
...
end
Variable name text for details about a book is not a good name, you need to pick apt variable names, many a times good variable names makes debugging easier

Related

Paginating no Active Records with will_paginate just show the values for the first page, the next pages are completely empty

I'm trying to paginate some results using the will_paginate gem in Ruby on Rails. The thing is the results are non Active Records so I follow the steps here: https://www.rubydoc.info/github/mislav/will_paginate/WillPaginate/Collection
to be able to do that, but the problem is that now I'm just able to see the results for the first page, the next pages are completely empty. I verified that the query has results for the next pages and everything looks good.
This is the code I wrote, any idea guys. Thanks!
I attached some pictures to show you guys how looks like, one is from the first page results and the other is from the second page results.
# controller AssetController
require Rails.root.to_s + '/config/initializers/will_paginate_array'
class AssetController < ApplicationController
before_action :require_login
def index
#search = params[:search]
#assetType = params[:assetType]
#searchBy = params[:searchBy]
limit = 30
currentPage = params[:page];
if currentPage == nil
currentPage = 1
end
offset = (currentPage.to_i - 1) * limit
count = Assets.getAssetsCount()
results = Assets.getAllAssets(offset, limit)
if results.class == Array
#assets = results.paginate_custom_array(currentPage, limit, count)
else
# TODO
end
end
# config/initializers/will_paginate_array.rb
require 'will_paginate/collection'
Array.class_eval do
def paginate_custom_array(page = 1, per_page = 15, size)
WillPaginate::Collection.create(page, per_page, size) do |pager|
pager.replace self[pager.offset, pager.per_page].to_a
end
end
end
# views/asset/index.erb
<div id="assets_results">
<%= render 'partials/assets_result' %>
</div>
# views/asset/index.js.erb
$("#assets_results").html("<%= escape_javascript render("partials/assets_result") %>");
$('#paginator').html('<%= escape_javascript(will_paginate(#assets, :remote => true).to_s) %>');
# views/partial/_assets_result.html.erb
<% #assets.each do |asset| %>
<tr>
<td>
...
</td>
</tr>
<% end %>
<div class="text-center">
<div id="paginator"class="pagination pagination-lg">
<%= will_paginate #assets, :remote => true %>
</div>
</div>
You are applying pagination logic twice.
First, you select only one page of results from your Assets.getAllAssets method:
count = Assets.getAssetsCount()
results = Assets.getAllAssets(offset, limit)
Then, you pass that single page of 30 records into will_paginate's own pagination methods, which cannot show you results 31..60 if you only give it a single page of 30 results.
You should either drop your will_paginate logic, or change your getAllAssets method to return something like an ActiveRecord::Relation that can have a limit/offset applied to it by will_paginate.

setting and array of params in rails

A series of objects need to be purchased in a single transaction, however each item must be a different transaction object due to relationship issues.
A confirmation action aims to list each of these items independently. The controller attempts to prepare parameters for viewing each transaction
<% #transactions.each do |transaction| %>
<%= params[:product_id][:product_id] %>
[...]
as follows:
#transactions = params[:product_ids].map do |product_id|
#transaction = Transaction.new
#inventories = Inventory.where(['product_id = ?', product_id]).all
#my_price = my_price
params[:product_id][:product_id] = product_id # sanity check
params[:product_id][:additional_info] = session[:additional_info]
params[:product_id][:rate] = #inventories.map(&#my_price).inject(0, :+)
end
#sub_total = #transactions.params[:product_id][:rate].sum
However undefined method []= for nil:NilClass is generated when building the params for each product_id. This command is obviously mistaken. [The approach could also be inefficient/ineffective...]
I believe you don't really mean params[:product_id][:product_id], but rather params[product_id][:product_id], since :product_id is a symbol, not a variable.
When setting a new hash in the hash, you need to first define it as such:
params[product_id] = {}
params[product_id][:product_id] = product_id
# ...
This should resolve the undefined method []= for nil:NilClass. I guess you will encounter some more problems (like #inventories.map(&#my_price)) after you fix this one though...
With some guesswork, I guess a code which does what you want will look something like this:
#transactions = params[:product_ids].map do |product_id|
transaction = Transaction.new
transaction.product_id = product_id # sanity check
transaction.additional_info = session[:additional_info]
transaction.rate = Inventory.where(['product_id = ?', product_id]).sum(:my_price)
transaction
end
#sub_total = #transactions.map(&:rate).sum
And the view would be something like this:
<% #transactions.each do |transaction| %>
<%= transaction.product_id %>
[...]

Ruby - trying to make hashtags within string into links

I am trying to make the hashtags within a string into links.
e.g. I'd like a string that's currently: "I'm a string which contains a #hashtag" to transform into: "I'm a string which contains #hashtag"
The code that I have at the moment is as follows:
<% #messages.each do |message| %>
<% string = message.content %>
<% hashtaglinks = string.scan(/#(\d*)/).flatten %>
<% hashtaglinks.each do |tag| %>
<li><%= string = string.gsub(/##{tag}\b/, link_to("google", "##{tag}") %><li>
<% end %>
<% end %>
I've been trying (in vain) for several hours to get this to work, reading through many similar stackoverflow threads- but frustration has got the better of me, and as a beginner rubyist, I'd be really appreciate it if someone could please help me out!
The code in my 'server.rb' is as follows:
get '/' do
#messages = Message.all
erb :index
end
post '/messages' do
content = params["content"]
hashtags = params["content"].scan(/#\w+/).flatten.map{|hashtag|
Hashtag.first_or_create(:text => hashtag)}
Message.create(:content => content, :hashtags => hashtags)
redirect to('/')
end
get '/hashtags/:text' do
hashtag = Hashtag.first(:text => params[:text])
#messages = hashtag ? hashtag.messages : []
erb :index
end
helpers do
def link_to(url,text=url,opts={})
attributes = ""
opts.each { |key,value| attributes << key.to_s << "=\"" << value << "\" "}
"<a href=\"#{url}\" #{attributes}>#{text}</a>"
end
end
Here is the code to get you started. This should replace (in-place) the hashtags in the string with the links:
<% string.gsub!(/#\w+/) do |tag| %>
<% link_to("##{tag}", url_you_want_to_replace_hashtag_with) %>
<% end %>
You may need to use html_safe on the string to display it afterwards.
The regex doesn't account for more complex cases, like what do you do in case of ##tag0 or #tag1#tag2. Should tag0 and tag2 be considered hashtags? Also, you may want to change \w to something like [a-zA-Z0-9] if you want to limit the tags to alphanumerics and digits only.

JSON will not display

I have a Sinatra application that should get image URLs from a JSON file and put them into HTML <img> tags.
I can parse through the JSON just fine when I print it to the command line, but when I use ERB to place the data, it won't show.
I put it in <ul> tags and got only the bullet points for every image in the JSON file.
Here is my code:
app.rb:
get "/" do
file = open("./images.json")
json = file.read
#parsed = JSON.parse(json)
erb :roar
##parsed.each do |roar|
# p roar["url"]
#end
end
Roar.erb:
<ul>
<% #parsed.each do |shop| %>
<li> <%shop["url"] %> </li>
<% end %>
</ul>
Are you not just missing an "=" here :
<li> <%= shop["url"] %> </li>
Just some comments on the code in general:
Don't use:
file = open("./images.json")
json = file.read
#parsed = JSON.parse(json)
Instead, use:
json = File.open("./images.json") do |fi|
fi.read
end
#parsed = JSON.parse(json)
Or:
json = File.open("./images.json") { |fi| fi.read }
#parsed = JSON.parse(json)
Or:
json = File.read("./images.json")
#parsed = JSON.parse(json)
Or:
#parsed = JSON.parse(File.read("./images.json"))
The reasons are:
file = open("./images.json") opens the file but never closes it. That's not good form, and it's also not idiomatic Ruby. The first two replacements automatically close the opened file.
Using File.read returns the contents of the file just as opening it and reading in a separate step, only it's all in one step. The file is also automatically closed afterwards.

How to build nested menu "trees" in HAML

I am trying to build a simple nested html menu using HAML and am not sure how to go about inserting the elements with the correct indentation, or the general best way to build nested trees. I would like to be able to do something like this, but infinitely deep:
- categories.each_key do |category|
%li.cat-item{:id => "category-#{category}"}
%a{:href => "/category/#{category}", :title => "#{category.titleize}"}
= category.titleize
It feels like I should be able to accomplish this pretty easily without resorting to writing the tags by hand in html, but I'm not the best with recursion. Here is the code I've currently come up with:
View Helper
def menu_tag_builder(array, &block)
return "" if array.nil?
result = "<ul>\n"
array.each do |node|
result += "<li"
attributes = {}
if block_given?
text = yield(attributes, node)
else
text = node["title"]
end
attributes.each { |k,v| result += " #{k.to_s}='#{v.to_s}'"}
result += ">\n"
result += text
result += menu_tag_builder(node["children"], &block)
result += "</li>\n"
end
result += "</ul>"
result
end
def menu_tag(array, &block)
haml_concat(menu_tag_builder(array, &block))
end
View
# index.haml, where config(:menu) converts the yaml below
# to an array of objects, where object[:children] is a nested array
- menu_tag(config(:menu)) do |attributes, node|
- attributes[:class] = "one two"
- node["title"]
Sample YAML defining Menu
menu:
-
title: "Home"
path: "/home"
-
title: "About Us"
path: "/about"
children:
-
title: "Our Story"
path: "/about/our-story"
Any ideas how to do that so the output is like this:
<ul>
<li class='one two'>
Home
</li>
<li class='one two'>
About Us
</li>
</ul>
...not like this:
<ul>
<li class='one two'>
Home</li>
<li class='one two'>
About Us</li>
</ul>
... and so it's properly indented globally.
Thanks for the help,
Lance
The trick to nicely-indented, Ruby-generated Haml code is the haml_tag helper. Here's how I'd convert your menu_tag method to using haml_tag:
def menu_tag(array, &block)
return unless array
haml_tag :ul do
array.each do |node|
attributes = {}
if block_given?
text = yield(attributes, node)
else
text = node["title"]
end
haml_tag :li, text, attributes
menu_tag_builder(node["children"], &block)
end
end
end
How about something along the lines of:
def nested_list(list)
return unless list
haml_tag :ul do
list.each do |item|
haml_tag :li do
haml_concat link_to item["title"], item["path"]
if item["children"]
nested_list item["children"]
end
end
end
end
end
Awesome, #shingara's hint put me in the right direction :). This works perfectly:
def menu_tag(array, &block)
return "" if array.nil?
haml_tag :ui do
array.each do |node|
attributes = {}
if block_given?
text = yield(attributes, node)
else
text = node[:title]
end
haml_tag :li, attributes do
haml_concat text
menu_tag_builder(node[:children], &block)
end
end
end
end
If somebody can make that even shorter, or make it more easy to customize the attributes on the nested nodes, I'll mark that as correct instead of this.
Cheers.
It's because you send a pur HTML by your helper. The indentation become with HAML. You can can generate some HAML in your helper.

Resources