Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Display 'No records found.' if there are no results returned. #41

Merged
merged 22 commits into from

3 participants

@barendt

Would you be open to incorporating something like this?

If so, please let me know if you'd like me to make any changes to the implementation. Also, do you have any suggestions on specs you'd like to see?

@bogdan
Owner

I like it in general

But there is a localization problem.
Can we use any symbolic representation of 'No records found.' ? For example long dash or something else.

Please add one spec somewhere in here https://github.com/bogdan/datagrid/blob/master/spec/datagrid/helper_spec.rb

@barendt

I changed this to use I18n.translate, is that a better solution?

Also, can you give me any advice on writing this spec? I'm having a hard time coming up with a good way to create a report within the context of helper_spec.rb that doesn't have any records in it.

@bogdan
Owner

I18n.translate is ok.

Can we try —— (——) as a default?
It's localization agnostic and probably would fit more projects out of the box.
We need to ensure that it looks nice in browser.

Take a look at http://gusiev.com/dorspec/#1 if you want to get started with advanced rspec features.
Test case:

context "when grid has no records" do
let(:grid) do
  test_report do 
    scope {Entry.where("1 != 1")}
  end
end

it "should show empty table" do
 datagrid_table = subject.datagrid_table(grid)

 datagrid_table.should match_css_pattern(...)
end
end
@barendt

Thanks! I pushed a spec for this. I also changed this to use —— as the default. I think it looks nice, but let me know if you'd like me to try something else.

@bogdan bogdan merged commit f32adef into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 3, 2012
  1. @barendt
Commits on Aug 7, 2012
  1. @barendt

    Use HTML entity arrows as default sort links.

    barendt authored barendt committed
  2. @barendt

    Update the ordering layout spec.

    barendt authored barendt committed
  3. @barendt

    Float filter bugfix. Fixes #34.

    authored barendt committed
  4. @barendt

    Remove unused method

    authored barendt committed
  5. @swlkr @barendt

    Fixed an issue with mongoid version 3.

    swlkr authored barendt committed
  6. @barendt

    Version bump to 0.6.1

    authored barendt committed
  7. @barendt

    Regenerate gemspec for version 0.6.1

    authored barendt committed
  8. @barendt

    Version bump to 0.6.2

    authored barendt committed
  9. @barendt

    Regenerate gemspec for version 0.6.2

    authored barendt committed
  10. @barendt

    datagrid_table: add :columns option

    authored barendt committed
  11. @barendt

    Add some doc

    authored barendt committed
  12. @barendt

    Fix gem for ruby 1.8.7

    authored barendt committed
  13. @barendt

    Support inheritence of columns

    authored barendt committed
  14. @barendt

    Make grids inheritable

    authored barendt committed
  15. @barendt

    Use class attribute for datagrid_attributes

    authored barendt committed
  16. @barendt

    Improve drivers

    authored barendt committed
  17. @barendt
  18. @barendt

    Add localization for eboolean filter

    authored barendt committed
  19. @barendt
Commits on Aug 17, 2012
  1. @barendt
  2. @barendt
