table_cloth 0.2.3 → 0.3.0.beta1

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.
Files changed (39) hide show
  1. data/.gitignore +1 -0
  2. data/lib/generators/templates/table.rb +7 -0
  3. data/lib/table_cloth.rb +14 -15
  4. data/lib/table_cloth/base.rb +3 -26
  5. data/lib/table_cloth/column.rb +0 -12
  6. data/lib/table_cloth/{action.rb → column_jury.rb} +10 -6
  7. data/lib/table_cloth/configurable_elements.rb +6 -13
  8. data/lib/table_cloth/extensions/actions.rb +25 -0
  9. data/lib/table_cloth/extensions/actions/action.rb +21 -0
  10. data/lib/table_cloth/extensions/actions/action_collection.rb +11 -0
  11. data/lib/table_cloth/extensions/actions/column.rb +21 -0
  12. data/lib/table_cloth/extensions/actions/jury.rb +37 -0
  13. data/lib/table_cloth/presenter.rb +16 -5
  14. data/lib/table_cloth/presenters/default.rb +1 -1
  15. data/lib/table_cloth/version.rb +1 -1
  16. data/spec/factories/actions.rb +6 -0
  17. data/spec/factories/columns.rb +18 -0
  18. data/spec/factories/dummy_tables.rb +11 -0
  19. data/spec/lib/base_spec.rb +6 -22
  20. data/spec/lib/column_jury_spec.rb +35 -0
  21. data/spec/lib/column_spec.rb +9 -14
  22. data/spec/lib/extensions/actions/action_collection_spec.rb +29 -0
  23. data/spec/lib/extensions/actions/action_spec.rb +57 -0
  24. data/spec/lib/extensions/actions/column_spec.rb +23 -0
  25. data/spec/lib/extensions/actions/jury_spec.rb +67 -0
  26. data/spec/lib/extensions/actions_spec.rb +42 -0
  27. data/spec/lib/presenter_spec.rb +29 -31
  28. data/spec/lib/presenters/default_spec.rb +61 -124
  29. data/spec/spec_helper.rb +7 -0
  30. data/spec/support/dummy_table.rb +0 -4
  31. data/spec/support/dummy_table_with_value_options.rb +1 -1
  32. data/spec/support/matchers/element_matchers.rb +6 -0
  33. data/table_cloth.gemspec +2 -0
  34. metadata +63 -14
  35. data/lib/table_cloth/actions.rb +0 -23
  36. data/lib/table_cloth/columns/action.rb +0 -24
  37. data/spec/lib/actions_spec.rb +0 -28
  38. data/spec/lib/columns/action_spec.rb +0 -73
  39. data/spec/support/dummy_table_with_actions.rb +0 -7
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  *.gem
2
2
  *.rbc
3
3
  .bundle
4
+ coverage
4
5
  .config
5
6
  .yardoc
6
7
  Gemfile.lock
@@ -1,4 +1,7 @@
1
1
  class <%= class_name %>Table < TableCloth::Base
2
+ # To include actions on this table, uncomment this line
3
+ # include TableCloth::Extensions::Actions
4
+
2
5
  # Define columns with the #column method
3
6
  # column :name, :email
4
7
 
@@ -22,8 +25,12 @@ class <%= class_name %>Table < TableCloth::Base
22
25
  # Actions give you the ability to create a column for any actions you'd like to provide.
23
26
  # Pass a block with an arity of 2, (object, view context).
24
27
  # You can add as many actions as you want.
28
+ # Make sure you include the actions extension.
25
29
  #
26
30
  # actions do
27
31
  # action {|object| link_to "Edit", edit_object_path(object) }
32
+ # action(if: :valid?) {|object| link_to "Invalidate", invalidate_object_path(object) }
28
33
  # end
34
+ #
35
+ # If action provides an "if:" option, it will call that method on the object. It can also take a block with an arity of 1.
29
36
  end
data/lib/table_cloth.rb CHANGED
@@ -1,24 +1,23 @@
1
- require 'action_view'
2
- require 'active_support/core_ext/class'
3
- require 'table_cloth/version'
4
- require 'table_cloth/configurable_elements'
5
- require 'table_cloth/base'
1
+ require "action_view"
2
+ require "active_support/core_ext/class"
3
+ require "table_cloth/version"
4
+ require "table_cloth/configurable_elements"
5
+ require "table_cloth/base"
6
6
 
