Outputting HTML in Ruby Not working with html_safe - html

I have a loop which outputs some information I grabbed from a website. To make the information display in an readable fashion, I added a <br> to the string. However when it runs, it displays <br>, escaping the html. I've used html_safe and raw but neither works. What's wrong with the code? The code is called in the view home (don't worry I'll move it once the code works).
<%=
require 'rubygems'
require 'nokogiri'
require 'open-uri'
time = Time.new
month = I18n.t("date.abbr_month_names")[time.month]
day = time.day
"#{month} #{day}"
#United States
cities = [
"sfbay", "losangeles", "athensga", "phoenix", "santabarbara", "denver",
"panamacity", "miami", "austin", "bakersfield", "keys", "newyork"
]
cities.map do |city|
#Search Terms
search_terms = ["mechanic", "car", "tech"]
search_terms.map do |term|
escaped_term = CGI.escape(term)
url = "http://#{city}.craigslist.org/search/jjj?query=#{escaped_term}&catAbb=jjj&
srchType=A"
doc = Nokogiri::HTML(open(url))
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
#strings << "#{date} #{text} #{link}"
end
end
end
end
%>

Hmm not sure at all but try the following:
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
#string = "#{date} #{text} #{link}<br />"
end
end.html_safe
Another way, not solving yours but displaying with br at each line:
# in controller or whatever
#strings = []
if date = "#{month} #{day}"
#strings << "#{date} #{text} #{link}"
end
# in the view:
<%= raw(#strings.join('<br />')) %>

doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
"<br/>".html_safe + "#{date} #{text} #{link}"
end
end

Related

Prevent jekyll from cleaning up generated JSON file?

I've written a simple plugin that generates a small JSON file
module Jekyll
require 'pathname'
require 'json'
class SearchFileGenerator < Generator
safe true
def generate(site)
output = [{"title" => "Test"}]
path = Pathname.new(site.dest) + "search.json"
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'w') do |f|
f.write("---\nlayout: null\n---\n")
f.write(output.to_json)
end
# 1/0
end
end
end
But the generated JSON file gets deleted every time Jekyll runs to completion. If I uncomment the division by zero line and cause it to error out, I can see that the search.json file is being generated, but it's getting subsequently deleted. How do I prevent this?
I found the following issue, which suggested adding the file to keep_files: https://github.com/jekyll/jekyll/issues/5162 which worked:
The new code seems to avoid search.json from getting deleted:
module Jekyll
require 'pathname'
require 'json'
class SearchFileGenerator < Generator
safe true
def generate(site)
output = [{"title" => "Test"}]
path = Pathname.new(site.dest) + "search.json"
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'w') do |f|
f.write("---\nlayout: null\n---\n")
f.write(output.to_json)
end
site.keep_files << "search.json"
end
end
end
Add your new page to site.pages :
module Jekyll
class SearchFileGenerator < Generator
def generate(site)
#site = site
search = PageWithoutAFile.new(#site, site.source, "/", "search.json")
search.data["layout"] = nil
search.content = [{"title" => "Test 32"}].to_json
#site.pages << search
end
end
end
Inspired by jekyll-feed code.

Trying to serialize with JSON a save method in my Ruby Hangman game

I'm trying to get my save_game() method to work, nothing is getting written to the JSON file. This is my first assignment working with JSON and serialization in general. I'm not quite sure where I'm even going wrong.
These are my serialization methods:
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
This is my program, on line 92 I try to call my save_game method.
require 'json'
load 'display.rb'
class Hangman
attr_accessor :name
#name = name
def initialize
puts "What is your name?"
#name = gets.chomp
puts "
################################################
HANGMAN
################################################
_________
|
| |
| O
| /|\\
| |
| / \\
|
-----------------
Welcome #{#name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
"
end
end
class Gameplay
attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
def initialize
#array = []
#filestuff = File.foreach('5text.txt') do |x|
chomped = x.chomp
#array << chomped if (chomped.length >= 5 and chomped.length <= 12)
end
#random_word = #array.sample
#cipher = #random_word.gsub(/[a-z]/, '*').split(//)
#random_word2 = #random_word.split(//)
#counter = 5
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
def choice(n)
#random_word2.each_with_index do |i,index|
if i == n
#cipher[index] = i
end
end
if n == #random_word2.join.to_s
puts "You win"
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
Hangman.new
else exit
end
end
if #random_word2.include?(n) == false
#counter -= 1
display
puts "#{#counter} guesses remaining."
puts "To save press 1"
save = gets.chomp
if save == "1"
#Will not save
save_game($b.to_json)
end
end
if #counter == 0
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
else exit
end
end
puts #cipher.join
end
#counter = 5
while #counter > 0
choice(gets.chomp)
end
end
end
Hangman.new
$b = Gameplay.new
You need to close the file in order to make sure your output is actually written to the disk ("flushed"). You can manually, call close:
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
or, you can use File.open, which takes a block and closes the file when the block ends:
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
Since, writing to the disk is a slow operation, Ruby (and all languages that I can think of right now) will hold off on actually writing the file until it has accumulated a certain amount of text in a buffer. Once it has reached this limit, it will flush the buffer and write everything in it to disk. In order to make sure all your text is actually written when trying to write a file, you need to call close on the file, and as part of closing it, Ruby will flush whatever is left in its buffer.
There are other ways of making sure your content is flushed but when you're just starting to learn about this stuff, it should suffice to just make sure to always close files when you're done reading or writing them.

Printing out python output to an html file

I am writing a script to print the output to an html file. I am stuck on the format of my output. Below is my code:
def printTohtml(Alist):
myfile = open('zip_files.html', 'w')
html = """<html>
<head></head>
<body><p></p>{htmlText}</body>
</html>"""
title = "Study - User - zip file - Last date modified"
myfile.write(html.format(htmlText = title))
for newL in Alist:
for j in newL:
if j == newL[-1]:
myfile.write(html.format(htmlText=j))
else:
message = j + ', '
myfile.write(html.format(htmlText = message))
myfile.close()
Alist = [['123', 'user1', 'New Compressed (zipped) Folder.zip', '05-24-17'],
['123', 'user2', 'Iam.zip', '05-19-17'], ['abcd', 'Letsee.zip', '05-22-17'],
['Here', 'whichTwo.zip', '06-01-17']]
printTohtml(Alist)
I want my output to be like this:
Study - User - zip file - Last date modified
123, user1, New Compressed (zipped) Folder.zip, 05-24-17
123, user2, Iam.zip, 05-19-17
abcd, Letsee.zip, 05-22-17
Here, whichTwo.zip, 06-01-17
But my code is giving me everything on its own line. Can anyone please help me?
Thanks in advance for your help!
My Output:
Study - User - zip file - Last date modified
123,
user1,
New Compressed (zipped) Folder.zip,
05-24-17
123,
user2,
Iam.zip,
05-19-17
abcd,
Letsee.zip,
05-22-17
Here,
whichTwo.zip,
06-01-17
You might want to try something like that. I haven't tested but this will create the string first and then write it to the file. Might be faster for avoiding multiple writes but I am not sure how python is handling that on the background.
def printTohtml(Alist):
myfile = open('zip_files.html', 'w')
html = """<html>
<head></head>
<body><p></p>{htmlText}</body>
</html>"""
title = "Study - User - zip file - Last date modified"
Alist = [title] + [", ".join(line) for line in Alist]
myfile.write(html.format(htmlText = "\n".join(Alist)))
myfile.close()
Alist = [['123', 'user1', 'New Compressed (zipped) Folder.zip', '05-24-17'],
['123', 'user2', 'Iam.zip', '05-19-17'], ['abcd', 'Letsee.zip', '05-22-17'],
['Here', 'whichTwo.zip', '06-01-17']]
printTohtml(Alist)
Your issue is that you're including the html, body and paragraph tags every time you write a line to your file.
Why don't you concatenate your string, separating the lines with <br> tags, and then load them into your file, like so:
def printTohtml(Alist):
myfile = open('zip_files.html', 'w')
html = """<html>
<head></head>
<body><p>{htmlText}</p></body>
</html>"""
complete_string = "Study - User - zip file - Last date modified"
for newL in Alist:
for j in newL:
if j == newL[-1]:
complete_string += j + "<br>"
else:
message = j + ', '
complete_string += message + "<br>"
myfile.write(html.format(htmlText = complete_string))
myfile.close()
Also, your template placeholder is in the wrong spot, it should be between your paragraph tags.

rails ,paginate, sort and more than one table

I'm new to rails, I want to know how to paginate and sort by a particular column.
I can do this within one table, but when there is more than table, and the sorted column not in the main table.
I have tried many times but failed, and I also searched the web, but also in vain. Now I need your help...help please!
while paginate i'm using this:
# encoding: utf-8
module Pagination
class Paginator
attr_reader :item_count, :per_page, :page, :page_param
def initialize(*args)
if args.first.is_a?(ActionController::Base)
args.shift
ActiveSupport::Deprecation.warn "Paginator no longer takes a controller instance as the first argument. Remove it from #new arguments."
end
item_count, per_page, page, page_param = *args
#item_count = item_count
#per_page = per_page
page = (page || 1).to_i
if page < 1
page = 1
end
#page = page
#page_param = page_param || :page
end
def offset
(page - 1) * per_page
end
def first_page
if item_count > 0
1
end
end
def previous_page
if page > 1
page - 1
end
end
def next_page
if last_item < item_count
page + 1
end
end
def last_page
if item_count > 0
(item_count - 1) / per_page + 1
end
end
def first_item
item_count == 0 ? 0 : (offset + 1)
end
def last_item
l = first_item + per_page - 1
l > item_count ? item_count : l
end
def linked_pages
pages = []
if item_count > 0
pages += [first_page, page, last_page]
pages += ((page-2)..(page+2)).to_a.select {|p| p > first_page && p < last_page}
end
pages = pages.compact.uniq.sort
if pages.size > 1
pages
else
[]
end
end
def items_per_page
ActiveSupport::Deprecation.warn "Paginator#items_per_page will be removed. Use #per_page instead."
per_page
end
def current
ActiveSupport::Deprecation.warn "Paginator#current will be removed. Use .offset instead of .current.offset."
self
end
end
# Paginates the given scope or model. Returns a Paginator instance and
# the collection of objects for the current page.
#
# Options:
# :parameter name of the page parameter
#
# Examples:
# #user_pages, #users = paginate User.where(:status => 1)
#
def paginate(scope, options={})
options = options.dup
finder_options = options.extract!(
:conditions,
:order,
:joins,
:include,
:select
)
if scope.is_a?(Symbol) || finder_options.values.compact.any?
return deprecated_paginate(scope, finder_options, options)
end
paginator = paginator(scope.count, options)
collection = scope.limit(paginator.per_page).offset(paginator.offset).to_a
return paginator, collection
end
def deprecated_paginate(arg, finder_options, options={})
ActiveSupport::Deprecation.warn "#paginate with a Symbol and/or find options is depreceted and will be removed. Use a scope instead."
klass = arg.is_a?(Symbol) ? arg.to_s.classify.constantize : arg
scope = klass.scoped(finder_options)
paginate(scope, options)
end
def paginator(item_count, options={})
options.assert_valid_keys :parameter, :per_page
page_param = options[:parameter] || :page
page = (params[page_param] || 1).to_i
per_page = options[:per_page] || per_page_option
Paginator.new(item_count, per_page, page, page_param)
end
module Helper
include Redmine::I18n
# Renders the pagination links for the given paginator.
#
# Options:
# :per_page_links if set to false, the "Per page" links are not rendered
#
def pagination_links_full(*args)
pagination_links_each(*args) do |text, parameters, options|
if block_given?
yield text, parameters, options
else
link_to text, params.merge(parameters), options
end
end
end
# Yields the given block with the text and parameters
# for each pagination link and returns a string that represents the links
def pagination_links_each(paginator, count=nil, options={}, &block)
options.assert_valid_keys :per_page_links
per_page_links = options.delete(:per_page_links)
per_page_links = false if count.nil?
page_param = paginator.page_param
html = ''
if paginator.previous_page
# \xc2\xab(utf-8) = «
text = "\xc2\xab " + l(:label_previous)
html << yield(text, {page_param => paginator.previous_page}, :class => 'previous') + ' '
end
previous = nil
paginator.linked_pages.each do |page|
if previous && previous != page - 1
html << content_tag('span', '...', :class => 'spacer') + ' '
end
if page == paginator.page
html << content_tag('span', page.to_s, :class => 'current page')
else
html << yield(page.to_s, {page_param => page}, :class => 'page')
end
html << ' '
previous = page
end
if paginator.next_page
# \xc2\xbb(utf-8) = »
text = l(:label_next) + " \xc2\xbb"
html << yield(text, {page_param => paginator.next_page}, :class => 'next') + ' '
end
html << content_tag('span', "(#{paginator.first_item}-#{paginator.last_item}/#{paginator.item_count})", :class => 'items') + ' '
if per_page_links != false && links = per_page_links(paginator, &block)
html << content_tag('span', links.to_s, :class => 'per-page')
end
html.html_safe
end
# Renders the "Per page" links.
def per_page_links(paginator, &block)
values = per_page_options(paginator.per_page, paginator.item_count)
if values.any?
links = values.collect do |n|
if n == paginator.per_page
content_tag('span', n.to_s)
else
yield(n, :per_page => n, paginator.page_param => nil)
end
end
l(:label_display_per_page, links.join(', ')).html_safe
end
end
def per_page_options(selected=nil, item_count=nil)
options = Setting.per_page_options_array
if item_count && options.any?
if item_count > options.first
max = options.detect {|value| value >= item_count} || item_count
else
max = item_count
end
options = options.select {|value| value <= max || value == selected}
end
if options.empty? || (options.size == 1 && options.first == selected)
[]
else
options
end
end
end
end
end
I think the Kaminari gem is a little bit better now than will_paginate. Take a look at that one too.
Haven't you tried 'will_paginate' gem? It seems clearer for me than whatever in your code, and it's well-documented.
Here it is, take a look: https://github.com/mislav/will_paginate
If you want to paginate elements by quantity, like "100 posts per page", will_paginate is the best solution, I think.
But the problem appears, if you try to paginate elements by date or so on, when you can't predict, how many elements will be on the page. In this case, I'm using this solution will_paginate can it order by day. Works well for me.

Counting tag and text characters in an HTML document in Ruby

I need to parse an HTML document to count the number of characters for both tags (including attributes) and text in Ruby. For performance reasons, I don't want to use a DOM parser. I've looked at Nokogiri's SAX and Reader parsers and also to SaxMachine, but neither seems to offer me a way to track the parser's position in the input HTML.
Does anyone know a way to access this information in Ruby? Thanks in advance
Input string
html = <<-HTML
<html>
<head>
<title>Title</title>
</head>
<body>
Hello world!
</body>
</html>
HTML
Dumb solution
Crude solution, it counts every alphabet character (ie. </html> count for 4 characters).
tag_count = 0
text_count = 0
in_tag = false
html.each_char do |char|
case char
when '<'
in_tag = true
when '>'
in_tag = false
when /\w/
in_tag ? tag_count += 1 : text_count += 1
end
end
puts "Text char count: #{text_count}"
puts "Tag char count: #{tag_count}"
Nokogiri SAX solution
This one could be easily translated to another language (eg. Java).
require 'nokogiri'
class HtmlCounter < Nokogiri::XML::SAX::Document
attr_accessor :tag_count, :text_count, :comment_count
def initialize(filtered_tags = [])
#filtered_tags = filtered_tags
end
def start_document
#tag_count = Hash.new(0)
#text_count = Hash.new(0)
#comment_count = 0
#current_tags = []
end
def start_element(name, attrs)
# Keep track of the nesting
#current_tags.push(name)
if should_count?
# Count the end element as well
count_tag(name.length * 2)
count_tag(attrs.flatten.map(&:length).inject(0) {|sum, length| sum + length})
end
end
def end_element(name)
#current_tags.pop
end
def comment(string)
count_comment(string.length) if should_count?
end
def characters(string)
count_text(string.strip.length) if should_count?
end
def should_count?
# Are we in a filtered tag ?
(#current_tags & #filtered_tags).empty?
end
def count_text(count)
#text_count[#current_tags.last] += count
end
def count_tag(count)
#tag_count[#current_tags.last] += count
end
def count_comment(count)
#comment_count[#current_tags.last] += count
end
end
# Don't count things in title tags
counter = HtmlCounter.new(["title"])
parser = Nokogiri::HTML::SAX::Parser.new(counter)
parser.parse(html)
puts "Text char count: #{counter.text_count}"
puts "Tag char count: #{counter.tag_count}"
output :
Text char count: {"body"=>12}
Tag char count: {"html"=>8, "head"=>8, "body"=>8}
Hope this helps.