This page is out of date. Refresh to see the latest.
View
2  Gemfile
@@ -10,7 +10,7 @@ group :development do
gem "bundler", "~> 1.1.0"
gem "jeweler", "~> 1.6.0"
- gem "debugger"
+ gem "debugger", :platform => "ruby_19"
gem "rspec"
gem "accept_values_for"
View
2  VERSION
@@ -1 +1 @@
-0.6.0
+0.6.2
View
2  app/views/datagrid/_head.html.erb
@@ -1,5 +1,5 @@
<tr>
- <% grid.columns.each do |column| %>
+ <% grid.columns(*options[:columns]).each do |column| %>
<th class="<%= datagrid_column_classes(grid, column) %>">
<%= column.header %>
<%= datagrid_order_for(grid, column) if column.order && options[:order]%>
View
2  app/views/datagrid/_row.html.erb
@@ -1,5 +1,5 @@
<tr class="<%= options[:cycle] && cycle(*options[:cycle]) %>">
- <% grid.columns.each do |column| %>
+ <% grid.columns(*options[:columns]).each do |column| %>
<td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_format_value(grid, column, asset) %></td>
<% end %>
</tr>
View
4 app/views/datagrid/_table.html.erb
@@ -9,6 +9,10 @@ Local variables:
<%= datagrid_header(grid, options) %>
</thead>
<tbody>
+ <% if grid.rows.empty? %>
+ <tr><td class="noresults" colspan="100%"><%= I18n.t 'datagrid.no_results', :default => '&mdash;&mdash;'.html_safe %></td></tr>
+ <% else %>
<%= datagrid_rows(grid, assets, options) %>
+ <% end %>
</tbody>
<% end %>
View
7 datagrid.gemspec
@@ -5,11 +5,11 @@
Gem::Specification.new do |s|
s.name = "datagrid"
- s.version = "0.6.0"
+ s.version = "0.6.2"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Bogdan Gusiev"]
- s.date = "2012-05-24"
+ s.date = "2012-06-22"
s.description = "This allows you to easily build datagrid aka data tables with sortable columns and filters"
s.email = "agresso@gmail.com"
s.extra_rdoc_files = [
@@ -46,6 +46,7 @@ Gem::Specification.new do |s|
"lib/datagrid/filters/date_filter.rb",
"lib/datagrid/filters/default_filter.rb",
"lib/datagrid/filters/enum_filter.rb",
+ "lib/datagrid/filters/float_filter.rb",
"lib/datagrid/filters/integer_filter.rb",
"lib/datagrid/filters/string_filter.rb",
"lib/datagrid/form_builder.rb",
@@ -57,12 +58,14 @@ Gem::Specification.new do |s|
"lib/tasks/datagrid_tasks.rake",
"spec/datagrid/active_model_spec.rb",
"spec/datagrid/columns_spec.rb",
+ "spec/datagrid/core_spec.rb",
"spec/datagrid/drivers/active_record_spec.rb",
"spec/datagrid/drivers/mongo_mapper_spec.rb",
"spec/datagrid/drivers/mongoid_spec.rb",
"spec/datagrid/filters/boolean_enum_filter_spec.rb",
"spec/datagrid/filters/composite_filters_spec.rb",
"spec/datagrid/filters/enum_filter_spec.rb",
+ "spec/datagrid/filters/float_filter_spec.rb",
"spec/datagrid/filters_spec.rb",
"spec/datagrid/form_builder_spec.rb",
"spec/datagrid/helper_spec.rb",
View
28 lib/datagrid/columns.rb
@@ -1,4 +1,5 @@
require "datagrid/utils"
+require "active_support/core_ext/class/attribute"
module Datagrid
@@ -10,6 +11,8 @@ def self.included(base)
base.class_eval do
include Datagrid::Core
+ class_attribute :columns_array
+ self.columns_array = []
end
base.send :include, InstanceMethods
@@ -17,9 +20,12 @@ def self.included(base)
module ClassMethods
- def columns(options = {})
- (@columns ||= []).reject do |column|
- options[:data] && column.html?
+ def columns(*args)
+ options = args.extract_options!
+ args.compact!
+ args.map!(&:to_sym)
+ columns_array.select do |column|
+ (!options[:data] || column.data?) && (args.empty? || args.include?(column.name))
end
end
@@ -28,8 +34,7 @@ def column(name, options = {}, &block)
block ||= lambda do |model|
model.send(name)
end
- @columns ||= []
- @columns << Datagrid::Columns::Column.new(self, name, options, &block)
+ columns_array << Datagrid::Columns::Column.new(self, name, options, &block)
end
def column_by_name(name)
@@ -37,6 +42,12 @@ def column_by_name(name)
col.name.to_sym == name.to_sym
end
end
+
+ def inherited(child_class)
+ super(child_class)
+ child_class.columns_array = self.columns_array.clone
+ end
+
end # ClassMethods
module InstanceMethods
@@ -62,16 +73,19 @@ def hash_for(asset)
result
end
+ # Returns Array of Arrays with data for each row in datagrid assets without header.
def rows
self.assets.map do |asset|
self.row_for(asset)
end
end
+ # Returns Array of Arrays with data for each row in datagrid assets with header.
def data
self.rows.unshift(self.header)
end
+ # Return Array of Hashes where keys are column names and values are column values for each row in datagrid <tt>#assets</tt>
def data_hash
self.assets.map do |asset|
hash_for(asset)
@@ -95,8 +109,8 @@ def to_csv(options = {})
end
end
- def columns(options ={})
- self.class.columns(options)
+ def columns(*args)
+ self.class.columns(*args)
end
def data_columns
View
4 lib/datagrid/columns/column.rb
@@ -52,10 +52,12 @@ def order_desc
self.options[:order_desc]
end
-
def html?
!! self.options[:html]
end
+ def data?
+ !html?
+ end
end
View
34 lib/datagrid/core.rb
@@ -1,4 +1,5 @@
require "datagrid/drivers"
+require "active_support/core_ext/class/attribute"
module Datagrid
module Core
@@ -6,7 +7,9 @@ module Core
def self.included(base)
base.extend ClassMethods
base.class_eval do
-
+ class_attribute :scope_value
+ class_attribute :datagrid_attributes
+ self.datagrid_attributes = []
end
base.send :include, InstanceMethods
end # self.included
@@ -29,16 +32,12 @@ def datagrid_attribute(name, &block)
end
end
- def datagrid_attributes
- @datagrid_attributes ||= []
- end
-
def scope(&block)
if block
- @scope = block
+ self.scope_value = block
else
check_scope_defined!
- @scope.call
+ scope_value.call
end
end
@@ -47,8 +46,14 @@ def driver
end
protected
- def check_scope_defined!(message = "Scope not defined")
- raise(Datagrid::ConfigurationError, message) unless @scope
+ def check_scope_defined!(message = nil)
+ message ||= "Scope not defined"
+ raise(Datagrid::ConfigurationError, message) unless scope_value
+ end
+
+ def inherited(child_class)
+ super(child_class)
+ child_class.datagrid_attributes = self.datagrid_attributes.clone
end
end # ClassMethods
@@ -65,7 +70,7 @@ def initialize(attributes = nil)
def attributes
result = {}
- self.class.datagrid_attributes.each do |name|
+ self.datagrid_attributes.each do |name|
result[name] = self[name]
end
result
@@ -96,9 +101,10 @@ def paginate(*args, &block)
def scope(&block)
if block_given?
- @current_scope = block
+ self.scope_value = block
else
- @current_scope ? @current_scope.call : self.class.scope
+ check_scope_defined!
+ scope_value.call
end
end
@@ -106,8 +112,8 @@ def driver
self.class.driver
end
- def check_scope_defined!(message)
- self.class.check_scope_defined!(message)
+ def check_scope_defined!(message = nil)
+ self.class.send :check_scope_defined!, message
end
end # InstanceMethods
View
3  lib/datagrid/drivers/abstract_driver.rb
@@ -49,6 +49,9 @@ def less_equal(scope, field, value)
raise NotImplementedError
end
+ def has_column?(scope, column_name)
+ raise NotImplementedError
+ end
end
end
end
View
8 lib/datagrid/drivers/active_record.rb
@@ -30,7 +30,7 @@ def desc(scope, order)
end
def default_order(scope, column_name)
- scope.column_names.include?(column_name.to_s) ? [scope.table_name, column_name].join(".") : nil
+ has_column?(scope, column_name) ? [scope.table_name, column_name].join(".") : nil
end
def greater_equal(scope, field, value)
@@ -40,6 +40,12 @@ def greater_equal(scope, field, value)
def less_equal(scope, field, value)
scope.where(["#{field} <= ?", value])
end
+
+ def has_column?(scope, column_name)
+ scope.column_names.include?(column_name.to_s)
+ rescue ::ActiveRecord::StatementInvalid
+ false
+ end
end
end
end
View
6 lib/datagrid/drivers/mongo_mapper.rb
@@ -28,7 +28,7 @@ def desc(scope, order)
end
def default_order(scope, column_name)
- scope.key?(column_name) ? column_name : nil
+ has_column?(scope, column_name) ? column_name : nil
end
def greater_equal(scope, field, value)
@@ -38,6 +38,10 @@ def greater_equal(scope, field, value)
def less_equal(scope, field, value)
scope.where(field => {"$lte" => value})
end
+
+ def has_column?(scope, column_name)
+ scope.key?(column_name)
+ end
end
end
end
View
8 lib/datagrid/drivers/mongoid.rb
@@ -12,7 +12,7 @@ def self.match?(scope)
end
def to_scope(scope)
- scope.where
+ scope.where(nil)
end
def where(scope, condition)
@@ -28,7 +28,7 @@ def desc(scope, order)
end
def default_order(scope, column_name)
- to_scope(scope).klass.fields.keys.include?(column_name.to_s) ? column_name : nil
+ has_column?(scope, column_name) ? column_name : nil
end
def greater_equal(scope, field, value)
@@ -38,6 +38,10 @@ def greater_equal(scope, field, value)
def less_equal(scope, field, value)
scope.where(field => {"$lte" => value})
end
+
+ def has_column?(scope, column_name)
+ to_scope(scope).klass.fields.keys.include?(column_name.to_s)
+ end
end
end
end
View
24 lib/datagrid/filters.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/class/attribute"
+
module Datagrid
module Filters
@@ -29,6 +31,8 @@ def self.included(base)
include Datagrid::Core
include Datagrid::Filters::CompositeFilters
+ class_attribute :filters
+ self.filters = []
end
base.send :include, InstanceMethods
@@ -36,17 +40,12 @@ def self.included(base)
module ClassMethods
- def filters
- @filters ||= []
- end
-
def filter_by_name(attribute)
self.filters.find do |filter|
filter.name.to_sym == attribute.to_sym
end
end
-
def filter(attribute, *args, &block)
options = args.extract_options!
type = args.shift || :default
@@ -68,11 +67,22 @@ def filter(attribute, *args, &block)
protected
def default_filter(attribute)
check_scope_defined!("Scope should be defined before filters")
- lambda do |value, scope, grid|
- grid.driver.where(scope, attribute => value)
+ if !driver.has_column?(scope, attribute) && driver.to_scope(scope).respond_to?(attribute)
+ lambda do |value, scope, grid|
+ grid.driver.to_scope(scope).send(attribute, value)
+ end
+ else
+ lambda do |value, scope, grid|
+ grid.driver.where(scope, attribute => value)
+ end
end
end
+ def inherited(child_class)
+ super(child_class)
+ child_class.filters = self.filters.clone
+ end
+
end # ClassMethods
module InstanceMethods
View
8 lib/datagrid/filters/base_filter.rb
@@ -63,5 +63,13 @@ def allow_blank?
options[:allow_blank]
end
+ def form_builder_helper_name
+ self.class.form_builder_helper_name
+ end
+
+ def self.form_builder_helper_name
+ :"datagrid_#{self.to_s.demodulize.underscore}"
+ end
+
end
View
11 lib/datagrid/filters/boolean_enum_filter.rb
@@ -2,21 +2,20 @@ class Datagrid::Filters::BooleanEnumFilter < Datagrid::Filters::EnumFilter
YES = "YES"
NO = "NO"
- VALUES = ActiveSupport::OrderedHash.new
- VALUES[YES] = YES
- VALUES[NO] = NO
def initialize(report, attribute, options = {}, &block)
- options[:select] = VALUES.keys
+ options[:select] = [YES, NO].map do |key, value|
+ [I18n.t("datagrid.filters.eboolean.#{key.downcase}", :default => key.humanize), key]
+ end
super(report, attribute, options, &block)
end
def apply(grid_object, scope, value)
- super(grid_object, scope, to_boolean(value))
+ super(grid_object, scope, value)
end
def to_boolean(value)
- VALUES[value]
+ #TODO decide what to do with conversion
end
end
View
10 lib/datagrid/form_builder.rb
@@ -8,7 +8,7 @@ def datagrid_filter(filter_or_attribute, options = {})
options[:class] ||= ""
options[:class] += " " unless options[:class].blank?
options[:class] += "#{filter.name} #{datagrid_filter_html_class(filter)}"
- self.send(datagrid_filter_method(filter), filter, options)
+ self.send(filter.form_builder_helper_name, filter, options)
end
def datagrid_label(filter_or_attribute, options = {})
@@ -54,6 +54,10 @@ def datagrid_string_filter(attribute_or_filter, options = {})
datagrid_default_filter(attribute_or_filter, options)
end
+ def datagrid_float_filter(attribute_or_filter, options = {})
+ datagrid_default_filter(attribute_or_filter, options)
+ end
+
def get_attribute(attribute_or_filter)
attribute_or_filter.is_a?(Symbol) ? attribute_or_filter : attribute_or_filter.name
end
@@ -71,10 +75,6 @@ def datagrid_filter_html_class(filter)
filter.class.to_s.demodulize.underscore
end
- def datagrid_filter_method(filter)
- :"datagrid_#{filter.class.to_s.demodulize.underscore}"
- end
-
class Error < StandardError
end
end
View
4 lib/datagrid/renderer.rb
@@ -65,11 +65,11 @@ def rows(grid, assets, options = {})
def order_for(grid, column)
@template.content_tag(:div, :class => "order") do
@template.link_to(
- I18n.t("datagrid.table.order.asc", :default => "ASC"),
+ I18n.t("datagrid.table.order.asc", :default => "&uarr;".html_safe),
@template.url_for(grid.param_name => grid.attributes.merge(:order => column.name, :descending => false)),
:class => "order asc"
) + " " + @template.link_to(
- I18n.t("datagrid.table.order.desc", :default => "DESC"),
+ I18n.t("datagrid.table.order.desc", :default => "&darr;".html_safe),
@template.url_for(grid.param_name => grid.attributes.merge(:order => column.name, :descending => true )),
:class => "order desc"
)
View
15 spec/datagrid/columns_spec.rb
@@ -60,5 +60,20 @@
report.rows.last.first.should be_false
end
+ it "should inherit columns correctly" do
+ parent = Class.new do
+ include Datagrid
+ scope { Entry }
+ column(:name)
+ end
+
+ child = Class.new(parent) do
+ column(:group_id)
+ end
+ parent.column_by_name(:name).should_not be_nil
+ parent.column_by_name(:group_id).should be_nil
+ child.column_by_name(:name).should_not be_nil
+ child.column_by_name(:group_id).should_not be_nil
+ end
end
View
2  spec/datagrid/filters/boolean_enum_filter_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe Datagrid::Filters::BooleanEnumFilter do
-
+
end
View
28 spec/datagrid/filters_spec.rb
@@ -33,7 +33,7 @@
report = test_report do
scope {Entry}
filter(:group_id, :integer) do |value, scope|
- scope.where(group_id: value)
+ scope.where(:group_id => value)
end
end
lambda {
@@ -55,6 +55,19 @@ class TheReport
TheReport.new(:name => 'hello')
end
+ it "should support inheritence" do
+ parent = Class.new do
+ include Datagrid
+ scope {Entry}
+ filter(:name)
+ end
+ child = Class.new(parent) do
+ filter(:group_id)
+ end
+ parent.filters.size.should == 1
+ child.filters.size.should == 2
+ end
+
describe "allow_blank and allow_nil options" do
def check_performed(value, result, options)
@@ -87,4 +100,17 @@ def check_performed(value, result, options)
check_performed(nil, true, :allow_nil => true, :allow_blank => false)
end
end
+
+ describe "default filter as scope" do
+ it "should create default filter if scope respond to filter name method" do
+ Entry.create!
+ Entry.create!
+ grid = test_report(:limit => 1) do
+ scope {Entry}
+ filter(:limit)
+ end
+ grid.assets.to_a.size.should == 1
+ end
+
+ end
end
View
23 spec/datagrid/form_builder_spec.rb
@@ -19,6 +19,12 @@ class MyTemplate
describe ".datagrid_filter" do
+ it "should work for every filter type" do
+ Datagrid::Filters::FILTER_TYPES.each do |type, klass|
+ Datagrid::FormBuilder.instance_methods.map(&:to_sym).should include(klass.form_builder_helper_name)
+ end
+ end
+
subject { view.datagrid_filter(_filter)}
context "with default filter type" do
let(:_grid) {
@@ -97,8 +103,8 @@ class MyTemplate
end
it { should equal_to_dom(
'<select class="disabled boolean_enum_filter" id="report_disabled" name="report[disabled]"><option value=""></option>
- <option value="YES">YES</option>
- <option value="NO">NO</option></select>'
+ <option value="YES">Yes</option>
+ <option value="NO">No</option></select>'
)}
end
context "with string filter" do
@@ -129,6 +135,19 @@ class MyTemplate
let(:_filter) { :name }
it {should equal_to_dom('<select class="name enum_filter" id="report_name" name="report[name]"></select>')}
end
+ context "with float filter type" do
+ let(:_grid) {
+ test_report do
+ scope {Entry}
+ filter(:group_id, :float)
+ end
+ }
+ let(:_filter) { :group_id }
+ it { should equal_to_dom(
+ '<input class="group_id float_filter" id="report_group_id" name="report[group_id]" size="30" type="text"/>'
+ )}
+
+ end
end
describe ".datagrid_label" do
View
43 spec/datagrid/helper_spec.rb
@@ -26,6 +26,22 @@
) }
let(:grid) { SimpleReport.new }
+ context "when grid has no records" do
+ let(:grid) do
+ test_report do
+ scope { Entry.where("1 != 1") }
+ end
+ end
+
+ it "should show an empty table with dashes" do
+ datagrid_table = subject.datagrid_table(grid)
+
+ datagrid_table.should match_css_pattern(
+ "table.datagrid tr td.noresults" => 1
+ )
+ end
+ end
+
describe ".datagrid_table" do
it "should have grid class as html class on table" do
subject.datagrid_table(grid).should match_css_pattern(
@@ -71,6 +87,15 @@
subject.datagrid_table(grid, [entry], :order => false).should match_css_pattern("table.datagrid th .order" => 0)
end
+ it "should support columns option" do
+ subject.datagrid_table(grid, [entry], :columns => [:name]).should match_css_pattern(
+ "table.datagrid th.name" => 1,
+ "table.datagrid td.name" => 1,
+ "table.datagrid th.group" => 0,
+ "table.datagrid td.group" => 0
+ )
+ end
+
describe ".datagrid_rows" do
it "should support urls" do
@@ -137,10 +162,24 @@
)
end
+ it "should support columns option" do
+ rp = test_report do
+ scope { Entry }
+ column(:name)
+ column(:category)
+ end
+ subject.datagrid_rows(rp, [entry], :columns => [:name]).should match_css_pattern(
+ "tr td.name" => "Star"
+ )
+ subject.datagrid_rows(rp, [entry], :columns => [:name]).should match_css_pattern(
+ "tr td.category" => 0
+ )
+ end
+
end
describe ".datagrid_order_for" do
- it "should render ordreing layout" do
+ it "should render ordering layout" do
class OrderedGrid
include Datagrid
scope { Entry }
@@ -149,7 +188,7 @@ class OrderedGrid
grid = OrderedGrid.new(:descending => true, :order => :category)
subject.datagrid_order_for(grid, grid.column_by_name(:category)).should equal_to_dom(<<-HTML)
<div class="order">
-<a href="ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category" class="order asc">ASC</a> <a href="ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category" class="order desc">DESC</a>
+<a href="ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category" class="order asc">&uarr;</a> <a href="ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category" class="order desc">&darr;</a>
</div>
HTML
end
View
4 spec/support/matchers.rb
@@ -27,6 +27,7 @@ def matches?(text)
def failure_message
"Expected dom \n#{@matcher.inspect}\n to include \n#{@expectation.inspect}\n, but it wasn't"
end
+
end
@@ -90,4 +91,7 @@ def matches?(text)
end
end
+ def negative_failure_message
+ "Expected do not match dom pattern. But it was"
+ end
end
Something went wrong with that request. Please try again.