I have a requirement to read a third-party public website. Currently I am able to do this via Nokogiri, but I'm having trouble writing some unit tests using rspec.
I have this HTML <div>:
<div class="user">
<div class="name">User Name</div>
</div>
In my Reader model I have the method name, which reads the name from the HTML:
class SampleReader
def name(html_div)
html_div.css('name')
end
end
In my RSpec test case I pass the above HTML <div> as a string to the name method and I get the following error:
undefined method `css' for #<String:0x007fd8a0b39c98>
I believe it's because Nokogiri cannot identified the string as HTML, so I would appreciate if someone can help me write the test case. And, if possible, my preferred option is to pass only the <div> string, not the entire HTML page source, to the method.
I'm using Rails 3.2.9
Rspec2
Nokogiri
You'll need to wrap the HTML string as a Nokogiri document:
require 'nokogiri'
str = <<-HTML
<div class="user">
<div class="name">User Name</div>
</div>
HTML
class SampleReader
def name(html_div)
doc = Nokogiri::HTML(html_div)
doc.css('.name').text
end
end
reader = SampleReader.new
puts reader.name(str) #=> "User Name"
Also, don't forget to upgrade your application to rails 3.2.11.
Related
I am trying to use .html_safe in the below description where I receive the error as Undefined method for Nokogiri HTML document.
blogs_controller.rb
#blog = Blog.find(19)
#description = Nokogiri::HTML.parse(#blog.description)
#description.search('a.fr-file').each do |desc|
desc['href']= File.join(ActionController::Base.asset_host, desc['href'])
end
show.html.erb
<p><%= #description.html_safe %></p>
Kindly advise.
html_safe is a Rails method defined on String but not on Nokogiri::HTML.
I would try to translate the Nokogiri document into a HTML first:
<p><%= #description.to_html.html_safe %></p>
I was demoing an app for a class I was teaching and ran into a bug. I was demo'ing POST requests in a Sinatra application. Here's my routes:
require "bundler/setup"
require "sinatra"
require "sinatra/reloader"
first_names = []
get '/' do
#first_names = first_names
erb :index
end
post '/add_name' do
first_names << params[:first_name]
redirect "/"
end
In the index.erb:
<h1>All Names</h1>
<% #first_names.each do |name| %>
<div><%= name %></div>
<% end %>
<h2>Enter New Name Here and Hit Enter</h2>
<form action="add_name" method="post">
<input name='first_name'>
<input type="submit" value="Add First Name"
</form>
Gemfile:
source 'https://rubygems.org'
gem "sinatra"
gem "sinatra-contrib"
Notice the unclosed input tag in the form. THIS actually works. Which is fine, HTML is buggy? But the part, that trips me up, is if I change the action of the form to addd_name(mispelled). It doesn't give me the error I expect. "Sinatra doesn't know this diddy...." It just does nothing. Any thoughts?
If you close the input tag and try to misspell action as '/addd_name' you'll get that ditty error :)
It that case, browser do not handle your "form" as a form. Check your network tab from developer console. You'll see that nothing happens when you click submit button.
Btw, if you've not declared (e.g. in configure do ... end block) first_names array, you'll get another error - nilClass or sth like that.
I'm trying to build an e-commerce site using Sinatra, as practice. I'm getting stumped on how to implement the 'Add to Cart' Button. My thought process about it is:
User clicks 'add to cart'
The button 'add to cart' invokes a ruby method, for example clicking on the following button
<input class='btn btn-primary' type='button' value='Add To Cart'></input>
should call a ruby method like
shop.add_to_cart(product, quantity)
An example of what this method might looking like:
class Shop
attr_reader :cart
def initialize
#cart = []
end
def add_to_cart(product, quantity)
#cart << product, quantity
end
end
In Rails, I think we use the helper_method in the controller? Is there anything similar I can do in Sinatra?
Thanks!
Note:
This is if you want to do it in ruby. You could probably also do it in javascript as mentioned in the other answer, but I cannot help you with that because I don't know javascript well enough.
To run the ruby method on button click you first need to create a <form> with only the button, then have that run a route in your app file that will run the method then redirect back to the page you were on. Here is my code (have not tested):
home.erb:
<form method="post" action="/runMethod">
<input type="hidden" name="product" value="whatever">
<input type="hidden" name="quantity" value="whatever">
<input class='btn btn-primary' type='submit' value='Add To Cart'>
</form>
You would set the values of the two hidden inputs (where I wrote "whatever") to the quantity and product according to their names.
App File:
class Shop
attr_reader :cart
def initialize
#cart = []
end
def add_to_cart(product, quantity)
#cart << product, quantity
end
end
get '/' do
erb :home
end
post '/runMethod' do
shop.add_to_cart(params[:product], params[:quantity])
redirect '/'
end
This can also be accomplished with ajax so that you dont have to leave the page:
$("#hideCarousel").submit(function() {
//posts the contents of the form to /action using ajax
$.post("/action", $("#myform").serialize(), function(result){
// assuming result is a string of the updated data in html
// and assuming that your data goes in an element with the id data-table
$("#data-table").html(result)
});
return false; // prevents the form from submitting normally
});
Rails/Sinatra run on the server side. If you want stuff happening in Rails directly you probably need a form and post back data.
nowadays people use javascript and it's javascript that makes the callbacks in an asynchronous fashion for this kinds of things.
I need to call method download_images("\folder","http:\url") which save pictures from url in choosen directory.This method should be called in index.html.erb and grab folder address from textbox1 and url from textbox2 after pressing the button1.
Right now I don't know how to grab strings from textboxes, I am trying to call method correctlyThe index.html.erb code:
<h1>Welcome#index</h1>
<p><%= "Download pictures from url!" %></p>
<div class="form-horizontal">
<p> Input url: </p>
<p> <input type="text"/> </p>
<p> Input destination folder: </p>
<p> <input type="text"/> </p>
<button class="btn">Go!</button>
<% button_to "btn", :method=> download_images("`/tutorial1/downloadedpics","http://www.yandex.ru/") %>
</div>
I defined method download_images in welcome_controller.rb:
class WelcomeController < ApplicationController
def index
end
def download_images(url, destination_path, options = {})
base_url = URI.join(url, "/").to_s
body = Typhoeus::Request.get(url).body
imgs = Nokogiri::HTML(body).css("img")
image_srcs = imgs.map { |img| img["src"] }.compact.uniq
hydra = Typhoeus::Hydra.new(:max_concurrency => options[:max_concurrency] || 50)
image_paths = image_srcs.map do |image_src|
image_url = URI.join(base_url, image_src).to_s
path = File.join(destination_path, File.basename(image_url))
request = Typhoeus::Request.new(image_url)
request.on_complete { |response| File.write(path, response.body) }
hydra.queue(request)
path
end
hydra.run
image_paths
end
end
After I switch server and go to localhost, I receive an exception:
NoMethodError in Welcome#index, undefined method download_images' for #<#<Class:0x007f202fc3ae50>:0x007f202f9ab518>, in line <% button_to "btn", :method=> download_images("/tutorial1/downloadedpics","http://www.yandex.ru/") %>
I am a noob programmer, so I can do rather dumb mistakes...
And it is important to mention: I work in Nitrous web box, and don't really know is it possible to download images in box folder:
~/tutorial1/downloadedpics
Also I use Bootstrap controllers,Nokogiri gem and Typhoeus gem.
Ruby version:ruby 2.1.1p76
Rails version:Rails 4.1.0
Thank you for your attention.
As a FYI, doing:
imgs = Nokogiri::HTML(body).css("img")
image_srcs = imgs.map { |img| img["src"] }.compact.uniq
is not the right way to find images with "src" parameters. Because you're not searching correctly, you get nils in your resulting array, forcing you to use compact. Instead, don't rely on cleaning up after making a mess, just avoid making the mess in the first place:
require 'nokogiri'
body = <<EOT
<html>
<body>
<img>
<img src="foo">
</body>
</html>
EOT
imgs = Nokogiri::HTML(body).css("img[#src]")
image_srcs = imgs.map { |img| img["src"] }
image_srcs # => ["foo"]
I'm having an issue of selecting the content I want to store as a string, as the data has the same div class name and there is no set ID to use instead.
I'm aware of the first and last operator in ruby, but is there anyway to select the options in between?
So for example
<html>
<body>
<div class="example">1111</div>
<div class="example">2222</div>
<div class="example">3333</div>
<div class="example">4444</div>
<div class="example">5555</div>
</body>
</html>
How can I get ruby to select the 4th class of the same class name, so I can store 4444 as my string?
Another way (using Watir API):
browser.div(:class => "example", :index => 3)
browser.divs(:class => "example")[3]