declarative_grid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +90 -0
- data/Rakefile +12 -0
- data/declarative_grid.gemspec +30 -0
- data/lib/declarative_grid/base.rb +46 -0
- data/lib/declarative_grid/configuration.rb +6 -0
- data/lib/declarative_grid/model/column.rb +35 -0
- data/lib/declarative_grid/model/grid_class.rb +29 -0
- data/lib/declarative_grid/model/row.rb +54 -0
- data/lib/declarative_grid/model.rb +7 -0
- data/lib/declarative_grid/renderers/abstract_renderer.rb +99 -0
- data/lib/declarative_grid/renderers/csv.rb +17 -0
- data/lib/declarative_grid/renderers/html_table.rb +38 -0
- data/lib/declarative_grid/renderers.rb +9 -0
- data/lib/declarative_grid/version.rb +3 -0
- data/lib/declarative_grid.rb +33 -0
- data/spec/declarative_grid/base_spec.rb +70 -0
- data/spec/declarative_grid/model/column_spec.rb +110 -0
- data/spec/declarative_grid/model/grid_class_spec.rb +39 -0
- data/spec/declarative_grid/model/row_spec.rb +52 -0
- data/spec/declarative_grid/renderers/abstract_renderer_spec.rb +99 -0
- data/spec/declarative_grid_spec.rb +15 -0
- data/spec/factories/models.rb +24 -0
- data/spec/spec_helper.rb +17 -0
- metadata +175 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 David Lin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# DeclarativeGrid
|
2
|
+
|
3
|
+
DeclarativeGrid is a simple, flexible and extensible datagrid/table generator.
|
4
|
+
|
5
|
+
- Configurable: Described in DSL
|
6
|
+
- Extensible Renderers: You can build a custom renderer to generate XLSX, json, and so on
|
7
|
+
- Select and sort columns: You can assign array of column names to renderer
|
8
|
+
- Any Enumerable or Enumerator can be source data
|
9
|
+
- It is independent from ActiveRecord and other ORMs
|
10
|
+
|
11
|
+
## Example
|
12
|
+
|
13
|
+
in models/member_grid.rb
|
14
|
+
|
15
|
+
class MemberGrid < DeclarativeGrid::Base
|
16
|
+
# Columns
|
17
|
+
#
|
18
|
+
column(:name)
|
19
|
+
column(:birthday) {|r| (r.birthdate) }
|
20
|
+
column(:gender, header: 'Sex')
|
21
|
+
|
22
|
+
# Renderers
|
23
|
+
#
|
24
|
+
# with builtin render class: DeclarativeGrid::Renderers::Csv
|
25
|
+
renderer :csv
|
26
|
+
#
|
27
|
+
# with builtin render class: DeclarativeGrid::Renderers::HtmlTable
|
28
|
+
renderer :html_table
|
29
|
+
#
|
30
|
+
# with selected columns
|
31
|
+
renderer :csv_selected, class: :csv, columns: [:gender, :name]
|
32
|
+
#
|
33
|
+
# with custom renderer class
|
34
|
+
renderer :xls, class: XlsRenderer
|
35
|
+
#
|
36
|
+
end
|
37
|
+
|
38
|
+
in member.rb
|
39
|
+
|
40
|
+
class Member < Struct.new(:name, :gender, :birthdate)
|
41
|
+
end
|
42
|
+
|
43
|
+
example code:
|
44
|
+
|
45
|
+
m = Member.new('Adam Jensen', 'Male', Date.new(1993,3,9))
|
46
|
+
@grid = MemberGrid.new { [m] }
|
47
|
+
|
48
|
+
puts "== sample 1 =="
|
49
|
+
@grid.renderer(:csv).each_data {|s| puts s }
|
50
|
+
|
51
|
+
puts "== sample 2 =="
|
52
|
+
@grid.renderer(:csv_selected).each_data {|s| puts s }
|
53
|
+
|
54
|
+
result:
|
55
|
+
|
56
|
+
== sample 1 ==
|
57
|
+
Name,Birthday,Sex
|
58
|
+
Adam Jensen,1993-03-09,Male
|
59
|
+
== sample 2 ==
|
60
|
+
Sex,Name
|
61
|
+
Male,Adam Jensen
|
62
|
+
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
Add this line to your application's Gemfile:
|
67
|
+
|
68
|
+
gem 'declarative_grid'
|
69
|
+
|
70
|
+
And then execute:
|
71
|
+
|
72
|
+
$ bundle
|
73
|
+
|
74
|
+
Or install it yourself as:
|
75
|
+
|
76
|
+
$ gem install declarative_grid
|
77
|
+
|
78
|
+
## Usage
|
79
|
+
|
80
|
+
## How to Test
|
81
|
+
|
82
|
+
$ rake spec
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
Rake::TestTask.new :spec do |t|
|
5
|
+
t.libs << "spec"
|
6
|
+
t.pattern = "spec/**/*_spec.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Open an irb session preloaded with this library"
|
10
|
+
task :console do
|
11
|
+
sh "irb -rubygems -I lib -r declarative_grid"
|
12
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'declarative_grid/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "declarative_grid"
|
8
|
+
gem.version = DeclarativeGrid::VERSION
|
9
|
+
gem.authors = ["David Lin"]
|
10
|
+
gem.email = ["davll.xc@gmail.com"]
|
11
|
+
gem.description = %q{DeclarativeGrid is a gem for rendering grids described in simple DSL}
|
12
|
+
gem.summary = %q{A flexible grid renderer}
|
13
|
+
gem.homepage = "https://github.com/davll/declarative_grid"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.required_ruby_version = "~> 1.9.0"
|
21
|
+
|
22
|
+
gem.add_dependency "activesupport", "~> 3.2.8"
|
23
|
+
gem.add_dependency "backports", "~> 2.6.4"
|
24
|
+
|
25
|
+
gem.add_development_dependency "rake", "~> 0.9.2.2"
|
26
|
+
gem.add_development_dependency "minitest", "~> 4.1.0"
|
27
|
+
gem.add_development_dependency "mocha", "~> 0.12.7"
|
28
|
+
gem.add_development_dependency "factory_girl", "~> 4.1.0"
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
class Base
|
3
|
+
extend Model::GridClass
|
4
|
+
|
5
|
+
# Renderer Classes
|
6
|
+
def self.renderer_classes
|
7
|
+
@renderer_classes ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
# Define a renderer
|
11
|
+
def self.renderer(name, options = {})
|
12
|
+
klass = Class.new begin
|
13
|
+
case k = options.delete(:class)
|
14
|
+
when Class then k
|
15
|
+
when String then k.camelize.constantize
|
16
|
+
else "declarative_grid/renderers/#{k || name}".camelize.constantize
|
17
|
+
end
|
18
|
+
end
|
19
|
+
klass.grid_class = self
|
20
|
+
klass.options.merge! options
|
21
|
+
self.renderer_classes[name] = klass
|
22
|
+
end
|
23
|
+
|
24
|
+
# Attributes
|
25
|
+
attr_accessor :records
|
26
|
+
|
27
|
+
# Constructor
|
28
|
+
#
|
29
|
+
# Usage 1: grid = MyGrid.new { MyRecord.all }
|
30
|
+
#
|
31
|
+
def initialize
|
32
|
+
self.records = yield
|
33
|
+
end
|
34
|
+
|
35
|
+
# Renderer by name
|
36
|
+
def renderer(name)
|
37
|
+
self.class.renderer_classes[name].new(self.records)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Prevent from @records dumping
|
41
|
+
def inspect
|
42
|
+
self.class.inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Model
|
3
|
+
class Column
|
4
|
+
# Attributes
|
5
|
+
attr_accessor :options, :block, :name
|
6
|
+
|
7
|
+
# Constructor
|
8
|
+
def initialize(name, options = {}, &block)
|
9
|
+
self.name, self.options = name, options
|
10
|
+
self.block = block || lambda {|record| record.send self.name }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return the header
|
14
|
+
def header
|
15
|
+
header = self.options[:header]
|
16
|
+
case header
|
17
|
+
when String, Symbol, Numeric then header
|
18
|
+
when Proc then header.call()
|
19
|
+
when nil then self.name.to_s.humanize
|
20
|
+
else raise DefinitionError, "#{header} is not valid"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return the corresponding value of the given record
|
25
|
+
def value_for(record)
|
26
|
+
block = self.block
|
27
|
+
case block.arity
|
28
|
+
when 1 then block.call record
|
29
|
+
else raise DefinitionError, "self.#{block} is not valid"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Model
|
3
|
+
module GridClass
|
4
|
+
|
5
|
+
def row
|
6
|
+
@row ||= Model::Row.new
|
7
|
+
end
|
8
|
+
|
9
|
+
# Define a column
|
10
|
+
# REFERENCE: Model::Row#define_column
|
11
|
+
def column(name, options = {}, &block)
|
12
|
+
self.row.define_column(name, options, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the columns
|
16
|
+
# REFERENCE: Model::Row#columns
|
17
|
+
def columns(*args)
|
18
|
+
self.row.columns(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
# override Class.inherited
|
22
|
+
def inherited(subclass)
|
23
|
+
super(subclass)
|
24
|
+
subclass.instance_variable_set :@row, self.row.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Model
|
3
|
+
class Row
|
4
|
+
|
5
|
+
# Attributes
|
6
|
+
attr_reader :columns_hash
|
7
|
+
|
8
|
+
# Constructor
|
9
|
+
def initialize
|
10
|
+
@columns_hash = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Copy Constructor
|
14
|
+
def initialize_copy(other)
|
15
|
+
super(other)
|
16
|
+
@columns_hash = other.columns_hash.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
# Define a column
|
20
|
+
def define_column(name, options = {}, &block)
|
21
|
+
name = name.to_sym
|
22
|
+
check_column_name_collision! name
|
23
|
+
@columns_hash[name] = Column.new name, options, &block
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return a list of the columns
|
27
|
+
#
|
28
|
+
# PARAM: names can be:
|
29
|
+
# 1) a list of Symbols : returns selected columns according to names
|
30
|
+
# 2) an empty list : returns all the columns
|
31
|
+
#
|
32
|
+
# NOTE: the returned columns is ordered by the given names
|
33
|
+
#
|
34
|
+
def columns(*names)
|
35
|
+
names = names.flatten
|
36
|
+
if names.empty?
|
37
|
+
@columns_hash.values
|
38
|
+
else
|
39
|
+
@columns_hash.values_at *(names.map(&:to_sym))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Check that the given name is used or not
|
46
|
+
def check_column_name_collision!(name)
|
47
|
+
if @columns_hash.has_key? name
|
48
|
+
raise DefinitionError, "#{self} has a column named as #{name}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Renderers
|
3
|
+
class AbstractRenderer
|
4
|
+
|
5
|
+
# Attributes of class
|
6
|
+
def self.grid_class; @grid_class; end
|
7
|
+
def self.grid_class=(k); @grid_class = k; end
|
8
|
+
def self.options; @options ||= {}; end
|
9
|
+
def self.options=(opt); @options = opt; end
|
10
|
+
|
11
|
+
def self.inherited(subclass)
|
12
|
+
super(subclass)
|
13
|
+
subclass.options = self.options.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
# Attributes
|
17
|
+
attr_accessor :records, :options
|
18
|
+
|
19
|
+
# Constructor
|
20
|
+
#
|
21
|
+
# PARAM: records is an object representing the records of model
|
22
|
+
# it can be:
|
23
|
+
# 1) Enumerator
|
24
|
+
# 2) Enumerable
|
25
|
+
#
|
26
|
+
# PARAM: options is a hash object containing optional configuration
|
27
|
+
# :columns => an array of column names to render
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def initialize(records, options = {})
|
31
|
+
self.records = records
|
32
|
+
self.options = self.class.options.merge options
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return selected columns to render
|
36
|
+
def columns
|
37
|
+
names = options[:columns] || []
|
38
|
+
self.class.grid_class.columns(*names)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return array of localized headers
|
42
|
+
def headers
|
43
|
+
self.columns.map(&:header)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return enumerator of rows
|
47
|
+
def each_row
|
48
|
+
if block_given?
|
49
|
+
self.each_record{|r| yield row_for(r) }
|
50
|
+
else
|
51
|
+
Enumerator.new self, :each_row
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return enumerator of self.records
|
56
|
+
def each_record
|
57
|
+
if block_given?
|
58
|
+
case records
|
59
|
+
when Enumerator, Enumerable then records.each{|r| yield r }
|
60
|
+
else raise RendererError, "#{records}: invalid type"
|
61
|
+
end
|
62
|
+
else
|
63
|
+
Enumerator.new self, :each_record
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Prevent from @records dumping
|
68
|
+
def inspect
|
69
|
+
self.class.inspect
|
70
|
+
end
|
71
|
+
|
72
|
+
# Rendered Object as an Enumerator
|
73
|
+
def each_data(&block)
|
74
|
+
block ? each_data_string(&block) : Enumerator.new(self, :each_data)
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# Render data to the block
|
80
|
+
def each_data_string
|
81
|
+
#
|
82
|
+
# yield string1
|
83
|
+
# yield string2
|
84
|
+
# yield string3
|
85
|
+
# ...
|
86
|
+
#
|
87
|
+
raise NotImplementedError
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Return row (array of data) of a given record
|
93
|
+
def row_for(record)
|
94
|
+
self.columns.map{|c| c.value_for(record) }
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Renderers
|
3
|
+
class Csv < AbstractRenderer
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def each_data_string
|
9
|
+
yield CSV.generate_line(headers)
|
10
|
+
each_row do |row|
|
11
|
+
yield CSV.generate_line(row)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module DeclarativeGrid
|
2
|
+
module Renderers
|
3
|
+
class HtmlTable < AbstractRenderer
|
4
|
+
|
5
|
+
def utils
|
6
|
+
ERB::Util
|
7
|
+
end
|
8
|
+
delegate :html_escape, to: :utils
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def each_data_string
|
13
|
+
yield "<table>"
|
14
|
+
|
15
|
+
if options[:caption]
|
16
|
+
yield "<caption>#{html_escape(options[:caption])}</caption>"
|
17
|
+
end
|
18
|
+
|
19
|
+
yield "<thead><tr>"
|
20
|
+
headers.each do |n|
|
21
|
+
yield "<th>#{html_escape(n)}</th>"
|
22
|
+
end
|
23
|
+
yield "</tr></thead>"
|
24
|
+
|
25
|
+
yield "<tbody>"
|
26
|
+
each_row do |r|
|
27
|
+
yield "<tr>"
|
28
|
+
r.each {|n| yield "<td>#{html_escape(n)}</td>" }
|
29
|
+
yield "</tr>"
|
30
|
+
end
|
31
|
+
yield "</tbody>"
|
32
|
+
|
33
|
+
yield "</table>"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "active_support/core_ext"
|
2
|
+
require "active_support/concern"
|
3
|
+
require "active_support/dependencies"
|
4
|
+
require "backports"
|
5
|
+
|
6
|
+
require "declarative_grid/version"
|
7
|
+
|
8
|
+
module DeclarativeGrid
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :Configuration
|
12
|
+
autoload :Base
|
13
|
+
autoload :Model
|
14
|
+
autoload :Renderers
|
15
|
+
|
16
|
+
def self.config
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.reset_config
|
21
|
+
@config = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
class DefinitionError < StandardError
|
25
|
+
end
|
26
|
+
|
27
|
+
class RendererError < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
class ConfigurationError < StandardError
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module DeclarativeGrid
|
4
|
+
describe Base do
|
5
|
+
before do
|
6
|
+
@klass = Class.new Base do
|
7
|
+
column :one
|
8
|
+
column :two
|
9
|
+
|
10
|
+
renderer :csv
|
11
|
+
renderer :html_table
|
12
|
+
renderer :html_table_1, class: :html_table
|
13
|
+
renderer :csv_1, class: 'DeclarativeGrid::Renderers::Csv'
|
14
|
+
renderer :csv_2, class: Renderers::Csv
|
15
|
+
renderer :csv_3, class: :csv, columns: [ :two, :two, :two ]
|
16
|
+
end
|
17
|
+
|
18
|
+
@record_class = Struct.new :one, :two
|
19
|
+
@records = 5.times.collect do |n|
|
20
|
+
@record_class.new n+1, n*2
|
21
|
+
end
|
22
|
+
|
23
|
+
@base = @klass.new { @records }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "is a Class of #{Model::GridClass}" do
|
27
|
+
@base.class.must_be_kind_of Model::GridClass
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has a collection of records" do
|
31
|
+
@base.records.must_equal @records
|
32
|
+
end
|
33
|
+
|
34
|
+
it "has a renderer named :csv" do
|
35
|
+
renderer = @base.renderer(:csv)
|
36
|
+
renderer.must_be_kind_of Renderers::Csv
|
37
|
+
end
|
38
|
+
|
39
|
+
it "has a renderer named :html_table" do
|
40
|
+
renderer = @base.renderer(:html_table)
|
41
|
+
renderer.must_be_kind_of Renderers::HtmlTable
|
42
|
+
end
|
43
|
+
|
44
|
+
it "has a renderer named :html_table_1" do
|
45
|
+
renderer = @base.renderer(:html_table_1)
|
46
|
+
renderer.must_be_kind_of Renderers::HtmlTable
|
47
|
+
renderer.options.wont_include :class
|
48
|
+
end
|
49
|
+
|
50
|
+
it "has a renderer named :csv_1" do
|
51
|
+
renderer = @base.renderer(:csv_1)
|
52
|
+
renderer.must_be_kind_of Renderers::Csv
|
53
|
+
renderer.options.wont_include :class
|
54
|
+
end
|
55
|
+
|
56
|
+
it "has a renderer named :csv_2" do
|
57
|
+
renderer = @base.renderer(:csv_2)
|
58
|
+
renderer.must_be_kind_of Renderers::Csv
|
59
|
+
renderer.options.wont_include :class
|
60
|
+
end
|
61
|
+
|
62
|
+
it "has a renderer named :csv_3" do
|
63
|
+
renderer = @base.renderer(:csv_3)
|
64
|
+
renderer.must_be_kind_of Renderers::Csv
|
65
|
+
renderer.options.wont_include :class
|
66
|
+
renderer.options.must_include :columns
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module DeclarativeGrid
|
4
|
+
describe Model::Column do
|
5
|
+
before do
|
6
|
+
@columns = 5.times.map{ FactoryGirl.build :model_column }
|
7
|
+
@columns_names = @columns.map(&:name)
|
8
|
+
|
9
|
+
@model_class = Struct.new(*(@columns_names + [:num]))
|
10
|
+
@record = @model_class.new(*(@columns.count.times.to_a + [10]))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "must be kind of column" do
|
14
|
+
result = @columns.collect{|c| c.is_a? Model::Column }
|
15
|
+
result.must_equal [true] * @columns.count
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has an attribute named :name (1)" do
|
19
|
+
@columns.map(&:name).must_equal @columns_names
|
20
|
+
@columns_names = @columns.collect do |column|
|
21
|
+
column.name = FactoryGirl.generate :model_column_name
|
22
|
+
end
|
23
|
+
@columns.map(&:name).must_equal @columns_names
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has a header with default value" do
|
27
|
+
@columns.map(&:header).must_equal @columns_names.map(&:to_s).map(&:humanize)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has a header that can be overriden with option (Symbol)" do
|
31
|
+
@columns.each do |column|
|
32
|
+
column.options[:header] = column.name.to_sym
|
33
|
+
end
|
34
|
+
@columns.map(&:header).must_equal @columns.map(&:name).map(&:to_sym)
|
35
|
+
@columns.map(&:header).must_equal @columns_names.map(&:to_sym)
|
36
|
+
|
37
|
+
@columns.each do |column|
|
38
|
+
column.name = FactoryGirl.generate :model_column_name
|
39
|
+
end
|
40
|
+
|
41
|
+
@columns.map(&:header).wont_equal @columns.map(&:name).map(&:to_sym)
|
42
|
+
@columns.map(&:header).must_equal @columns_names.map(&:to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has a header that can be overriden with option (String)" do
|
46
|
+
@columns.each do |column|
|
47
|
+
column.options[:header] = column.name.to_s
|
48
|
+
end
|
49
|
+
@columns.map(&:header).must_equal @columns.map(&:name).map(&:to_s)
|
50
|
+
@columns.map(&:header).must_equal @columns_names.map(&:to_s)
|
51
|
+
|
52
|
+
@columns.each do |column|
|
53
|
+
column.name = FactoryGirl.generate :model_column_name
|
54
|
+
end
|
55
|
+
|
56
|
+
@columns.map(&:header).wont_equal @columns.map(&:name).map(&:to_s)
|
57
|
+
@columns.map(&:header).must_equal @columns_names.map(&:to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "has a header that can be overriden with option (Numeric)" do
|
61
|
+
n = 0
|
62
|
+
@columns.each do |column|
|
63
|
+
column.options[:header] = (n += 1)
|
64
|
+
end
|
65
|
+
@columns.map(&:header).must_equal (1..@columns.count).to_a
|
66
|
+
end
|
67
|
+
|
68
|
+
it "has a header that can be overriden with option (Proc)" do
|
69
|
+
@columns.each do |column|
|
70
|
+
column.options[:header] = lambda{ column.name.to_s * 2 }
|
71
|
+
end
|
72
|
+
@columns.map(&:header).must_equal @columns_names.map{|n| n.to_s * 2 }
|
73
|
+
@columns.map(&:header).must_equal @columns.map{|c| c.name.to_s * 2 }
|
74
|
+
|
75
|
+
@columns.each do |column|
|
76
|
+
column.name = FactoryGirl.generate :model_column_name
|
77
|
+
end
|
78
|
+
|
79
|
+
@columns.map(&:header).wont_equal @columns_names.map{|n| n.to_s * 2 }
|
80
|
+
@columns.map(&:header).must_equal @columns.map{|c| c.name.to_s * 2 }
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can query value from record" do
|
84
|
+
result = @columns.collect do |column|
|
85
|
+
column.value_for(@record) == @record.send(column.name)
|
86
|
+
end
|
87
|
+
result.must_equal [true] * @columns.count
|
88
|
+
end
|
89
|
+
|
90
|
+
it "can query value from record - with block" do
|
91
|
+
@columns.each do |column|
|
92
|
+
column.block = lambda {|r| r.num }
|
93
|
+
end
|
94
|
+
|
95
|
+
@record.num = 20
|
96
|
+
result = @columns.map{|c| c.value_for @record }
|
97
|
+
result.must_equal [20] * @columns.count
|
98
|
+
|
99
|
+
@record.num = 30
|
100
|
+
result = @columns.map{|c| c.value_for @record }
|
101
|
+
result.must_equal [30] * @columns.count
|
102
|
+
end
|
103
|
+
|
104
|
+
it "must raise error if the arguments of block is not acceptable" do
|
105
|
+
@columns.each{|c| c.block = lambda { 'yoooooo' } }
|
106
|
+
proc{@columns.each{|c|c.value_for @record}}.must_raise DefinitionError
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module DeclarativeGrid
|
4
|
+
describe Model::GridClass do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@klass = Class.new Object do
|
8
|
+
extend Model::GridClass
|
9
|
+
column :name
|
10
|
+
column :kind
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has columns" do
|
15
|
+
@klass.columns.all?{|c| c.must_be_kind_of Model::Column }
|
16
|
+
|
17
|
+
names = @klass.columns.map(&:name)
|
18
|
+
names.must_include :name
|
19
|
+
names.must_include :kind
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has subclasses that inherit its definition" do
|
23
|
+
klass2 = Class.new @klass do
|
24
|
+
column :name2
|
25
|
+
end
|
26
|
+
|
27
|
+
names = @klass.columns.map(&:name)
|
28
|
+
names.must_include :name
|
29
|
+
names.must_include :kind
|
30
|
+
names.wont_include :name2
|
31
|
+
|
32
|
+
names2 = klass2.columns.map(&:name)
|
33
|
+
names2.must_include :name
|
34
|
+
names2.must_include :kind
|
35
|
+
names2.must_include :name2
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module DeclarativeGrid
|
4
|
+
describe Model::Row do
|
5
|
+
before do
|
6
|
+
@row = FactoryGirl.build :model_row
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can define many columns" do
|
10
|
+
@row.columns.must_be_empty
|
11
|
+
(1..10).collect do |n|
|
12
|
+
@row.define_column(FactoryGirl.generate :model_column_name)
|
13
|
+
@row.columns.count.must_equal n
|
14
|
+
|
15
|
+
result = @row.columns.collect {|c| c.is_a? Model::Column }
|
16
|
+
result.must_equal [true]*n
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can select columns" do
|
21
|
+
names = 5.times.map{ FactoryGirl.generate :model_column_name }
|
22
|
+
names.each {|x| @row.define_column x }
|
23
|
+
|
24
|
+
@row.columns.map(&:name).must_equal names
|
25
|
+
|
26
|
+
names.permutation do |perm|
|
27
|
+
[true, false].repeated_permutation(perm.count) do |flags|
|
28
|
+
if flags.all?{|f| f == false }
|
29
|
+
@row.columns([]).map(&:name).must_equal names
|
30
|
+
else
|
31
|
+
k = [perm, flags].transpose.select{|n,f|f}.transpose[0]
|
32
|
+
@row.columns(k).map(&:name).must_equal k
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "cannot have columns with the same name" do
|
39
|
+
name = FactoryGirl.generate :model_column_name
|
40
|
+
@row.define_column name
|
41
|
+
proc{ @row.define_column name }.must_raise DefinitionError
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can be cloned" do
|
45
|
+
row2 = @row.dup
|
46
|
+
# NOTE: Model::Column objects are not cloned actually
|
47
|
+
row2.columns_hash.must_equal @row.columns_hash
|
48
|
+
row2.columns_hash.wont_be_same_as @row.columns_hash
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module DeclarativeGrid
|
4
|
+
describe Renderers::AbstractRenderer do
|
5
|
+
before do
|
6
|
+
@klass = Class.new Object do
|
7
|
+
extend Model::GridClass
|
8
|
+
column :one
|
9
|
+
column :two
|
10
|
+
end
|
11
|
+
|
12
|
+
@record_class = Struct.new :one, :two
|
13
|
+
@records = 5.times.collect do |n|
|
14
|
+
@record_class.new n+1, n*2
|
15
|
+
end
|
16
|
+
|
17
|
+
@renderer_class = Class.new Renderers::AbstractRenderer
|
18
|
+
@renderer_class.grid_class = @klass
|
19
|
+
|
20
|
+
@renderer = @renderer_class.new @records
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is valid class" do
|
24
|
+
@renderer.must_be_kind_of @renderer_class
|
25
|
+
@renderer.class.grid_class.must_equal @klass
|
26
|
+
end
|
27
|
+
|
28
|
+
it "is vaild after initialization" do
|
29
|
+
@renderer.records.must_equal @records
|
30
|
+
@renderer.options.must_equal ({})
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has all the columns" do
|
34
|
+
@renderer.columns.each{|c| c.must_be_kind_of Model::Column }
|
35
|
+
@renderer.columns.map(&:name).must_equal [:one, :two]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "renders the headers" do
|
39
|
+
@renderer.headers.must_equal @renderer.columns.map(&:header)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "enumerates all the records" do
|
43
|
+
@renderer.each_record.must_be_kind_of Enumerator
|
44
|
+
@renderer.each_record.to_a.must_equal @records
|
45
|
+
end
|
46
|
+
|
47
|
+
it "enumerates all the rows" do
|
48
|
+
@renderer.each_row.must_be_kind_of Enumerator
|
49
|
+
@renderer.each_row.to_a.must_equal @records.map{|r| [r.one, r.two] }
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can select columns" do
|
53
|
+
@renderer.options[:columns] = [ :two ]
|
54
|
+
@renderer.columns.must_equal @klass.columns(:two)
|
55
|
+
@renderer.each_row.to_a.must_equal @records.map{|r| [r.two] }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can select columns with different orders" do
|
59
|
+
@renderer.options[:columns] = [ :two, :one, :one ]
|
60
|
+
@renderer.columns.must_equal @klass.columns(:two, :one, :one)
|
61
|
+
@renderer.each_row.to_a.must_equal @records.map{|r|[r.two,r.one,r.one]}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can accept records as Enumerator" do
|
65
|
+
@renderer.records = @records.each
|
66
|
+
@renderer.each_record.must_be_kind_of Enumerator
|
67
|
+
@renderer.each_record.to_a.must_equal @records
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can override class-defined options" do
|
71
|
+
@renderer_class.options[:columns] = [ :one, :one, :one ]
|
72
|
+
@renderer_class.options.wont_equal @renderer_class.superclass.options
|
73
|
+
@renderer = @renderer_class.new @records, columns: [:two]
|
74
|
+
@renderer.options[:columns].must_equal [:two]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "has a not implemented method :each_data_string" do
|
78
|
+
proc {
|
79
|
+
@renderer.send(:each_data_string)
|
80
|
+
}.must_raise NotImplementedError
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has a method :each_data" do
|
84
|
+
@renderer.expects(:each_data_string)
|
85
|
+
@renderer.each_data.must_be_kind_of Enumerator
|
86
|
+
@renderer.each_data {|s| s }
|
87
|
+
end
|
88
|
+
|
89
|
+
it "call each_data to return the string" do
|
90
|
+
def @renderer.each_data_string
|
91
|
+
yield "test"
|
92
|
+
yield "test2"
|
93
|
+
yield "test3"
|
94
|
+
end
|
95
|
+
@renderer.each_data.to_a.must_equal ["test", "test2", "test3"]
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DeclarativeGrid do
|
4
|
+
|
5
|
+
it "has configuration object" do
|
6
|
+
DeclarativeGrid.config.must_be_kind_of DeclarativeGrid::Configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can reset configuration" do
|
10
|
+
config = DeclarativeGrid.config
|
11
|
+
DeclarativeGrid.reset_config
|
12
|
+
DeclarativeGrid.config.wont_equal config
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
|
3
|
+
sequence(:model_column_name) {|n| :"model_column_#{n}" }
|
4
|
+
|
5
|
+
factory :model_row, class: DeclarativeGrid::Model::Row do
|
6
|
+
initialize_with { self.new }
|
7
|
+
end
|
8
|
+
|
9
|
+
factory :model_column, class: DeclarativeGrid::Model::Column do
|
10
|
+
name { FactoryGirl.generate :model_column_name }
|
11
|
+
options { {} }
|
12
|
+
block nil
|
13
|
+
|
14
|
+
initialize_with {
|
15
|
+
name = attributes[:name]
|
16
|
+
row = attributes[:row]
|
17
|
+
options = attributes[:options]
|
18
|
+
block = attributes[:block]
|
19
|
+
|
20
|
+
new(name, options, &block)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Bundler
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
# Test Framework
|
6
|
+
require "minitest/autorun"
|
7
|
+
require "mocha"
|
8
|
+
require "factory_girl"
|
9
|
+
|
10
|
+
#
|
11
|
+
require 'declarative_grid'
|
12
|
+
|
13
|
+
#
|
14
|
+
SPEC_ROOT = File.expand_path("..", __FILE__)
|
15
|
+
|
16
|
+
# load factories
|
17
|
+
Dir[File.join(SPEC_ROOT, "factories/**/*.rb")].each {|f| require f }
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: declarative_grid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Lin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.8
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.8
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: backports
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.6.4
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.6.4
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.2.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2.2
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 4.1.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 4.1.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: mocha
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.12.7
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.12.7
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: factory_girl
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 4.1.0
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 4.1.0
|
110
|
+
description: DeclarativeGrid is a gem for rendering grids described in simple DSL
|
111
|
+
email:
|
112
|
+
- davll.xc@gmail.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- Gemfile
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- declarative_grid.gemspec
|
123
|
+
- lib/declarative_grid.rb
|
124
|
+
- lib/declarative_grid/base.rb
|
125
|
+
- lib/declarative_grid/configuration.rb
|
126
|
+
- lib/declarative_grid/model.rb
|
127
|
+
- lib/declarative_grid/model/column.rb
|
128
|
+
- lib/declarative_grid/model/grid_class.rb
|
129
|
+
- lib/declarative_grid/model/row.rb
|
130
|
+
- lib/declarative_grid/renderers.rb
|
131
|
+
- lib/declarative_grid/renderers/abstract_renderer.rb
|
132
|
+
- lib/declarative_grid/renderers/csv.rb
|
133
|
+
- lib/declarative_grid/renderers/html_table.rb
|
134
|
+
- lib/declarative_grid/version.rb
|
135
|
+
- spec/declarative_grid/base_spec.rb
|
136
|
+
- spec/declarative_grid/model/column_spec.rb
|
137
|
+
- spec/declarative_grid/model/grid_class_spec.rb
|
138
|
+
- spec/declarative_grid/model/row_spec.rb
|
139
|
+
- spec/declarative_grid/renderers/abstract_renderer_spec.rb
|
140
|
+
- spec/declarative_grid_spec.rb
|
141
|
+
- spec/factories/models.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
homepage: https://github.com/davll/declarative_grid
|
144
|
+
licenses: []
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ~>
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: 1.9.0
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 1.8.24
|
164
|
+
signing_key:
|
165
|
+
specification_version: 3
|
166
|
+
summary: A flexible grid renderer
|
167
|
+
test_files:
|
168
|
+
- spec/declarative_grid/base_spec.rb
|
169
|
+
- spec/declarative_grid/model/column_spec.rb
|
170
|
+
- spec/declarative_grid/model/grid_class_spec.rb
|
171
|
+
- spec/declarative_grid/model/row_spec.rb
|
172
|
+
- spec/declarative_grid/renderers/abstract_renderer_spec.rb
|
173
|
+
- spec/declarative_grid_spec.rb
|
174
|
+
- spec/factories/models.rb
|
175
|
+
- spec/spec_helper.rb
|