7
7
  module TableCloth
8
- autoload :Configuration, 'table_cloth/configuration'
9
- autoload :Builder, 'table_cloth/builder'
10
- autoload :Column, 'table_cloth/column'
11
- autoload :Action, 'table_cloth/action'
12
- autoload :Actions, 'table_cloth/actions'
13
- autoload :Presenter, 'table_cloth/presenter'
14
- autoload :ActionViewExtension, 'table_cloth/action_view_extension'
8
+ autoload :Configuration, "table_cloth/configuration"
9
+ autoload :Builder, "table_cloth/builder"
10
+ autoload :Column, "table_cloth/column"
11
+ autoload :ColumnJury, "table_cloth/column_jury"
12
+ autoload :Presenter, "table_cloth/presenter"
13
+ autoload :ActionViewExtension, "table_cloth/action_view_extension"
15
14
 
16
15
  module Presenters
17
- autoload :Default, 'table_cloth/presenters/default'
16
+ autoload :Default, "table_cloth/presenters/default"
18
17
  end
19
18
 
20
- module Columns
21
- autoload :Action, 'table_cloth/columns/action'
19
+ module Extensions
20
+ autoload :Actions, "table_cloth/extensions/actions"
22
21
  end
23
22
 
24
23
  def config_for(type)
@@ -11,16 +11,6 @@ module TableCloth
11
11
  @view = view
12
12
  end
13
13
 
14
- def columns
15
- @columns ||= self.class.columns.each_with_object({}) do |(column_name, column), columns|
16
- columns[column_name] = column if column.available?(self)
17
- end
18
- end
19
-
20
- def has_actions?
21
- self.class.has_actions?
22
- end
23
-
24
14
  class << self
25
15
  def presenter(klass=nil)
26
16
  return @presenter = klass if klass
@@ -40,7 +30,7 @@ module TableCloth
40
30
  column_class = options.delete(:using) || Column
41
31
 
42
32
  args.each do |name|
43
- add_column name, column_class.new(name, options)
33
+ add_column(class: column_class, options: options, name: name)
44
34
  end
45
35
  end
46
36
 
@@ -53,22 +43,9 @@ module TableCloth
53
43
  @columns
54
44
  end
55
45
 
56
- def add_column(name, column)
46
+ def add_column(options)
57
47
  @columns ||= {}
58
- @columns[name] = column
59
- end
60
-
61
- def actions(options={}, &block)
62
- if block_given?
63
- actions = Actions.new(options, &block)
64
- columns[:actions] = actions.column
65
- end
66
-
67
- columns[:actions].actions
68
- end
69
-
70
- def has_actions?
71
- columns[:actions].present?
48
+ @columns[options[:name]] = options
72
49
  end
73
50
  end
74
51
  end
@@ -18,17 +18,5 @@ module TableCloth
18
18
  def human_name
19
19
  options[:label] || name.to_s.humanize
20
20
  end
21
-
22
- def available?(table)
23
- if options[:if] && options[:if].is_a?(Symbol)
24
- return !!table.send(options[:if])
25
- end
26
-
27
- if options[:unless] && options[:unless].is_a?(Symbol)
28
- return !table.send(options[:unless])
29
- end
30
-
31
- true
32
- end
33
21
  end
34
22
  end
@@ -1,12 +1,12 @@
1
1
  module TableCloth
2
- class Action
3
- attr_reader :action, :options
4
-
5
- def initialize(options)
6
- @options = options
2
+ class ColumnJury
3
+ attr_reader :column, :table
4
+
5
+ def initialize(column, table)
6
+ @column, @table = column, table
7
7
  end
8
8
 
9
- def available?(table)
9
+ def available?
10
10
  if options[:if] && options[:if].is_a?(Symbol)
11
11
  return !!table.send(options[:if])
12
12
  end
@@ -17,5 +17,9 @@ module TableCloth
17
17
 
18
18
  true
19
19
  end
20
+
21
+ def options
22
+ column.options
23
+ end
20
24
  end
21
25
  end
@@ -1,21 +1,14 @@
1
1
  module TableCloth
2
2
  module ConfigurableElements
