Joining an array of words / phrases with Ruby - ruby-on-rails

irb(main):001:0> a = ["global climate change", "calamity", "glaciers", "new york times"]
=> ["global climate change", "calamity", "glaciers", "new york times"]
irb(main):002:0> a.join(', ')
=> "global climate change, calamity, glaciers, new york times"
I need the result to be
=> "global climate change", "calamity", "glaciers", "new york times"
Ideas? A one-liner would be ideal.

The correct way to pass arrays of options to a query via ActiveRecord is just to use query parameters:
a = ["global climate change", "calamity", "glaciers", "new york times"]
Category.where("name IN (?)", a)
# Generated query:
# Category Load (0.4ms) SELECT `categories`.* FROM `categories` WHERE (name in ('global climate change','calamity','glaciers','new york times'))
You don't have to do anything special to transform it into a valid SQL fragment. You should specifically avoid formatting strings as SQL fragments, as without careful sanitization, you may open up SQL injection vulnerabilities in your application.

Using arrays in a query is very simple:
arr = ["global climate change", "calamity", "glaciers", "new york times"]
Category.where(:name => arr)
There is absolutely no need to generate some weird string ;)

array = ['bob', 'bill']
new_array_string = array.map { |name| "'#{name}'" }.join(',')
=> "'bob', 'bill'"
User.where("name IN (#{new_array_string})")

a = ["global climate change", "calamity", "glaciers", "new york times"]
=> ["global climate change", "calamity", "glaciers", "new york times"]
%Q!"#{a.join('", "')}"!
=> "\"global climate change\", \"calamity\", \"glaciers\", \"new york times\""
a.map{|e| "\"#{e}\""}.join(', ')
=> "\"global climate change\", \"calamity\", \"glaciers\", \"new york times\""

Related

How do I take a substring of a string with multiple quotes? Rails/Ruby

