declarative_grid 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|