Formatting a cell as Text using the axlsx spreadsheet ruby gem? - ruby

I'm using the axlsx ruby gem to create Excel-compatible .xlsx files. I can't figure out how to override the cell type that is generated by it's automatic type detection. For Active Record model attributes of type string the gem is setting the Excel cell format to General, but I want it to use Text explicitly. That way I can avoid stripping leading zeros off of zip codes, etc.
Anybody know how to accomplish this?

You can override the type of data using the types option on add row.
Something like:
worksheet.add_row ['0012342'], :types => [:string]
Grab me on irc (JST) if you need any help getting that to work.
Best
randym
edit --
I've added an example for this to examples/example.rb in the repo.
wb.add_worksheet(:name => "Override Data Type") do |sheet|
sheet.add_row ['dont eat my zeros!', '0088'] , :types => [nil, :string]
end
https://github.com/randym/axlsx/blob/master/examples/example.rb#L349

format_code: '#' will work for you. Please find below code for reference.
def default_data_type_as_string
#xlsx_package = Axlsx::Package.new
#workbook = #xlsx_package.workbook
#worksheet = #workbook.add_worksheet(:name => "Introduction")
default_style = #workbook.styles.add_style({ format_code: '#' })
row_data_array = ['1', '2%', '3$']
#worksheet.add_row row_data_array, :style => [nil, default_style, nil]
#xlsx_package.serialize('default_data_type_as_string.xlsx')
end

For gem versions gem 'axlsx', '2.1.0.pre', gem 'axlsx_rails' in order to have the file columns in text type should specify both style and type
default_style = worksheet.styles.add_style({ format_code: '#' })
worksheet.add_row ['0012687'], :types => [:string], :style => [default_style]

Related

Fill tilt template using hash

I got following ruby example:
require 'tilt'
data = { "site_link" => "http://www.example.com", "title" => "example"}
template = Tilt.new('../templates/test.erb', :default_encoding => 'UTF-8')
output = template.render(data)
puts output
and this is test.erb file:
This should be a link - <%= site_link %>
I can't find a proper syntax to get a value from data hash into template.
Ok,
it looks like I need to specify to tilt that data are a hash. Correct code is:
output = template.render(Hash,data)

Watir method (or monkey-patch) to select span (or other) tags with custom ("data-*") attribute values equaling a string value (or matching a regex)

So this is ruby right, and while I do have a solution already, which I'll show below, its not tight. Feels like I'm using ahem "C++ iterators", if you will. Too many lines of code. Not like ruby.
Anyway, I'm wondering if there is classier way to do this:
b = Watir::Browser.new
b.goto "javascriptinjectedtablevalues.com" #not real website url:)
# desired urls in list are immediately located within <span> tags with a "class" of
#"name" plus a custom html attribute attribute of "data-bind" = "name: $data". that's it
# unless I wanted to use child-selectors which I'm not very good at
allrows = b.spans(:class => "name").each_with_index.map do |x, i|
[0, x.attribute_value("data-bind")]
end
real_row_ids = allrows.select{|i, databind| databind == "name: $data" }.map(&:first) #now I have all correct span ids
spans = real_row_ids.map {|id| b.spans(:class => "name")[id] }
Now that's a little messy in my opinion. But it leaves artifacts so I can debug and go back and stuff.
I could use this command to just grab a just the spans
spans = b.spans(:class => "name").map do |span|
[span, span.attribute_value("data-bind")]
end.select {|span, databind| databind == "name: $data"}.map(&:first)
but that still feels messy having no artifacts to show for it to use for later when trying to isolate other html tags nearby the span.
I'm hoping there is something like this pseudo code for watir:
b.spans(:class => "name").with_custom_attributes(:key => "data-bind", :value => "name: $data")
that's what I'd really like to do. superman-patching this custom method onto Watir within a rails initializer would be the optimal solution second to it already existing within Watir!
Watir already supports using data attributes for locators. You simply need to replace the dashes with underscores.
For example:
b.spans(:class => 'name', :data_bind => "name: $data")
Would match elements like:
<span class="name" data-bind="name: $data">
Similarly, you can use a regex when matching the data attribute:
b.spans(:class => 'name', :data_bind => /name/)

Ruby - Dynamically named variable based on a string read from a .csv

I have a .csv from which I read and parse to create an instance of a class, I want to name the class after the string of text returned from the first row in the .csv.
I can create the classes just fine manually but want to read row[0] and name the variable after that.
eg.
CSV.foreach("banks.csv", :headers => true) do |row|
***contents of row[0]*** = Bank.new(row[0], row[1], row[2], row[3], row[4])
row[0] == "Bank_of_America" for example, so I want the code to be equivalent to the following;
Bank_of_America = Bank.new(row[0], row[1], row[2], row[3], row[4])
I have read a few other replies on similar topics using instance_variable_set but cannot get the code given to work.
Thanks in advance for any advice!
edit: The following worked;
instance_variable_set("##{row[0]}", Bank.new(row[0], row[1], row[2], row[3], row[4]))
You could do something like this and create an instance variable dynamically:
x = 10
# => 10
instance_variable_set("#the_number_#{x}", x)
# => 10
#the_number_10
# => 10
You can replace "#the_number_#{x}" with row[0] now.
EDIT: Sorry, I didn't read the last part of you question about instance variables. So, how do you mean you can't get them to work? Does it come up with some kind of exception or just doesn't set to the right value. Give us the code that doesn't work.

