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 ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ *~
4
+ .DS_Store
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in declarative_grid.gemspec
4
+ gemspec
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,6 @@
1
+ class DeclarativeGrid::Configuration
2
+
3
+ def initialize
4
+ end
5
+
6
+ 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,7 @@
1
+ module DeclarativeGrid::Model
2
+ extend ActiveSupport::Autoload
3
+
4
+ autoload :Column
5
+ autoload :Row
6
+ autoload :GridClass
7
+ 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,9 @@
1
+ module DeclarativeGrid::Renderers
2
+ extend ActiveSupport::Autoload
3
+
4
+ autoload :AbstractRenderer
5
+
6
+ autoload :Csv
7
+ autoload :HtmlTable
8
+
9
+ end
@@ -0,0 +1,3 @@
1
+ module DeclarativeGrid
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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