datagrid 0.0.3

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.
@@ -0,0 +1,7 @@
1
+ class Datagrid::Filters::BooleanFilter < Datagrid::Filters::BaseFilter
2
+
3
+ def format(value)
4
+ [true, 1, "1", "true", "yes", "on"].include?(value)
5
+ end
6
+
7
+ end
@@ -0,0 +1,41 @@
1
+ module Datagrid
2
+ module Filters
3
+ module CompositeFilters
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.class_eval do
8
+
9
+ end
10
+ base.send :include, InstanceMethods
11
+ end # self.included
12
+
13
+ module ClassMethods
14
+
15
+
16
+ def date_range_filters(field, from_name = :"from_#{field}", to_name = :"to_#{field}")
17
+ filter(from_name, :date) do |date|
18
+ self.from_date(date, field)
19
+ end
20
+ filter(to_name, :date) do |date|
21
+ self.to_date(date, field)
22
+ end
23
+ end
24
+
25
+ def integer_range_filters(field, from_name = :"from_#{field}", to_name = :"to_#{field}")
26
+ filter(from_name, :integer) do |value|
27
+ self.scoped(:conditions => "#{field} >= #{value}")
28
+ end
29
+ filter(to_name, :integer) do |value|
30
+ self.scoped(:conditions => "#{field} <= #{value}")
31
+ end
32
+ end
33
+ end # ClassMethods
34
+
35
+ module InstanceMethods
36
+
37
+ end # InstanceMethods
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ class Datagrid::Filters::DateFilter < Datagrid::Filters::BaseFilter
2
+ #TODO: more smart date normalizer
3
+ def format(value)
4
+ return value unless value.is_a?(String)
5
+ value.blank? ? nil : Date.parse(value)
6
+ rescue ArgumentError
7
+ nil
8
+ end
9
+ end
10
+
@@ -0,0 +1,5 @@
1
+ class Datagrid::Filters::DefaultFilter < Datagrid::Filters::BaseFilter
2
+ def format(value)
3
+ value
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ class Datagrid::Filters::EnumFilter < Datagrid::Filters::BaseFilter
2
+
3
+ def initialize(*args)
4
+ super(*args)
5
+ raise Datagrid::ConfigurationError, ":select option not specified" unless select
6
+ end
7
+ def format(value)
8
+ values = Array.new([*value])
9
+ values.reject do |value|
10
+ #TODO: really impelement #strict option
11
+ self.strict && !select.include?(value)
12
+ end
13
+ self.multiple ? values : values.first
14
+ end
15
+
16
+ def select
17
+ self.options[:select]
18
+ end
19
+
20
+
21
+ def include_blank
22
+ self.options.has_key?(:include_blank) ? options[:include_blank] : true
23
+ end
24
+
25
+ def strict
26
+ self.options[:strict]
27
+ end
28
+
29
+ def multiple
30
+ self.options[:multiple]
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ # ActiveRecord is a little brain fuck.
2
+ # We can not call instance_eval on ActiveRecord::Relation class
3
+ # because it will automatically convert it to an array because #instance_eval
4
+ # is not included in the method list that do not cause force result loading
5
+ # That is why we need thi helper class
6
+ class Datagrid::Filters::FilterEval
7
+
8
+ def initialize(filter, scope, value)
9
+ @filter = filter
10
+ @scope = scope
11
+ @value = value
12
+ end
13
+
14
+ def run
15
+ instance_exec @value, &(@filter.block)
16
+ end
17
+
18
+ def method_missing(meth, *args, &blk)
19
+ if @scope.respond_to?(meth)
20
+ @scope.send(meth, *args, &blk)
21
+ else
22
+ super(meth, *args, &blk)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ class Datagrid::Filters::IntegerFilter < Datagrid::Filters::BaseFilter
2
+ def format(value)
3
+ return nil if value.blank?
4
+ value.to_i
5
+ end
6
+ end
7
+
@@ -0,0 +1,56 @@
1
+ require "action_view"
2
+
3
+ module Datagrid
4
+ module FormBuilder
5
+
6
+ def datagrid_filter(filter_or_attribute, options = {})
7
+ filter = get_filter(filter_or_attribute)
8
+ options[:class] ||= ""
9
+ options[:class] += " " unless options[:class].blank?
10
+ options[:class] += "#{filter.name} #{datagrid_filter_class(filter.class)}"
11
+ self.send(:"datagrid_#{filter.class.to_s.underscore.split('/').last}", filter, options)
12
+ end
13
+
14
+ protected
15
+ def datagrid_boolean_enum_filter(attribute_or_filter, options = {})
16
+ datagrid_enum_filter(attribute_or_filter, options)
17
+ end
18
+
19
+ def datagrid_boolean_filter(attribute_or_filter, options = {})
20
+ check_box(get_attribute(attribute_or_filter), options)
21
+ end
22
+
23
+ def datagrid_date_filter(attribute_or_filter, options = {})
24
+ attribute = get_attribute(attribute_or_filter)
25
+ text_field(attribute, options)
26
+ end
27
+
28
+ def datagrid_default_filter(attribute_or_filter, options = {})
29
+ text_field get_attribute(attribute_or_filter), options
30
+ end
31
+
32
+ def datagrid_enum_filter(attribute_or_filter, options = {})
33
+ filter = get_filter(attribute_or_filter)
34
+ select filter.name, filter.select || [], {:include_blank => filter.include_blank}, {:multiple => filter.multiple}.merge(options)
35
+ end
36
+
37
+ def datagrid_integer_filter(attribute_or_filter, options = {})
38
+ text_field get_attribute(attribute_or_filter), options
39
+ end
40
+
41
+ def get_attribute(attribute_or_filter)
42
+ attribute_or_filter.is_a?(Symbol) ? attribute_or_filter : attribute_or_filter.name
43
+ end
44
+
45
+ def get_filter(attribute_or_filter)
46
+ attribute_or_filter.is_a?(Symbol) ? object.class.filter_by_name(attribute_or_filter) : attribute_or_filter
47
+ end
48
+
49
+ def datagrid_filter_class(klass)
50
+ klass.to_s.split("::").last.underscore
51
+ end
52
+ end
53
+ end
54
+
55
+ ActionView::Helpers::FormBuilder.send(:include, Datagrid::FormBuilder)
56
+
@@ -0,0 +1,80 @@
1
+ require "action_view"
2
+
3
+ module Datagrid
4
+ module Helper
5
+
6
+ def datagrid_format_value(column, asset)
7
+ value = column.value(asset)
8
+ if column.options[:url]
9
+ link_to(value, column.options[:url].call(asset))
10
+ else
11
+ case column.format
12
+ when :url
13
+ link_to(column.label ? asset.send(column.label) : I18n.t("datagrid.table.url_label", :default => "URL"), value)
14
+ else
15
+ value
16
+ end
17
+ end
18
+ end
19
+
20
+ def datagrid_table(report, *args)
21
+ options = args.extract_options!
22
+ html = options[:html] || {}
23
+ html[:class] ||= "datagrid"
24
+ paginate = options[:paginate] || {}
25
+ paginate[:page] ||= params[:page]
26
+ assets = report.assets.paginate(paginate)
27
+ content_tag(:table, html) do
28
+ table = content_tag(:tr, datagrid_header(report, options))
29
+ table << datagrid_rows(report.columns, assets, options)
30
+ table
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def datagrid_header(grid, options)
37
+ header = empty_string
38
+ grid.columns.each do |column|
39
+ data = column.header.html_safe
40
+ if column.order
41
+ data << datagrid_order_for(grid, column)
42
+ end
43
+ header << content_tag(:th, data)
44
+ end
45
+ header
46
+ end
47
+
48
+ def datagrid_rows(columns, assets, options)
49
+ rows = empty_string
50
+ assets.each do |asset|
51
+ rows << content_tag(:tr, :class => cycle("odd", "even")) do
52
+ html = empty_string
53
+ columns.each do |column|
54
+ html << content_tag(:td, datagrid_format_value(column, asset))
55
+ end
56
+ html
57
+ end
58
+
59
+ end
60
+ rows
61
+ end
62
+
63
+ def datagrid_order_for(grid, column)
64
+ content_tag(:div, :class => "order") do
65
+ link_to(
66
+ I18n.t("datagrid.table.order.asc", :default => "ASC"), url_for(grid.param_name => grid.attributes.merge(:order => column.name))
67
+ ) + " " +
68
+ link_to(I18n.t("datagrid.table.order.desc", :default => "DESC"), url_for(grid.param_name => grid.attributes.merge(:order => column.name, :reverse => true )))
69
+ end
70
+ end
71
+
72
+ def empty_string
73
+ res = ""
74
+ res.respond_to?(:html_safe) ? res.html_safe : res
75
+ end
76
+ end
77
+
78
+ ::ActionView::Base.send(:include, ::Datagrid::Helper)
79
+
80
+ end
@@ -0,0 +1,68 @@
1
+ shared_examples_for "Datagrid" do
2
+ describe "as Datagrid" do
3
+
4
+ it "should have at least one entry if assets" do
5
+ subject.assets.should_not be_empty
6
+ end
7
+
8
+ its(:data) {should_not be_empty}
9
+
10
+ described_class.columns.each do |column|
11
+ describe "column ##{column.name}" do
12
+
13
+ it "should has value in #data_hash" do
14
+ subject.data_hash.first.should have_key(column.name)
15
+ end
16
+
17
+ it "should support order" do
18
+ subject.order = column.name
19
+ subject.assets.first.should_not be_nil
20
+ end
21
+
22
+ it "should support reverse order" do
23
+ subject.reverse = true
24
+ subject.assets.first.should_not be_nil
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ described_class.filters.each do |filter|
31
+ describe "filter ##{filter.name}" do
32
+
33
+ let(:filter_value) do
34
+
35
+ case Datagrid::Filters::FILTER_TYPES.invert[filter.class]
36
+ when :default, :string
37
+ "text"
38
+ when :date
39
+ 1.day.ago
40
+ when :eboolean
41
+ Datagrid::Filters::BooleanEnumFilter::YES
42
+ when :boolean
43
+ true
44
+ when :integer
45
+ 1
46
+ when :enum
47
+ select = filter.select
48
+ select = select.call(subject) if select.respond_to?(:call)
49
+ select.first.try(:last)
50
+ else
51
+ raise "unknown filter type: #{filter.class}"
52
+ end.to_s
53
+ end
54
+
55
+ before(:each) do
56
+ subject.attributes = {filter.name => filter_value}
57
+ subject.send(filter.name).should_not be_nil
58
+ end
59
+
60
+ it "should be supported" do
61
+ subject.assets.should_not be_nil
62
+ #TODO: better matcher.
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ class MyFormBuilder
5
+ include Datagrid::FormBuilder
6
+ end
7
+
8
+ class MyTemplate
9
+ include ActionView::Helpers::FormHelper
10
+ end
11
+
12
+
13
+ describe Datagrid::FormBuilder do
14
+
15
+ let(:template) { ActionView::Base.new}
16
+ let(:grid) { SimpleReport.new }
17
+ let(:view) { ActionView::Helpers::FormBuilder.new(:report, grid, template, {}, Proc.new {|f| })}
18
+ subject { view }
19
+
20
+
21
+ describe ".datagrid_filter" do
22
+
23
+ subject { view.datagrid_filter(_filter)}
24
+ context "with default filter type" do
25
+ let(:_filter) { :name }
26
+ it { should equal_to_dom(
27
+ '<input class="name default_filter" id="report_name" name="report[name]" size="30" type="text"/>'
28
+ )}
29
+ end
30
+ context "with integer filter type" do
31
+ let(:_filter) { :group_id }
32
+ it { should equal_to_dom(
33
+ '<input class="group_id integer_filter" id="report_group_id" name="report[group_id]" size="30" type="text"/>'
34
+ )}
35
+ end
36
+ context "with enum filter type" do
37
+ let(:_filter) { :category }
38
+ it { should equal_to_dom(
39
+ '<select class="category enum_filter" id="report_category" name="report[category][]"><option value=""></option>
40
+ <option value="first">first</option>
41
+ <option value="second">second</option></select>'
42
+ )}
43
+ context "when first option is selected" do
44
+ before(:each) do
45
+ grid.category = "first"
46
+ end
47
+ it { should equal_to_dom(
48
+ '<select class="category enum_filter" id="report_category" name="report[category][]"><option value=""></option>
49
+ <option value="first" selected="true">first</option>
50
+ <option value="second">second</option></select>'
51
+ )}
52
+ end
53
+ end
54
+
55
+ context "with eboolean filter type" do
56
+ let(:_filter) { :disabled }
57
+ it { should equal_to_dom(
58
+ '<select class="disabled boolean_enum_filter" id="report_disabled" name="report[disabled][]"><option value=""></option>
59
+ <option value="NO">NO</option>
60
+ <option value="YES">YES</option></select>'
61
+ )}
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+
68
+
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require "will_paginate"
3
+ require "active_support/core_ext/hash"
4
+ require "active_support/core_ext/object"
5
+
6
+ describe Datagrid::Helper do
7
+ subject {ActionView::Base.new}
8
+
9
+ before(:each) do
10
+ subject.stub!(:params).and_return({})
11
+ subject.stub(:url_for) do |options|
12
+ options.to_param
13
+ end
14
+
15
+ end
16
+
17
+ let(:group) { Group.create!(:name => "Pop") }
18
+ let!(:entry) { Entry.create!(
19
+ :group => group, :name => "Star", :disabled => false, :confirmed => false, :category => "first"
20
+ ) }
21
+ let(:grid) { SimpleReport.new }
22
+
23
+ describe ".report_table" do
24
+ before(:each) do
25
+ subject.stub!(:datagrid_order_for).and_return(subject.content_tag(:div, "", :class => "order"))
26
+ end
27
+ it "should return data table html" do
28
+ subject.datagrid_table(grid).should equal_to_dom(
29
+ '<table class="datagrid">
30
+ <tr>
31
+ <th>Group<div class="order"></div>
32
+ </th>
33
+ <th>Name<div class="order"></div>
34
+ </th>
35
+ </tr>
36
+
37
+ <tr class="odd">
38
+ <td>Pop</td>
39
+ <td>Star</td>
40
+ </tr>
41
+ </table>')
42
+ end
43
+ end
44
+
45
+
46
+ end