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.
Related
How do I display data from a csv file into a Sinatra-App?
Sinatra:
csv = CSV.read(data.csv)
csv.each do |entry|
#output = "#{entry[1]},#{entry[4]}:#{entry[0]}"
end
Erb:
<%= #data %>
Which displays only the last row of the csv file.
Problem
With this code :
csv = CSV.read(data.csv)
csv.each do |entry|
#output = "#{entry[1]},#{entry[4]}:#{entry[0]}"
end
You iterate over all csv rows.
For each row, the block defines the #output variable.
After the first csv line, the code just keeps on overriding the #output_variable.
Possible solution
You need map, not each.
csv = CSV.read(data.csv)
#data = csv.map do |entry|
"#{entry[1]},#{entry[4]}:#{entry[0]}"
end
You can then use
<%= #data %>
in your views. #data is now an array of strings, with one string for each csv row.
You probably want to move the for loop to the template file.
In sinatra
#csv_data = CSV.read(data.csv)
In erb
<% #csv_data.each do |entry| %>
<%= entry[1] %>,<%= entry[4] %>:<%= entry[0] %>
<% end %>
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
My script edits an XML file using:
stringComplete = File.read("TheUpload.xml",encoding: "UTF-8").gsub(my mystery gsub)
and regenerates using:
newfile = File.open("EditXML.xml", "w")
newfile.puts "method(xyz)"
How do I correct my script so that it doesn't matter what file name has the ".xml" that is
uploaded and that every single user gets his own edited .xml file and not by someone else?
When uploading a file, you don't need to save it to disk on the server - simply read the upload IO (see here):
view.erb:
<%= form_tag({action: :upload}, multipart: true) do %>
<%= file_field_tag 'xml' %>
<% end %>
<%= form_for #upload do |f| %>
<%= f.file_field :xml %>
<% end %>
controller:
uploaded_io = params[:upload][:xml]
string_complete = uploaded_io.read.gsub(whatever)
For downloading, you also don't need to save to disk - simply serve the new file contents (see here)
send_data string_complete, filename: 'EditXML.xml'
If the files were uploaded to a directory in some other way, you can scan the input directory using Dir.glob, and then iterate over each, creating a new file in the output file with the name based on the input file's name:
Dir.glob(File.join(input_directory, '*.xml')).each do |input_file|
stringComplete = File.read(input_file,encoding: "UTF-8").gsub(my mystery gsub)
input_file_basename = File.basename(input_file)
newfile = File.open(File.join(output_directory, input_file_basename + '_output.xml', "w")
newfile.puts "method(xyz)"
end
How can I display the output in html? Here is my sample code in ruby:
m = ["a","b", "c"]
m.each do |i|
#html = "<p>"+#{i}+"</p>"
end
I have a separate view file which reads the #html string:
<%= #html.html_safe</p>
My question is, how do i invoke the view file?
First fix typo in your code
#html = "<p>"+#{i}+"</p>"
should read
#html = "<p>#{i}</p>"
Now coming to the point just pass this at the end of your action
erb :<name-of-your-template>
this assumes you are having your erb templates under views directory
I am trying to figure out a way to count a words in a particular string that contains html.
Example String:
<p>Hello World</p>
Is there a way in Ruby to count the words in between the p tags? Or any tag for that matter?
Examples:
<p>Hello World</p>
<h2>Hello World</h2>
<li>Hello World</li>
Thanks in advance!
Edit (here is my working code)
Controller:
class DashboardController < ApplicationController
def index
#pages = Page.find(:all)
#word_count = []
end
end
View:
<% #pages.each do |page| %>
<% page.current_state.elements.each do |el| %>
<% #count = Hpricot(el.description).inner_text.split.uniq.size %>
<% #word_count << #count %>
<% end %>
<li><strong>Page Name: <%= page.slug %> (Word Count: <%= #word_count.inject(0){|sum,n| sum+n } %>)</strong></li>
<% end %>
Here's how you can do it:
require 'hpricot'
content = "<p>Hello World...."
doc = Hpricot(content)
doc.inner_text.split.uniq
Will give you:
[
[0] "Hello",
[1] "World"
]
(sidenote: the output is formatted with awesome_print that I warmly recommend)
Sure
Use Nokogiri to parse the HTML/XML and XPath to find the element and its text value.
Split on whitespace to count the words
You'll want to use something like Hpricot to remove the HTML, then it's just a case of counting words in plain text.
Here is an example of stripping the HTML: http://underpantsgnome.com/2007/01/20/hpricot-scrub/
First start with something able to parse HTML like Hpricot, then use simple regular expression to do what you want (you can merely split over spaces and then count for example)