3
+ OPTIONS = %w(table thead th tbody tr td).map(&:to_sym)
4
+
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  included do
6
- cattr_accessor :table
7
- cattr_accessor :thead
8
- cattr_accessor :th
9
- cattr_accessor :tbody
10
- cattr_accessor :tr
11
- cattr_accessor :td
12
-
13
- self.table = ActiveSupport::OrderedOptions.new
14
- self.thead = ActiveSupport::OrderedOptions.new
15
- self.th = ActiveSupport::OrderedOptions.new
16
- self.tbody = ActiveSupport::OrderedOptions.new
17
- self.tr = ActiveSupport::OrderedOptions.new
18
- self.td = ActiveSupport::OrderedOptions.new
8
+ OPTIONS.each do |option|
9
+ cattr_accessor option
10
+ self.send "#{option}=", ActiveSupport::OrderedOptions.new
11
+ end
19
12
  end
20
13
  end
21
14
  end
@@ -0,0 +1,25 @@
1
+ module TableCloth
2
+ module Extensions
3
+ module Actions
4
+ autoload :Action, "table_cloth/extensions/actions/action"
5
+ autoload :Column, "table_cloth/extensions/actions/column"
6
+ autoload :ActionCollection, "table_cloth/extensions/actions/action_collection"
7
+ autoload :Jury, "table_cloth/extensions/actions/jury"
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ def actions(options={}, &block)
12
+ action_collection.instance_eval(&block)
13
+
14
+ column :actions, options.update(collection: action_collection, using: Column)
15
+ end
16
+
17
+ private
18
+
19
+ def action_collection
20
+ @action_collection ||= ActionCollection.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module TableCloth::Extensions::Actions
2
+ class Action
3
+ attr_reader :options
4
+
5
+ def initialize(options={})
6
+ @options = options
7
+ end
8
+
9
+ def jury
10
+ @jury ||= Jury.new(self)
11
+ end
12
+
13
+ def value(object, view)
14
+ if jury.available?(object)
15
+ view.instance_exec(object, &options[:proc])
16
+ else
17
+ ""
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module TableCloth::Extensions::Actions
2
+ class ActionCollection
3
+ def actions
4
+ @actions ||= []
5
+ end
6
+
7
+ def action(options={}, &block)
8
+ actions << Action.new({proc: block}.merge(options))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module TableCloth::Extensions::Actions
2
+ class Column < ::TableCloth::Column
3
+ def value(object, view, table=nil)
4
+ actions = action_collection.actions.map do |action|
5
+ action.value(object, view)
6
+ end
7
+
8
+ view.raw(actions.join(separator))
9
+ end
10
+
11
+ private
12
+
13
+ def separator
14
+ options[:separator] || " "
15
+ end
16
+
17
+ def action_collection
18
+ @action_collection ||= options[:collection]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module TableCloth::Extensions::Actions
2
+ class Jury
3
+ attr_reader :action
4
+
5
+ def initialize(action)
6
+ @action = action
7
+ end
8
+
9
+ def available?(object)
10
+ case action_if
11
+ when Proc
12
+ return !!action_if.call(object)
13
+ when Symbol
14
+ return !!object.send(action_if)
15
+ end
16
+
17
+ case action_unless
18
+ when Proc
19
+ return !action_unless.call(object)
20
+ when Symbol
21
+ return !object.send(action_unless)
22
+ end
23
+
24
+ return true
25
+ end
26
+
27
+ private
28
+
29
+ def action_if
30
+ action.options[:if]
31
+ end
32
+
33
+ def action_unless
34
+ action.options[:unless]
35
+ end
36
+ end
37
+ end
@@ -4,13 +4,12 @@ module TableCloth
4
4
  :table
5
5
 
6
6
  def initialize(objects, table, view)
7
- @view_context = view
8
- @table_definition = table
9
7
  @objects = objects
8
+ @table_definition = table
9
+ @view_context = view
10
10
  @table = table_definition.new(objects, view)
11
11
  end
12
12
 
13
- # Short hand so your fingers don't hurt
14
13
  def v
15
14
  view_context
16
15
  end
@@ -27,14 +26,26 @@ module TableCloth
27
26
  raise NoMethodError, "You must override the .rows method"
28
27
  end