Given a string such as (Shift opened: \"he clams \"sick\" but not sure\") how would I extract just the part between the first set of quotes? I've tried combinations of split, slice and squeeze but always run into a case where it doesn't work. Thanks.
EDIT: The user inputs text, which can be in any form, so yes, someone could have an odd number of quotes. The text before the input is generated for record purposes. Some examples:
n = (Shift opened: \"he clams \"sick\" but not sure\")
n.split('"')[1] > "he claims "
If I could find the size of the array created by split I could do split('"')[1..size-1] but I'm not sure how to find that.
n = (Shift opened: \"\"sick\"\")
n.squeeze('"').split('"')[1] >> "sick"
That works fine.
This is more for error checking and making sure if people use quotes on input, it doesn't mess things up. And no I cannot edit how the string is generated. Hope I'm clear enough!
You can leverage the fact that regex is greedy by default, and use /"(.*)", which will capture all text between the first and last quotes:
n = 'Shift opened: "he clams "sick" but not sure" some more text'
n[/"(.*)"/, 1]
# => "he clams "sick" but not sure"
n = "Shift opened: \"\"sick\"\""
n[/"(.*)"/, 1]
# => ""sick""
I'm not sure if you want to extract the text on quotes recursively and get something like this:
=> "he clams "sick" but not sure"
=> "sick"
or "lorem ipsum "xxxxx yyyy "alpha beta" zzzz wwww" dol"
=> "lorem ipsum "xxxxx yyyy "alpha beta" zzzz wwww" dol"
=> "xxxxx yyyy "alpha beta" zzzz wwww"
=> "alpha beta"
perhaps you will need a simple CFG:
S -> aS | a
a = /\".*\"/
or iterate the string stacking substrings on each quote

SAX parsing a bunch of dead presidents with Nokogiri HTML parser?

I would like to parse USA presidents on the "List of Presidents of the United States" wiki page.
I can do this with a bunch of XPath and loops. But SAx parsing is so fast and I would like to learn how to implement that.
The Nokogiri document gave me an HTML SAX parsing example:
class MyDoc < Nokogiri::XML::SAX::Document
def start_element name, attributes = []
puts "found a #{name}"
end
end
parser = Nokogiri::HTML::SAX::Parser.new(MyDoc.new)
parser.parse(File.read(ARGV[0], 'rb'))
But which methods do I use to define all the HTML elements and their content that I want to grab?
With SAX, you have to define callback methods in your parser for each 'event'. You have to keep track of state yourself. It is very crude. For example, to get president names from the page, you can do this:
class MyDoc < Nokogiri::XML::SAX::Document
def start_element name, attributes = []
if name == "li"
#inside_li = true
end
end
def characters(chars)
if #inside_li
puts "found an <li> containing the string '#{chars}'"
end
end
def end_element name
if name == "li"
puts "ending #{name}"
#inside_li = false
end
end
end
The above can be thought of as the rough equivalent of the statement:
doc.xpath('//li').map(&:text)
Which starts with the following output:
ending li
found an <li> containing the string 'Grover Cleveland'
ending li
found an <li> containing the string 'William McKinley'
ending li
found an <li> containing the string 'Theodore Roosevelt'
So far so good, However, it also outputs a lot of cruft, ending with:
found an <li> containing the string 'Disclaimers'
ending li
found an <li> containing the string 'Mobile view'
ending li
found an <li> containing the string '
'
found an <li> containing the string '
'
ending li
found an <li> containing the string '
'
found an <li> containing the string '
'
ending li
So to make this more precise and not get the li elements you don't care about, you'd have to keep track of which container elements you are in by adding more if clauses to start_element, characters, etc. And if you have nested elements of the same name, you'll have to keep track of counters yourself, or implement a stack to push and pop the elements you see. It gets VERY messy very fast.
SAX is best for filters where you don't care about the DOM, you're just doing some basic transformations.
Instead, consider using a single XPath statement, such as
doc.xpath("//table[contains(.//div, 'Presidents of the United States')]//ol/li").map(&:text)
This says "Find the table which contains a div with the words 'Presidents of the United States' and return the text from all the ordered list items within it". This can be done in SAX, but it would be a lot of messy code.
Output of the above XPath:
["George Washington", "John Adams", "Thomas Jefferson", "James Madison", "James Monroe", "John Quincy Adams", "Andrew Jackson", "Martin Van Buren", "William Henry Harrison", "John Tyler", "James K. Polk", "Zachary Taylor", "Millard Fillmore", "Franklin Pierce", "James Buchanan", "Abraham Lincoln", "Andrew Johnson", "Ulysses S. Grant", "Rutherford B. Hayes", "James A. Garfield", "Chester A. Arthur", "Grover Cleveland", "Benjamin Harrison", "Grover Cleveland", "William McKinley", "Theodore Roosevelt", "William Howard Taft", "Woodrow Wilson", "Warren G. Harding", "Calvin Coolidge", "Herbert Hoover", "Franklin D. Roosevelt", "Harry S. Truman", "Dwight D. Eisenhower", "John F. Kennedy", "Lyndon B. Johnson", "Richard Nixon", "Gerald Ford", "Jimmy Carter", "Ronald Reagan", "George H. W. Bush", "Bill Clinton", "George W. Bush", "Barack Obama"]

ROR grouped_options_for_select dynamic populate

I have this code
in view
grouped_options_for_select(#grouped_options, nil, #options_1)
in controller
#options_1 = [africa]
#grouped_options = [['North America',[['United States','US'],'Canada']],
['Europe',['Denmark','Germany','France']]]
how to make the countries populate dynamically from an array an not type them one by one?
for those who might be interested...
this what i did
in view
options_for_select(#options, :disabled => ['North America', 'Europe']
in controller
#countries.each do |l|
#options << [l.name, l.id]
end
for this output
Africa
North America
United States, US
Canada
Europe
Denmark
Germany
France

Mongoid fields issue

I am working on dynamic form generator. And I've noticed strange behaviour
class Model
include Mongoid::Document
field :name, :type => String
end
model = Model.new
model.name = "My Name"
model.surname = "My Surname"
#=> NoMethodError: undefined method `surname='
but
model = Model.new( :name => "My Name", :surname => "My Surname" )
#=> ok
model.surname
#=> "My Surname"
model.surname = "New Surname"
#=> "New Surname"
Can somebody explain why I can create new fields with mass assignment and can't add fields through attribute?
Per the Mongoid documentation, the getter/setter methods (e.g. .surname) will only work if the field exists in the document (which is why when you create a new Model with the field, it works).
You can still set/read the fields like so:
model[:surname]
model.read_attribute(:surname)
model[:surname] = "My Surname"
model.write_attribute(:surname, "My Surname")
See http://mongoid.org/docs/documents/dynamic.html

What is the best way to test export to excel or csv using rspec?

I'm using ruby CSV library to export reporting data from Ruby On Rails application.
And i am wondering what would be the best way to test such operation?
Thanks in advance
Hi I was using Ruby 1.9.2 which has Faster CSV, but the concept should be similar. I actually have my factories in a helper method, but put it all together to make it easier to see here. I'd also love to know if there is a better way, but this worked for me.
I generated the report, and then opened it to compare it with my expected results. I wanted to make sure the file existed. You could do something similar without actually saving the file.
describe "Rsvp Class" do
created = "#{Time.now.to_formatted_s(:db)} -0800"
before(:each) do
[{:first_name => "fred", :last_name => "flintstone", :state => "CA", :zip => "12345", :created_at => created},
{:first_name => "barny", :last_name => "rubble", :state => "CA", :zip => "12345", :created_at => created}].each do |person|
rsvp = Factory.build(:rsvp)
rsvp.first_name = person[:first_name]
rsvp.last_name = person[:last_name]
rsvp.state = person[:state]
rsvp.zip = person[:zip]
rsvp.created_at = person[:created_at]
rsvp.save
end
end
it "should generate a report csv file" do
response = "first name, last name, email, address, address line 1, city, state, zip, variation code, tracking code, created at\nfred, flintstone, fred#example.com, , , , CA, 12345, , , #{created}\nbarny, rubble, fred#example.com, , , , CA, 12345, , , #{created}\n"
Rsvp.generate_report
string = ""
CSV.foreach("#{::Rails.root.to_s}/reports/rsvp_report.csv", :quote_char => '"', :col_sep =>',', :row_sep =>:auto) do |row|
string << "#{row.join(", ")}\n"
end
string.should == response
end
end

Resources