Setting a hyperlink text color in axlsx

I'm trying to set the foreground color of text in a hyperlink cell but it doesn't seem to work.
Using something like: sheet["A1"].color = "0000FF" works fine for a normal cell, but not for a hyperlinked cell
This code simply creates a link to cell D1 on the "Log" sheet (which works fine) but A1 never turns blue!
sheet.add_hyperlink :location => "'Log'!D1", :target => :sheet, :ref => "A1"
sheet["A1"].color = "0000FF"
Thanks!
There are two important things to do before applying a color to a link:
You have to define the color within a style, and
You have to know the exact address of the cell in question.
Styles are normally applied to rows, but in this case you want to apply it to a specific cell. This is possible, but you need to address the cell directly through the Sheet Object. Also, and somewhat counter intuitively, the 'add_hyperlink' method is available to the Sheet object, not the Cell. So beware of that as well.
Here is an example of how to apply a style to a cell containing a link:
p = Axlsx::Package.new
p.workbook do |wb|
wb.styles do |s|
blue_link = s.add_style :fg_color => '0000FF'
wb.add_worksheet(:name => "Anchor Link Test") do |sheet|
sheet.add_row ['Title', 'Link']
# Define the row here, we will use that later
row = sheet.add_row ['Google', 'Click to go']
# Add the hyperlink by addressing the column you have used and add 1 to the row's index value.
sheet.add_hyperlink :location => "http://www.google.com", :ref => "B#{row.index + 1}"
sheet["B#{row.index + 1}"].style = blue_link
end
s = p.to_stream()
File.open("anchor_link_test.xlsx", 'w') { |f| f.write(s.read) }
end
end
Final note: You might note that I have written this spreadsheet using the methods
s = p.to_stream()
File.open("anchor_link_test.xlsx", 'w') { |f| f.write(s.read) }
There is evidence presented on the Axlsx Github Issues Page which shows that this means of writing out the file is significantly faster than
p.serialize
Just thought that deserved mention somewhere on StackOverflow!
This seems to work:
require 'axlsx'
p = Axlsx::Package.new
ws = p.workbook.add_worksheet
ws.add_row ['hoge-hoge']
ws['A1'].color = '0000FF'
ws.add_hyperlink :location => 'F6', :target => :sheet, :ref => 'A1'
p.serialize 'where_is_my_color.xlsx'
Can you post a larger example of your code that does not set the color?
Apparently Axlsx is only applying custom styles to String data types. Fixed this by setting each column to type :string like this:
Sheet.add_row [ "1", "2", "3" ], :types => [:string, :string, :string]
Thanks Randy!

Looping through XML to create an array of hashes in Ruby

I have the following XML
<CallResult>
<Success>true</Success>
<Result>
<ZoneInfo>
<Id>3</Id>
<Name>test-room</Name>
<NId>sdfsdg</NId>
</ZoneInfo>
<ZoneInfo>
<Id>16</Id>
<Name>Dynamic</Name>
<NId>sadadrwed543th</NId>
</ZoneInfo>
<ZoneInfo>
<Id>32</Id>
<Name>lobby</Name>
<NId>ssdfrgfdfg</NId>
</ZoneInfo>
<ZoneInfo>
<Id>33</Id>
<Name>conf</Name>
<NId>sdfsfewr232f</NId>
</ZoneInfo>
</Result>
<Message>Success</Message>
</CallResult>
I am trying to parse the XML so that each different 'ZoneInfo' attributes is a hash in an array.
E.g.
Zones[0] = Hash[Id => 32, Name => lobby, NId => ssdfrgfdfg]
Zones[1] = Hash[Id => 33, Name => conf, NId => sdfsfewr232f]
etc...
My limited XML parsing knowledge has come a croper. All I really know is how to extract a single element. E.g.
doc = REXML::Document.new(xmlData)
doc.elements.each("CallResult/Success") do |ele|
p ele.text;
end
Could someone help with some more info on how to loop through just extracting info from each 'ZoneInfo' element?
Thanks
I use another gem 'nokogiri', maybe the best gem to parse HTML/XML now.
require 'nokogiri'
str = "<CallResult> ......"
doc = Nokogiri.XML(str)
Zones = []
doc.xpath('//ZoneInfo').each do |zone|
Zones << { "Id" => zone.xpath('Id').text, "Name" => zone.xpath('Name').text, "NId" => zone.xpath("NId").text}
end
You just need to use nori gem
require 'nori'
your_hash = Nori.parse(your_xml)
And then it should be straightforward to convert this nested hash to an array of hashes if you need to store your data that way.
If you need more info, api doc is here - http://rubydoc.info/gems/nori/1.1.3/frames

Resources