Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
simsalabim committed Dec 9, 2012
0 parents commit 49dc9c5
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .gitignore
@@ -0,0 +1,18 @@
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
.idea/
4 changes: 4 additions & 0 deletions .travis.yml
@@ -0,0 +1,4 @@
language: ruby
script: "bundle exec cucumber"
rvm:
- 1.9.3
8 changes: 8 additions & 0 deletions Gemfile
@@ -0,0 +1,8 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in lazy-wombat.gemspec
gemspec

group :test do
gem 'rake'
end
22 changes: 22 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,22 @@
Copyright (c) 2012 Alexander Kaupanin

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
99 changes: 99 additions & 0 deletions README.md
@@ -0,0 +1,99 @@
# Lazy::Wombat ![Build Status](https://travis-ci.org/simsalabim/lazy-wombat.png "Build Status")

A simple yet powerful DSL to generate Excel spreadsheets built on top of [axlsx](https://github.com/randym/axlsx) gem.

## Why Lazy Wombat?
Axlsx is awesome and quite complex in usage.
Often you need something simple and easy-to-use to generate an excel spreadsheet as easy, as you markup tables with HTML.
Now you can.

## Installation

Add this line to your application's Gemfile:

gem 'lazy-wombat'

And then execute:

$ bundle

Or install it yourself as:

$ gem install lazy-wombat

## Usage

### Basic sample: no styles, just content

Code
```ruby
LazyWombat::Xlsx.new do
spreadsheet do
row do
cell 'Cyberdyne Systems'
cell 'Model 101'
cell 'The Terminator'
end
end
end.save 'my_laziness.xlsx'
```
Or shortened:
```ruby
LazyWombat::Xlsx.new do
cell 'Cyberdyne Systems'
cell 'Model 101'
cell 'The Terminator'
end.save 'my_laziness.xlsx'
```
will create `my_laziness` spreadsheet looks like this: ![Generated spreadsheet](http://img525.imageshack.us/img525/7037/spreadsheet1.png)

Since spreadsheet elements inheritance is alike `spreadsheet -> row -> cell`, you can arbitrary omit every unnecessary
elder element of your spreadsheets.

### Where is my HTML??
Oh yeah, I promised you `as you markup tables with HTML`: there're logic aliases:
`spreadsheet` is `table`, `row` is `tr`, `cell` is, of course, `td`

Thus here's our simplest example:
```ruby
LazyWombat::Xlsx.new do
table do
tr do
td 'Cyberdyne Systems'
td 'Model 101'
td 'The Terminator'
end
end
end.save 'my_laziness.xlsx'

# or
LazyWombat::Xlsx.new do
td 'Cyberdyne Systems'
td 'Model 101'
td 'The Terminator'
end.save 'my_laziness.xlsx'
```

### Additional options: spreadsheet names, rows and cells styles
By default spreadsheets are named as `Sheet 1`, `Sheet 2`, etc. It can be overwritten using `name` option.
``ruby
LazyWombat::Xlsx.new do
spreadsheet name: 'My Laziness' do
row style: :bold do
cell 'Cyberdyne Systems'
cell 'Model 101', style: :italic
cell 'The Terminator'
end
end
end.save 'my_laziness.xlsx'
```
![Generated spreadsheet](http://img521.imageshack.us/img521/4272/spreadsheet2.png)
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
1 change: 1 addition & 0 deletions Rakefile
@@ -0,0 +1 @@
require "bundler/gem_tasks"
14 changes: 14 additions & 0 deletions features/generate_spreadsheet.feature
@@ -0,0 +1,14 @@
Feature: As a lazy wombat I should be able to generate an xlsx spreadsheet

Scenario: I generate spreadsheet and save it to file
Given I generate following spreadsheet:
| Arnold | Schwarzenegger | Cyberdyne Systems Model 101 | The Terminator | 1 |
| Wombat | Animal | Universal | Optimum Nutrition | 001 |
| Hamster | Mole | Apartments | Valuation | Mockups |
And save it as "my_spreadsheet.xlsx"
Then "my_spreadsheet.xlsx" file should exist
And "my_spreadsheet.xlsx" should have 1 spreadsheet
And "my_spreadsheet.xlsx" should look like this:
| Arnold | Schwarzenegger | Cyberdyne Systems Model 101 | The Terminator | <1.0> |
| Wombat | Animal | Universal | Optimum Nutrition | <1.0> |
| Hamster | Mole | Apartments | Valuation | Mockups |
44 changes: 44 additions & 0 deletions features/step_definitions/generate_spreadsheet_steps.rb
@@ -0,0 +1,44 @@
require 'roo'

Given /^I generate following spreadsheet:$/ do |table|
@excel = LazyWombat::Xlsx.new do
row do
table.hashes.first.keys.each { |key| cell key }
end
table.hashes.each do |hash|
row do
hash.values.each { |value| cell value }
end
end
end
end

Given /^save it as "(.*?)"$/ do |file_name|
@excel.save file_name
end

Then /^"(.*?)" file should exist$/ do |file_name|
File.exists? file_name
end

Then /^"(.*?)" should have (\d+) spreadsheet$/ do |file_name, count|
Excelx.new(file_name).sheets.should have(count).records
end

# gem 'roo' casts all read numbers to float
Then /^"(.*?)" should look like this:$/ do |file_name, table|
workbook = Excelx.new file_name
prototype = table.raw
columns_count = prototype.first.count
1.upto(workbook.last_row) do |row_number|
row = columns_count.times.map { |column| workbook.cell row_number, column + 1 }
row.should == row_with_types(prototype[ row_number - 1 ])
end
end

def row_with_types row
return if row.nil?
row.each_with_index do |value, index|
row[index] = (value =~ /\<(.*)\>/) ? eval(value[1..-2]) : value
end
end
4 changes: 4 additions & 0 deletions features/support/env.rb
@@ -0,0 +1,4 @@
$:.push File.expand_path('../../../lib', __FILE__)

require 'lazy-wombat'
require 'rspec/matchers'
24 changes: 24 additions & 0 deletions lazy-wombat.gemspec
@@ -0,0 +1,24 @@
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'lazy-wombat/version'

Gem::Specification.new do |s|
s.name = 'lazy-wombat'
s.version = LazyWombat::VERSION
s.authors = ['Alexander Kaupanin']
s.email = %w(kaupanin@gmail.com)
s.description = %q{A simple yet powerful DSL to create Excel spreadsheets built on top of axlsx gem}
s.summary = %q{A simple yet powerful DSL to create Excel spreadsheets built on top of axlsx gem}
s.homepage = 'http://github.com/simsalabim/lazy-wombat'

s.files = `git ls-files`.split($/)
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
s.test_files = s.files.grep(%r{^(test|spec|features)/})
s.require_paths = %w(lib)

s.add_development_dependency 'cucumber', '~> 1.1'
s.add_development_dependency 'rspec', '~> 2.9'
s.add_development_dependency 'roo'
s.add_runtime_dependency 'axlsx'
end
143 changes: 143 additions & 0 deletions lib/lazy-wombat.rb
@@ -0,0 +1,143 @@
# encoding: utf-8
require 'lazy-wombat/version'
require 'axlsx'

module LazyWombat

class Xlsx

def initialize options = {}, &block
@package_options = options
@package = Axlsx::Package.new
instance_exec &block
end

def spreadsheet options = {}, &block
if block_given?
@package.workbook.add_worksheet do |spreadsheet|
@spreadsheet = spreadsheet
@spreadsheet.name = options[:name] unless options[:name].nil? || options[:name].empty?
build_spreadsheet_styles
instance_exec &block
end
else
build_spreadsheet
end
end
alias_method :table, :spreadsheet

def build_spreadsheet
@spreadsheet ||= @package.workbook.add_worksheet.tap { @row = @cell = nil }
end

def row options = {}, &block
if block_given?
@row_options = options
spreadsheet.add_row do |row|
@row = row
instance_exec &block
end
else
build_row
end
end
alias_method :tr, :row

def build_row
@row ||= spreadsheet.add_row.tap{ @cell = nil }
end

def cell value, options = {}
unless @row_options.nil? || @row_options.empty?
options = @row_options.merge(options){ |key, row_option, cell_option| Array.new << row_option << cell_option }
end
@cell = row.add_cell value.to_s, style: style_from_options(options)
unless options[:colspan].nil? || options[:colspan].empty?
spreadsheet.merge_cells "#{pos[:x]}#{pos[:y]}:#{shift_x(pos[:x], 5)}#{pos[:y]}"
end
end
alias_method :td, :cell

def save file_name
@package.serialize file_name
File.open file_name
end

def to_temp_file
stream = @package.to_stream
Tempfile.new(%w(temporary-workbook .xlsx), encoding: 'utf-8').tap do |file|
file.write stream.read
file.close
end
end

def to_xml
spreadsheet.to_xml_string
end

private

def pos
{ x: x_axis[@cell.pos[0]], y: y_axis[@cell.pos[1]] }
end

def shift_x x, diff
x_axis[x_axis.index(x) + diff]
end

def style_from_options options
if options[:style].is_a? Array
add_complex_style options[:style]
end
spreadsheet_style_by_name style_name(options[:style])
end

def style_name style_keys
Array(style_keys).join('_').to_sym
end

def add_complex_style style_keys
complex_option_args = {}
style_keys.map{ |style_key| style_builder_args[style_key] }.compact.map{ |args| complex_option_args.merge! args }
@style_builder_args[style_name(style_keys)] = complex_option_args
rebuild_spreadsheet_styles
end

def style_builder_args
@style_builder_args ||= {
center: { alignment: { horizontal: :center } },
bold: { b: true },
italic: { i: true }
}
end

def build_spreadsheet_styles
@spreadsheet_styles = {}
spreadsheet.workbook.styles do |style_builder|
style_builder_args.each do |style_name, style_args|
@spreadsheet_styles[style_name] = style_builder.add_style style_args
end
end
@spreadsheet_styles
end
alias_method :rebuild_spreadsheet_styles, :build_spreadsheet_styles

def spreadsheet_styles
@spreadsheet_styles ||= build_spreadsheet_styles
end

def spreadsheet_style_by_name name
spreadsheet_styles[name]
end

# should be dynamic but for now to avoid calculations is static
def x_axis
('A'..'Z').to_a
end

# should be dynamic but for now to avoid calculations is static
def y_axis
(1..1000).to_a
end
end
end

0 comments on commit 49dc9c5

Please sign in to comment.