sorting_table_for 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.mdown +4 -0
- data/init.rb +0 -4
- data/lib/sorting_table_for.rb +3 -0
- data/lib/sorting_table_for/format_cell.rb +189 -0
- data/lib/sorting_table_for/format_line.rb +110 -0
- data/lib/sorting_table_for/i18n.rb +16 -9
- data/lib/sorting_table_for/model_scope.rb +76 -0
- data/lib/sorting_table_for/table_builder.rb +15 -306
- data/spec/db/schema.rb +1 -1
- data/spec/fixtures/{user.rb → sorting_table_for_user.rb} +2 -2
- data/spec/helpers/builder_spec.rb +8 -5
- data/spec/helpers/caption_spec.rb +7 -4
- data/spec/helpers/cell_value_spec.rb +7 -4
- data/spec/helpers/column_spec.rb +14 -11
- data/spec/helpers/footer_spec.rb +7 -4
- data/spec/helpers/header_spec.rb +33 -8
- data/spec/helpers/i18n_spec.rb +12 -7
- data/spec/locales/test.yml +11 -2
- data/spec/locales/test_rails3.yml +6 -4
- data/spec/model/sorting_table_model_scope_spec.rb +24 -18
- data/spec/spec_helper.rb +18 -0
- metadata +7 -15
- data/lib/model/sorting_table_model_scope.rb +0 -72
@@ -6,8 +6,7 @@ module SortingTableFor
|
|
6
6
|
|
7
7
|
class TableBuilder
|
8
8
|
|
9
|
-
include ::ActionView::Helpers
|
10
|
-
include ::ActionView::Helpers::NumberHelper
|
9
|
+
include ::ActionView::Helpers
|
11
10
|
|
12
11
|
class_inheritable_accessor :reserved_columns, :currency_columns,
|
13
12
|
:default_boolean, :show_total_entries,
|
@@ -31,7 +30,7 @@ module SortingTableFor
|
|
31
30
|
def initialize(collection, object_or_array, template, options, params)
|
32
31
|
@collection, @@object_or_array, @@template, @@options, @@params = collection, object_or_array, template, options, params
|
33
32
|
set_default_global_options
|
34
|
-
I18n.set_options(params, model_name(@collection.first),
|
33
|
+
I18n.set_options(params, model_name(@collection.first), @@options[:i18n])
|
35
34
|
@lines = []
|
36
35
|
end
|
37
36
|
|
@@ -135,7 +134,7 @@ module SortingTableFor
|
|
135
134
|
column_options, html_options = get_column_and_html_options( args.extract_options! )
|
136
135
|
if block_given?
|
137
136
|
@header_line = FormatLine.new(args, column_options, html_options, nil, :thead)
|
138
|
-
|
137
|
+
capture(&block)
|
139
138
|
else
|
140
139
|
@header_line = FormatLine.new(args, column_options, html_options, @collection.first, :thead)
|
141
140
|
end
|
@@ -184,7 +183,7 @@ module SortingTableFor
|
|
184
183
|
#
|
185
184
|
def header(*args, &block)
|
186
185
|
if block_given?
|
187
|
-
block =
|
186
|
+
block = capture(&block)
|
188
187
|
@header_line.add_cell(@collection.first, args, nil, block)
|
189
188
|
else
|
190
189
|
@header_line.add_cell(@collection.first, args)
|
@@ -392,7 +391,7 @@ module SortingTableFor
|
|
392
391
|
#
|
393
392
|
def column(*args, &block)
|
394
393
|
if block_given?
|
395
|
-
block =
|
394
|
+
block = capture(&block)
|
396
395
|
@lines.last.add_cell(@current_object, args, nil, block)
|
397
396
|
else
|
398
397
|
@lines.last.add_cell(@current_object, args)
|
@@ -451,7 +450,7 @@ module SortingTableFor
|
|
451
450
|
column_options, html_options = get_column_and_html_options( args.extract_options! )
|
452
451
|
if block_given?
|
453
452
|
@footer_line = FormatLine.new(args, column_options, html_options, nil, :tfoot)
|
454
|
-
|
453
|
+
capture(&block)
|
455
454
|
else
|
456
455
|
@footer_line = FormatLine.new(args, column_options, html_options, @collection.first, :tfoot) if !args.empty?
|
457
456
|
end
|
@@ -506,7 +505,7 @@ module SortingTableFor
|
|
506
505
|
#
|
507
506
|
def footer(*args, &block)
|
508
507
|
if block_given?
|
509
|
-
block =
|
508
|
+
block = capture(&block)
|
510
509
|
@footer_line.add_cell(@collection.first, args, nil, block)
|
511
510
|
else
|
512
511
|
@footer_line.add_cell(@collection.first, args)
|
@@ -557,7 +556,7 @@ module SortingTableFor
|
|
557
556
|
def caption(*args, &block)
|
558
557
|
@caption[:option], @caption[:html] = get_column_and_html_options( args.extract_options! )
|
559
558
|
if block_given?
|
560
|
-
@caption[:value] =
|
559
|
+
@caption[:value] = capture(&block)
|
561
560
|
else
|
562
561
|
@caption[:value] = (args.empty?) ? I18n.t(:table_caption) : args.first;
|
563
562
|
end
|
@@ -571,6 +570,11 @@ module SortingTableFor
|
|
571
570
|
object.present? ? object.class.name : object.to_s.classify
|
572
571
|
end
|
573
572
|
|
573
|
+
# Send method to ActionView
|
574
|
+
def method_missing(name, *args, &block)
|
575
|
+
@@template.send(name, *args, &block)
|
576
|
+
end
|
577
|
+
|
574
578
|
private
|
575
579
|
|
576
580
|
def render_caption
|
@@ -595,6 +599,7 @@ module SortingTableFor
|
|
595
599
|
def render_tbody
|
596
600
|
if @lines and @lines.size > 0
|
597
601
|
return Tools::html_safe(content_tag(:tbody, render_total_entries + Tools::html_safe(@lines.collect { |line| line.render_line }.join)))
|
602
|
+
return Tools::html_safe(content_tag(:tr, content_tag(:td, I18n.t(:total_entries, :scope => :sorting_table_for, :value => total_entries), {:colspan => max_cells}), { :class => 'total-entries' }))
|
598
603
|
end
|
599
604
|
''
|
600
605
|
end
|
@@ -624,7 +629,7 @@ module SortingTableFor
|
|
624
629
|
total_entries = @collection.total_entries rescue @collection.size
|
625
630
|
header_total_cells = @header_line ? @header_line.total_cells : 0
|
626
631
|
max_cells = (@lines.first.total_cells > header_total_cells) ? @lines.first.total_cells : header_total_cells
|
627
|
-
return Tools::html_safe(content_tag(:tr, content_tag(:td, I18n.t(:total_entries, :
|
632
|
+
return Tools::html_safe(content_tag(:tr, content_tag(:td, I18n.t(:total_entries, :value => total_entries), {:colspan => max_cells}), { :class => 'total-entries' }))
|
628
633
|
end
|
629
634
|
''
|
630
635
|
end
|
@@ -635,300 +640,4 @@ module SortingTableFor
|
|
635
640
|
end
|
636
641
|
|
637
642
|
end
|
638
|
-
|
639
|
-
class FormatLine < TableBuilder
|
640
|
-
|
641
|
-
def initialize(args, column_options = {}, html_options = {}, object = nil, type = nil)
|
642
|
-
@args, @column_options, @html_options, @object, @type = args, column_options, html_options, object, type
|
643
|
-
@cells = []
|
644
|
-
if object
|
645
|
-
@attributes = (args.empty?) ? (get_columns - TableBuilder.reserved_columns) : @args
|
646
|
-
create_cells
|
647
|
-
end
|
648
|
-
end
|
649
|
-
|
650
|
-
# Create a new cell with the class FormatCell
|
651
|
-
# Add the object in @cells
|
652
|
-
def add_cell(object, args, type = nil, block = nil)
|
653
|
-
@cells << FormatCell.new(object, args, type, block)
|
654
|
-
end
|
655
|
-
|
656
|
-
# Return a tr line based on the type (:thead, :tbody or :tfoot)
|
657
|
-
def render_line
|
658
|
-
if @type == :thead
|
659
|
-
return content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_thead }.join), @html_options)
|
660
|
-
elsif @type == :tfoot
|
661
|
-
return content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_tfoot }.join), @html_options)
|
662
|
-
else
|
663
|
-
content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_tbody }.join), @html_options.merge(:class => "#{@html_options[:class]} #{@@template.cycle(:odd, :even)}".strip))
|
664
|
-
end
|
665
|
-
end
|
666
|
-
|
667
|
-
# Return the number of cells in line
|
668
|
-
def total_cells
|
669
|
-
@cells.size
|
670
|
-
end
|
671
|
-
|
672
|
-
protected
|
673
|
-
|
674
|
-
# Return each column in the model's database table
|
675
|
-
def content_columns
|
676
|
-
model_name(@object).constantize.content_columns.collect { |c| c.name.to_sym }.compact rescue []
|
677
|
-
end
|
678
|
-
|
679
|
-
# Return true if the column is in the model's database table
|
680
|
-
def model_have_column?(column)
|
681
|
-
model_name(@object).constantize.content_columns.each do |model_column|
|
682
|
-
return true if model_column.name == column.to_s
|
683
|
-
end
|
684
|
-
false
|
685
|
-
end
|
686
|
-
|
687
|
-
# Return true if the column is in the model's database table
|
688
|
-
def can_sort_column?(column)
|
689
|
-
model_have_column?(column)
|
690
|
-
end
|
691
|
-
|
692
|
-
# Options only for cells
|
693
|
-
def only_cell_option?(key)
|
694
|
-
[:colspan].include? key
|
695
|
-
end
|
696
|
-
|
697
|
-
# Format ask to send options to cell
|
698
|
-
def format_options_to_cell(ask, options = @column_options)
|
699
|
-
options.each do |key, value|
|
700
|
-
if only_cell_option?(key)
|
701
|
-
if ask.is_a? Hash
|
702
|
-
ask.merge!(key => value)
|
703
|
-
else
|
704
|
-
ask = [ask] unless ask.is_a? Array
|
705
|
-
(ask.last.is_a? Hash and ask.last.has_key? :html) ? ask.last[:html].merge!(key => value) : ask << { :html => { key => value }}
|
706
|
-
end
|
707
|
-
end
|
708
|
-
end
|
709
|
-
ask
|
710
|
-
end
|
711
|
-
|
712
|
-
private
|
713
|
-
|
714
|
-
# Call after headers or columns with no attributes (table.headers)
|
715
|
-
# Create all the cells based on each column in the model's database table
|
716
|
-
# Create cell's actions based on option default_actions or on actions given (:actions => [:edit])
|
717
|
-
def create_cells
|
718
|
-
@attributes.each { |ask| add_cell(@object, format_options_to_cell(ask)) }
|
719
|
-
if @args.empty?
|
720
|
-
TableBuilder.default_actions.each { |action| add_cell(@object, action, :action) }
|
721
|
-
else
|
722
|
-
get_column_actions.each { |action| add_cell(@object, action, :action) }
|
723
|
-
end
|
724
|
-
end
|
725
|
-
|
726
|
-
# Return an Array of all actions given to headers or columns (:actions => [:edit, :delete])
|
727
|
-
def get_column_actions
|
728
|
-
if @column_options.has_key? :actions
|
729
|
-
if @column_options[:actions].is_a?(Array)
|
730
|
-
return @column_options[:actions]
|
731
|
-
else
|
732
|
-
return [ @column_options[:actions] ]
|
733
|
-
end
|
734
|
-
end
|
735
|
-
[]
|
736
|
-
end
|
737
|
-
|
738
|
-
# Return an Array of the columns based on options :only or :except
|
739
|
-
def get_columns
|
740
|
-
if @column_options.has_key? :only
|
741
|
-
return @column_options[:only] if @column_options[:only].is_a?(Array)
|
742
|
-
[ @column_options[:only] ]
|
743
|
-
elsif @column_options.has_key? :except
|
744
|
-
return content_columns - @column_options[:except] if @column_options[:except].is_a?(Array)
|
745
|
-
content_columns - [ @column_options[:except] ]
|
746
|
-
else
|
747
|
-
content_columns
|
748
|
-
end
|
749
|
-
end
|
750
|
-
|
751
|
-
end
|
752
|
-
|
753
|
-
class FormatCell < FormatLine
|
754
|
-
|
755
|
-
def initialize(object, args, type = nil, block = nil)
|
756
|
-
@object, @type, @block = object, type, block
|
757
|
-
if args.is_a? Array
|
758
|
-
@options, @html_options = get_cell_and_html_options( args.extract_options! )
|
759
|
-
@ask = args.first
|
760
|
-
if @ask.nil? and @options.has_key?(:action)
|
761
|
-
@type = :action
|
762
|
-
@ask = @options[:action]
|
763
|
-
end
|
764
|
-
else
|
765
|
-
@ask = args
|
766
|
-
end
|
767
|
-
set_default_options
|
768
|
-
@can_sort = true if @options and @options[:sort] and can_sort_column?(@ask)
|
769
|
-
end
|
770
|
-
|
771
|
-
# Return a td with the formated value or action for columns
|
772
|
-
def render_cell_tbody
|
773
|
-
if @type == :action
|
774
|
-
cell_value = action_link_to(@ask)
|
775
|
-
elsif @ask
|
776
|
-
cell_value = (@ask.is_a?(Symbol)) ? format_cell_value(@object[@ask], @ask) : format_cell_value(@ask)
|
777
|
-
else
|
778
|
-
cell_value = @block
|
779
|
-
end
|
780
|
-
cell_value = action_link_to(@options[:action], cell_value) if @type != :action and @options.has_key?(:action)
|
781
|
-
content_tag(:td, cell_value, @html_options)
|
782
|
-
end
|
783
|
-
|
784
|
-
# Return a td with the formated value or action for headers
|
785
|
-
def render_cell_thead
|
786
|
-
if @ask
|
787
|
-
cell_value = (@ask.is_a?(Symbol)) ? I18n.t(@ask, {}, :header) : @ask
|
788
|
-
else
|
789
|
-
cell_value = @block
|
790
|
-
end
|
791
|
-
if @can_sort and @options[:sort]
|
792
|
-
@html_options.merge!(:class => "#{@html_options[:class]} #{sorting_html_class}".strip)
|
793
|
-
content_tag(:th, sort_link_to(cell_value), @html_options)
|
794
|
-
else
|
795
|
-
content_tag(:th, cell_value, @html_options)
|
796
|
-
end
|
797
|
-
end
|
798
|
-
|
799
|
-
def render_cell_tfoot
|
800
|
-
if @ask
|
801
|
-
cell_value = (@ask.is_a?(Symbol)) ? I18n.t(@ask, {}, :footer) : @ask
|
802
|
-
else
|
803
|
-
cell_value = @block
|
804
|
-
end
|
805
|
-
cell_value = action_link_to(@options[:action], cell_value) if @type != :action and @options.has_key?(:action)
|
806
|
-
content_tag(:td, cell_value, @html_options)
|
807
|
-
end
|
808
|
-
|
809
|
-
private
|
810
|
-
|
811
|
-
# Return options and html options for a cell
|
812
|
-
def get_cell_and_html_options(options)
|
813
|
-
return options, options.delete(:html) || {}
|
814
|
-
end
|
815
|
-
|
816
|
-
# Set default options for cell
|
817
|
-
# Set an empty hash if no html options
|
818
|
-
# Set an empty hash if no options
|
819
|
-
# Set sort to true if no options sort
|
820
|
-
def set_default_options
|
821
|
-
@html_options = {} unless defined? @html_options
|
822
|
-
@options = {} unless defined? @options
|
823
|
-
@html_options = format_options_to_cell(@html_options, @options)
|
824
|
-
@options[:sort] = @@options[:sort] if !@options.has_key? :sort
|
825
|
-
end
|
826
|
-
|
827
|
-
# Create the link for actions
|
828
|
-
# Set the I18n translation or the given block for the link's name
|
829
|
-
def action_link_to(action, block = nil)
|
830
|
-
object_or_array = @@object_or_array.clone
|
831
|
-
object_or_array.push @object
|
832
|
-
return case action.to_sym
|
833
|
-
when :delete
|
834
|
-
create_link_to(block || I18n.t(:delete), object_or_array, @@options[:link_remote], :delete, I18n.t(:confirm_delete))
|
835
|
-
when :show
|
836
|
-
create_link_to(block || I18n.t(:show), object_or_array, @@options[:link_remote])
|
837
|
-
else
|
838
|
-
object_or_array.insert(0, action)
|
839
|
-
create_link_to(block || I18n.t(@ask), object_or_array, @@options[:link_remote])
|
840
|
-
end
|
841
|
-
end
|
842
|
-
|
843
|
-
# Create sorting link
|
844
|
-
def sort_link_to(name)
|
845
|
-
create_link_to(name, sort_url, @@options[:sort_remote])
|
846
|
-
end
|
847
|
-
|
848
|
-
# Create the link based on object
|
849
|
-
# Set an ajax link if option link_remote is set to true
|
850
|
-
# Compatible with rails 2 and 3.
|
851
|
-
def create_link_to(block, url, remote, method = nil, confirm = nil)
|
852
|
-
if remote and Tools::rails3?
|
853
|
-
return @@template.link_to(block, url, :method => method, :confirm => confirm, :remote => true)
|
854
|
-
elsif remote
|
855
|
-
method = :get if method.nil?
|
856
|
-
return @@template.link_to_remote(block, { :url => url, :method => method, :confirm => confirm })
|
857
|
-
end
|
858
|
-
@@template.link_to(block, url, :method => method, :confirm => confirm)
|
859
|
-
end
|
860
|
-
|
861
|
-
# Return a string with html class of sorting for headers
|
862
|
-
# The html class is based on option: SortingTableFor::TableBuilder.html_sorting_class
|
863
|
-
def sorting_html_class
|
864
|
-
return TableBuilder.html_sorting_class.first if current_sorting.nil?
|
865
|
-
return TableBuilder.html_sorting_class.second if current_sorting == :asc
|
866
|
-
TableBuilder.html_sorting_class.third
|
867
|
-
end
|
868
|
-
|
869
|
-
# Return an url for sorting
|
870
|
-
# Add the param sorting_table[name]=direction to the url
|
871
|
-
# Add the default direction: :asc
|
872
|
-
def sort_url
|
873
|
-
url_params = @@params.clone
|
874
|
-
if url_params.has_key? TableBuilder.params_sort_table
|
875
|
-
if url_params[TableBuilder.params_sort_table].has_key? @ask
|
876
|
-
url_params[TableBuilder.params_sort_table][@ask] = inverse_sorting
|
877
|
-
return @@template.url_for(url_params)
|
878
|
-
end
|
879
|
-
url_params[TableBuilder.params_sort_table].delete @ask
|
880
|
-
end
|
881
|
-
url_params[TableBuilder.params_sort_table] = { @ask => :asc }
|
882
|
-
@@template.url_for(url_params)
|
883
|
-
end
|
884
|
-
|
885
|
-
# Return a symbol of the current sorting (:asc, :desc, nil)
|
886
|
-
def current_sorting
|
887
|
-
if @@params.has_key? TableBuilder.params_sort_table and @@params[TableBuilder.params_sort_table].has_key? @ask
|
888
|
-
return @@params[TableBuilder.params_sort_table][@ask].to_sym
|
889
|
-
end
|
890
|
-
nil
|
891
|
-
end
|
892
|
-
|
893
|
-
# Return a symbol, the inverse of the current sorting
|
894
|
-
def inverse_sorting
|
895
|
-
return :asc if current_sorting.nil?
|
896
|
-
return :desc if current_sorting == :asc
|
897
|
-
:asc
|
898
|
-
end
|
899
|
-
|
900
|
-
# Return the formated cell's value
|
901
|
-
def format_cell_value(value, attribute = nil)
|
902
|
-
unless (ret_value = format_cell_value_as_ask(value)).nil?
|
903
|
-
return ret_value
|
904
|
-
end
|
905
|
-
format_cell_value_as_type(value, attribute)
|
906
|
-
end
|
907
|
-
|
908
|
-
# Format the value if option :as is set
|
909
|
-
def format_cell_value_as_ask(value)
|
910
|
-
return nil if !@options or @options.empty? or !@options.has_key?(:as)
|
911
|
-
return case @options[:as]
|
912
|
-
when :date then ::I18n.l(value.to_date, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
913
|
-
when :time then ::I18n.l(value.to_datetime, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
914
|
-
when :currency then number_to_currency(value)
|
915
|
-
else nil
|
916
|
-
end
|
917
|
-
end
|
918
|
-
|
919
|
-
# Format the value based on value's type
|
920
|
-
def format_cell_value_as_type(value, attribute)
|
921
|
-
if value.is_a?(Time) or value.is_a?(Date)
|
922
|
-
return ::I18n.l(value, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
923
|
-
elsif TableBuilder.currency_columns.include?(attribute)
|
924
|
-
return number_to_currency(value)
|
925
|
-
elsif value.is_a?(TrueClass)
|
926
|
-
return TableBuilder.default_boolean.first
|
927
|
-
elsif value.is_a?(FalseClass)
|
928
|
-
return TableBuilder.default_boolean.second
|
929
|
-
end
|
930
|
-
value
|
931
|
-
end
|
932
|
-
|
933
|
-
end
|
934
643
|
end
|
data/spec/db/schema.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class SortingTableForUser < ActiveRecord::Base
|
2
2
|
if ::SortingTableFor::Tools::rails3?
|
3
3
|
scope :good_position, :conditions => 'position > 3'
|
4
4
|
scope :set_limit, lambda { |limit| { :limit => limit } }
|
@@ -10,7 +10,7 @@ class User < ActiveRecord::Base
|
|
10
10
|
end
|
11
11
|
|
12
12
|
20.times do |n|
|
13
|
-
|
13
|
+
SortingTableForUser.create(
|
14
14
|
:username => "my_usename_#{n}",
|
15
15
|
:firstname => "my_firstname_#{n}",
|
16
16
|
:lastname => "my_lastname_#{n}",
|
@@ -1,16 +1,19 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
-
require File.expand_path(File.dirname(__FILE__) + '/../fixtures/
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../fixtures/sorting_table_for_user')
|
5
5
|
|
6
6
|
include SortingTableForSpecHelper
|
7
7
|
|
8
8
|
describe SortingTableFor, :type => :helper do
|
9
9
|
|
10
|
+
before :all do
|
11
|
+
(SortingTableFor::Tools::rails3?) ? routes_rails3 : routes_rails2
|
12
|
+
end
|
13
|
+
|
10
14
|
before :each do
|
11
|
-
@users =
|
12
|
-
helper.stub!(:
|
13
|
-
helper.stub!(:params).and_return({ :controller => 'fakes', :action => 'index' })
|
15
|
+
@users = SortingTableForUser.all
|
16
|
+
helper.stub!(:params).and_return({ :controller => 'sorting_table_for_users', :action => 'index' })
|
14
17
|
helper.output_buffer = ''
|
15
18
|
end
|
16
19
|
|
@@ -42,7 +45,7 @@ describe SortingTableFor, :type => :helper do
|
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
|
-
it "should
|
48
|
+
it "should use i18n by default" do
|
46
49
|
helper.sorting_table_for(@users) do |table|
|
47
50
|
html = table.headers(:username)
|
48
51
|
html.should have_comp_tag("th:nth-child(1)", :text => 'Usernames')
|