29
28
 
29
+ def columns
30
+ @columns ||= table.class.columns.map do |name, options|
31
+ column = options[:class].new(name, options[:options])
32
+
33
+ if ColumnJury.new(column, table).available?
34
+ column
35
+ else
36
+ nil
37
+ end
38
+ end.compact
39
+ end
40
+
30
41
  def column_names
31
- @column_names ||= table.columns.each_with_object([]) do |(column_name, column), names|
42
+ @column_names ||= columns.each_with_object([]) do |column, names|
32
43
  names << column.human_name
33
44
  end
34
45
  end
35
46
 
36
47
  def row_values(object)
37
- table.columns.each_with_object([]) do |(key, column), values|
48
+ columns.each_with_object([]) do |column, values|
38
49
  values << column.value(object, view_context, table)
39
50
  end
40
51
  end
@@ -15,7 +15,7 @@ module TableCloth
15
15
 
16
16
  def render_row(object)
17
17
  wrapper_tag :tr do
18
- v.raw table.columns.inject('') {|tds, (key, column)| tds + render_td(column, object) }
18
+ v.raw columns.inject('') {|tds, column| tds + render_td(column, object) }
19
19
  end
20
20
  end
21
21
 
@@ -1,3 +1,3 @@
1
1
  module TableCloth
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0.beta1"
3
3
  end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :action, class: "TableCloth::Extensions::Actions::Action" do
3
+ options({})
4
+ initialize_with { new(options) }
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ FactoryGirl.define do
2
+ factory :column, class: "TableCloth::Column" do
3
+ name :name
4
+ options({})
5
+
6
+ factory :if_column do
7
+ name :name
8
+ options({ if: :admin? })
9
+ end
10
+
11
+ factory :unless_column do
12
+ name :name
13
+ options({ unless: :moderator? })
14
+ end
15
+
16
+ initialize_with { new(name, options) }
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ FactoryGirl.define do
2
+ factory :dummy_table, class: "TableCloth::Base" do
3
+ initialize_with do
4
+ Class.new(TableCloth::Base).tap do |klass|
5
+ attributes.each do |key,attribute|
6
+ klass.column *[key, attribute]
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -16,7 +16,8 @@ describe TableCloth::Base do
16
16
  end
17
17
 
18
18
  it 'column accepts options' do
19
- expect { subject.column :n, {option: 'value'} }.not_to raise_error
19
+ subject.column :name, option: "value"
20
+ expect(subject.columns[:name][:options][:option]).to eq("value")
20
21
  end
21
22
 
22
23
  it '.columns returns all columns' do
@@ -33,22 +34,16 @@ describe TableCloth::Base do
33
34
  subject.column(:name) { 'Wee' }
34
35
 
35
36
  column = subject.columns[:name]
36
- expect(column.options[:proc]).to be_present
37
- expect(column.options[:proc]).to be_kind_of(Proc)
37
+ expect(column[:options][:proc]).to be_present
38
+ expect(column[:options][:proc]).to be_kind_of(Proc)
38
39
  end
39
40
 
40
41
  context "custom" do
41
- let(:custom_column) do
42
- Class.new(TableCloth::Column) do
43
- def value(object, view)
44
- "AN EMAIL!"
45
- end
46
- end
47
- end
42
+ let(:custom_column) { stub(:custom, value: "AN EMAIL") }
48
43
 
49
44
  it '.column can take a custom column' do
50
45
  subject.column :email, using: custom_column
51
- subject.columns[:email].value(dummy_model, view_context).should == "AN EMAIL!"
46
+ expect(subject.columns[:email][:class]).to eq(custom_column)
52
47
  end
53
48
  end
54
49
  end
@@ -59,17 +54,6 @@ describe TableCloth::Base do
59
54
  end
60
55
  end
61
56
 
62
- context 'actions' do
63
- it "takes a block" do
64
- subject.actions do
65
- action { 'Edit' }
66
- action { 'Delete' }
67
- end
68
-
69
- expect(subject.columns[:actions]).to be_kind_of TableCloth::Columns::Action
70
- end
71
- end
72
-
73
57
  context "configuration" do
74
58
  subject { Class.new(TableCloth::Base) }
75
59
  let(:sibling1_class) { Class.new(subject) }