datagrid 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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