Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
A slick Ruby DSL for reporting.
Ruby
tag: v0.0.4

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
features
lib
spec
.autotest
.document
.gitignore
LICENSE
README.markdown
Rakefile
VERSION
rose.gemspec

README.markdown

rose

Rose (say it out loud: rows, rows, rows) is a slick Ruby DSL for reporting:

Rose.make(:worlds) do
  rows do
    column(:hello => "Hello")
    column(:world)
  end
end

class World < Struct.new(:hello, :world)
end

Rose(:worlds).bloom([World.new("Say", "what?")]).to_s

+---------------+
| Hello | world |
+---------------+
| Say   | what? |
+---------------+

Install the gem:

gem install rose

Usage

Making a Report

class Flower < Struct.new(:type, :color, :age)
end

Rose.make(:poem, :class => Flower) do
  rows do
    column(:type => "Type")
    column("Color", &:color)
  end
end

Running a Report

Rose(:poem).bloom([Flower.new(:roses, :red), Flower.new(:violets, :blue)])

+-----------------+
|  Type   | Color |
+-----------------+
| roses   | red   |
| violets | blue  |
+-----------------+

Sorting

Rose.make(:with_sort_by_age_descending, :class => Flower) {
  rows do
    column(:type => "Type")
    column(:color => "Color")
    column(:age => "Age")
  end
  sort("Age", :descending)
}

Filtering

Rose.make(:with_filter, :class => Flower) {
  rows do
    column(:type => "Type")
    column(:color => "Color")
    column(:age => "Age")
  end
  filter do |row|
    row["Color"] != "blue"
  end
}

Summarizing

Rose.make(:with_summary, :class => Flower) {
  rows do
    column(:type => "Type")
    column(:color => "Color")
  end
  summary("Type") do
    column("Color") { |colors| colors.uniq.join(", ") }
    column("Count") { |colors| colors.size }
  end
}

Pivoting

Rose.make(:with_pivot, :class => Flower) {
  rows do
    column(:type => "Type")
    column(:color => "Color")
    column(:age => "Age")
  end
  pivot("Color", "Type") do |rows|
    rows.map(&:Age).map(&:to_i).inject(0) { |sum,x| sum+x }
  end
}

Importing

Rose.make(:with_find_and_update) do
  rows do
    identity(:id => "ID")
    column(:type => "Type")
    column(:color => "Color")
    column(:age => "Age")
  end
  roots do
    # find is optional. By default will return items with item["ID"] == idy
    find do |items, idy|
      items.find { |item| item.id.to_s == idy }
    end
    update do |item, updates|
      item.color = updates["Color"]
    end
  end
end

#identity must be used for one column. Without it Rose won't be able to identify which items to update.

Manually

Rose(:with_find_and_update).photosynthesize(@flowers, {
  :updates => {
    "0" => { "Color" => "blue" }
    # ID => Updates
  }
})

CSV

Rose(:with_find_and_update).photosynthesize(@flowers, {
  :csv_file => "change_flowers.csv"
})

Preview

Rose(:with_find_and_update).photosynthesize(@flowers, {
  :updates => {
    "0" => { "Color" => "blue" }
  }, 
  :preview => true
})

Rose(:with_find_and_update).photosynthesize(@flowers, {
  :csv_file => "change_flowers.csv",
  :preview  => true
})

ActiveRecord

First, use the ActiveRecord adapter:

config.gem 'rose', :lib => 'rose/active_record'

For the most part, the ActiveRecord adapter has the same interface as the ObjectAdapter, except for the following differences:

Making a Report

Employee.rose(:department_salaries) do
  rows do
    column("Name") { |e| "#{e.firstname} #{e.lastname}" }
    column("Department") { |e| e.department.name }
    column("Salary") { |e| e.salary }
  end
  summary("Department") do
    column("Salary") { |salaries| salaries.map(&:to_i).sum }
  end
end

Running a Report

Employee.rose_for(:department_salaries, :conditions => ["salary <> ?", nil])

+----------------------+
| Department  | Salary |
+----------------------+
| Accounting  |  85000 |
| Admin       |  69000 |
| Sales       | 120000 |
| Engineering | 122000 |
| IT          |  50000 |
| Graphics    |  42000 |
+----------------------+

Employee#rose_for is a helper method that blooms on Employee.find(:all, :conditions => ["salary <> ?", nil]). If you still want direct access to your report, you can use Employee.seedlings(:department_salaries)

Importing (with Preview)

Post.rose(:for_update) {
  rows do
    identity(:guid => "ID")
    column("Title", &:title)
    column("Comments") { |item| item.comments.size }
  end

  sort("Comments", :descending)

  roots do
    find do |items, idy|
      items.find { |item| item.guid == idy }
    end
    update do |record, updates|
      record.update_attribute(:title, updates["Title"])
    end
  end
}

Post.root_for(:for_update, {
  :updates => {
    "1" => { "Title" => "New Title" }
  },
  :preview => true
}) # => Returns a table

Post.root_for(:for_update, {
  :csv_file => "change_flowers.csv"
  :preview  => true
})

Other

Inspired by Machinist and factory_girl


Future

  • Documentation

Copyright

Copyright (c) 2010 Henry Hsu. See LICENSE for details.

Something went wrong with that request. Please try again.