rows_controller 2.2.1 → 3.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21096738d0ca4096f5740974f764bb27f40f8c37f3ed21144e62918baafc8584
4
- data.tar.gz: 602c9204a275821c93c46c4bac941bb06110fe1849d0fbed23a35cbcd5f6dac9
3
+ metadata.gz: c66d74c692d41703b03eeae2e2e0603a145cfd3c0478496c1792e4a4ebf70776
4
+ data.tar.gz: 4657d006dbff4e84e1ba0806d72e22aebbcd86dc254cb3389208edd05c6d472e
5
5
  SHA512:
6
- metadata.gz: 1527109202ce0a2ec0ff24ef3cd9489a6448fd1b307512bffcb0c98982a12107b4f0357d8f346b40ba78fd0ec66c10e1b62fcd09718341c4a38da1b919c640e6
7
- data.tar.gz: 83f3fafe8f046aacff8bc4b809e9f032f231b27e5db90229371c578fcd7d5c310e53eaad35c851e381ecca0e838fb4f1cb1770f8f5a68d1f3d14269537694372
6
+ metadata.gz: e117f77fb4a317df03e93e9f92ab72f418418124c305c821a3fafd2bdad6abbe6ea12d09fc303e1479d28930e71cea6b2ea145f77bdee48199d8e28f539380aa
7
+ data.tar.gz: 5ad9d07738d72d9f688681fe4ffac092082f374f4e4498294bf58481e0c15f5fb2fde06141373a5db61ff343d371a754e62a2b19ba7fd9b26408ab52a5aac933
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2009-2018 Dittmar Krall http://matique.de
1
+ Copyright (c) 2012-2021 Dittmar Krall - www.matique.com
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  RowsController
2
2
  ==============
3
3
  [![Gem Version](https://badge.fury.io/rb/rows_controller.svg)](https://badge.fury.io/rb/rows_controller)
4
- [![Build Status](https://travis-ci.org/matique/rows_controller.png?branch=master)](https://travis-ci.org/matique/rows_controller)
5
4
 
6
5
  DRYs Rails controllers. Imagine replacing that @order by 'resource' in the
7
6
  controllers/views and, imho, an area for DRYing appears.
@@ -26,8 +25,8 @@ use:
26
25
 
27
26
  I.e. RowsController defines all the usual methods (index, show, edit,...).
28
27
 
29
- The methods may be redefined in OrdersController overwriting the
30
- methods from RowsController.
28
+ The methods may be redefined in OrdersController
29
+ (overwrites methods from RowsController).
31
30
 
32
31
  Low level methods like 'resources' may be redefined as well.
33
32
  An example:
@@ -70,6 +69,12 @@ be changed by e.g.:
70
69
  The model class can be retrieved with the helper model_class.
71
70
 
72
71
 
72
+ Rails 6
73
+ -------
74
+
75
+ This gem is intended for Rails 6.
76
+ Older Rails versions may use "gem 'rows_controller', '= 2.2.2'".
77
+
73
78
  Rails 5
74
79
  -------
75
80
 
@@ -118,7 +123,7 @@ As usual:
118
123
 
119
124
  gem 'rows_controller' # in Gemfile
120
125
  bundle
121
- ( cd spec/dummy; rake db:create db:migrate )
126
+ ( cd spec/dummy; rake db:create db:migrate ) # not required for Rails 6
122
127
  rake
123
128
 
124
129
 
@@ -131,4 +136,4 @@ Look for:
131
136
  - inherited_resources
132
137
  - decent_exposure
133
138
 
134
- Copyright (c) 2009-2017 [Dittmar Krall], released under the MIT license.
139
+ Copyright (c) 2009-2020 [Dittmar Krall], released under the MIT license.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rows/resources'
2
4
  require 'rows/model'
3
5
  require 'rows/utils'
@@ -56,7 +58,8 @@ class RowsController < ApplicationController
56
58
  def destroy
57
59
  set_resource
58
60
  resource_destroy
59
- flash[:notice] = t('ui.destroyed', model: model_name).html_safe unless request.xhr?
61
+ msg = t('ui.destroyed', model: model_name).html_safe
62
+ flash[:notice] = msg unless request.xhr?
60
63
  respond_to do |format|
61
64
  format.html { redirect_to action: :index }
62
65
  format.js { render template: 'rows/destroy', layout: false }
@@ -67,14 +70,14 @@ class RowsController < ApplicationController
67
70
  private
68
71
  def create_update(which, msg)
69
72
  respond_to do |format|
70
- if self.send(which)
73
+ if send(which)
71
74
  format.html {
72
75
  flash[:notice] = t(msg, scope: :ui, model: model_name,
73
76
  default: "%{model} was successfully #{msg}.").html_safe
74
77
  if params[:commit] == 'OK'
75
78
  redirect_to action: :index
76
79
  else
77
- redirect_to action: 'edit', id: resource.id
80
+ redirect_to action: 'edit', id: resource.id
78
81
  end
79
82
  }
80
83
  format.json { render action: 'show',
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extensions
1
4
  class RowsExtController < RowsController
2
5
 
3
6
  def copy
4
7
  set_resource resource.dup
5
8
  resource.id = nil
6
9
  respond_to do |format|
7
- format.html { render :action => :new }
10
+ format.html { render action: :new }
8
11
  end
9
12
  end
10
13
 
@@ -3,6 +3,7 @@ ruby:
3
3
  # Column :multi_selection generates a check_box
4
4
 
5
5
  cnt = 0
6
+ odd_even = %w[odd even]
6
7
 
7
8
  table.rows-list
8
9
  thead
@@ -12,7 +13,7 @@ table.rows-list
12
13
  tbody
13
14
  - resources.each do |resource|
14
15
  - cnt += 1
15
- tr id="row_#{resource.id}" class="#{%w{odd even}.at(cnt % 2)}"
16
+ tr id="row_#{resource.id}" class="#{odd_even.at(cnt % 2)}"
16
17
  = render 'list_row', resource: resource, columns: columns
17
18
  td
18
19
  = render 'row_buttons', row: resource
@@ -1,5 +1,5 @@
1
1
  / Default list footer presenting a 'Create' button linked to action new.
2
2
  / May be overwriten for e.g. pagination
3
3
 
4
- div.list-footer
4
+ .list-footer
5
5
  = link_to t('button.create').html_safe, url_for(action: :new), class: 'button'
@@ -4,4 +4,3 @@
4
4
 
5
5
  - columns.each do |column|
6
6
  th = column == :multi_selection ? '' : t("header.#{column}")
7
-
@@ -1,14 +1,14 @@
1
1
  ruby:
2
2
  # Fills the last column of the list with "buttons".
3
3
  # Requires row current_controller (supplied by Rails)
4
- if defined?(current_controller)
5
- url = "/#{current_controller}/#{row.id}"
6
- else
7
- url = url_for row
8
- end
4
+ url = if defined?(current_controller)
5
+ "/#{current_controller}/#{row.id}"
6
+ else
7
+ url_for row
8
+ end
9
9
 
10
- = link_to t('button.show').html_safe, url
11
- = link_to t('button.edit').html_safe, url + '/edit'
10
+ = link_to t('button.show').html_safe, url
11
+ = link_to t('button.edit').html_safe, "#{url}/edit"
12
12
  = link_to t('button.delete').html_safe, url,
13
13
  remote: true,
14
- data: { confirm: 'Are you sure?' }, method: :delete
14
+ data: {confirm: 'Are you sure?'}, method: :delete
@@ -10,7 +10,7 @@ ruby:
10
10
  left ||= nil
11
11
  right ||= nil
12
12
 
13
- div.rows-submit style="width:100%"
13
+ .rows-submit style="width:100%"
14
14
  input style="display:none" type="submit" name="commit" value="OK"
15
15
  p style="float:left"
16
16
  = render '/rows/submit_part', content: :submit_left, names: left
@@ -4,7 +4,8 @@ ruby:
4
4
  # partial 'form' template
5
5
 
6
6
  content_for(:submit_left) {
7
- link_to t('button.back').html_safe, "/#{controller_name}", class: 'button' }
7
+ link_to t('button.back').html_safe, "/#{controller_name}", class: 'button'
8
+ }
8
9
 
9
10
  fieldset class="wide mask" id=model_symbol
10
11
  legend = t('ui.editing', model: model_name).html_safe
@@ -14,4 +15,4 @@ fieldset class="wide mask" id=model_symbol
14
15
  /"/#{controller_name}/update/#{@row.id}"
15
16
  = form_for resource, url: "/#{controller_name}/#{@row.id}", html: {multipart: @multipart} do |f|
16
17
  = render 'form', f: f
17
- = render '/rows/submit', right: ['update', 'OK']
18
+ = render '/rows/submit', right: %w[update OK]
@@ -1,8 +1,8 @@
1
- - # Template for RowsController (index)
2
- - # Requires resources model_name resource_columns
1
+ / Template for RowsController (index)
2
+ / Requires resources model_name resource_columns
3
3
 
4
- - if resources.length > 0
5
- h1 = t('ui.listing', model: model_name).html_safe
4
+ - if resources.length.positive?
5
+ h1 = t('ui.listing', model: model_name).html_safe
6
6
  = render '/rows/list', resources: resources, columns: resource_columns
7
7
  - else
8
8
  h1 = t('ui.empty_list', model: model_name).html_safe
@@ -4,7 +4,8 @@ ruby:
4
4
  # partial 'form' template
5
5
 
6
6
  content_for(:submit_left) {
7
- link_to t('button.back').html_safe, "/#{controller_name}", class: 'button' }
7
+ link_to t('button.back').html_safe, "/#{controller_name}", class: 'button'
8
+ }
8
9
 
9
10
  fieldset.wide class="mask" id=model_symbol
10
11
  legend = t('ui.new', model: model_name).html_safe
@@ -13,4 +14,4 @@ fieldset.wide class="mask" id=model_symbol
13
14
  - @multipart ||= false
14
15
  = form_for resource, html: {multipart: @multipart} do |f|
15
16
  = render 'form', f: f
16
- = render '/rows/submit', right: ['create', 'OK']
17
+ = render '/rows/submit', right: %w[create OK]
@@ -4,9 +4,11 @@ ruby:
4
4
  # partial 'form' template
5
5
 
6
6
  content_for(:submit_left) {
7
- link_to t('button.back').html_safe, url_for(action: :index), class: 'button' }
7
+ link_to t('button.back').html_safe, url_for(action: :index), class: 'button'
8
+ }
8
9
  content_for(:submit_right) {
9
- link_to t('button.edit').html_safe, url_for(action: :edit), class: 'button' }
10
+ link_to t('button.edit').html_safe, url_for(action: :edit), class: 'button'
11
+ }
10
12
 
11
13
  fieldset.show class="wide mask" id=model_symbol
12
14
  legend = t('ui.showing', model: model_name).html_safe
@@ -1,9 +1,9 @@
1
1
  / # Generates error messages
2
2
  / # requires resource
3
3
 
4
- - if resource && resource.respond_to?(:errors) && resource.errors.any?
5
- div#error_explanation
6
- h2 #{pluralize(resource.errors.count, "error")} prohibited this template from being saved:
4
+ - if resource.respond_to?(:errors) && resource.errors.any?
5
+ #error_explanation
6
+ h2 #{pluralize(resource.errors.count, 'error')} prohibited this template from being saved:
7
7
 
8
8
  ul
9
9
  - resource.errors.full_messages.each do |msg|
data/lib/rows/engine.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # rubocop: disable all
2
+
1
3
  module Rows
2
4
  class Engine < ::Rails::Engine
3
5
  end
data/lib/rows/model.rb CHANGED
@@ -1,30 +1,30 @@
1
- module Rows::Model
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rows::Model
3
4
  def model_class
4
- @_model_class ||= self.class.model_class ||
5
- params[:controller].singularize.camelize.constantize
5
+ @_model_class ||= self.class.model_class ||
6
+ Kernel.const_get(params[:controller].classify)
6
7
  end
7
8
 
8
- if Rails::VERSION::MAJOR > 3
9
- def model_name
10
- @_model_name ||= model_class.model_name.name
11
- end
9
+ if Rails::VERSION::MAJOR > 3
10
+ def model_name
11
+ @_model_name ||= model_class.model_name.name
12
+ end
12
13
 
13
- def model_symbol
14
- @_model_symbol ||= model_class.model_name.singular
15
- end
16
- else
17
- def model_name
18
- @_model_name ||= model_class.name
19
- end
14
+ def model_symbol
15
+ @_model_symbol ||= model_class.model_name.singular
16
+ end
17
+ else
18
+ def model_name
19
+ @_model_name ||= model_class.name
20
+ end
20
21
 
21
- def model_symbol
22
- @_model_symbol ||= model_name.underscore.gsub( %r{/}, '_' )
22
+ def model_symbol
23
+ @_model_symbol ||= model_name.underscore.tr('/', '_')
24
+ end
23
25
  end
24
- end
25
26
 
26
27
  def model_symbol_plural
27
28
  @_model_symbol_plural ||= model_symbol.pluralize
28
29
  end
29
-
30
30
  end
@@ -1,5 +1,6 @@
1
- module Rows::Resources
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rows::Resources
3
4
  def set_resources(rows = nil)
4
5
  rows ||= model_class.all
5
6
  instance_variable_set("@#{model_symbol_plural}", rows)
@@ -21,8 +22,8 @@ module Rows::Resources
21
22
  end
22
23
 
23
24
  private
24
- # low level resource methods
25
- # can be monkey patched
25
+ # low level resource methods
26
+ # can be monkey patched
26
27
  def resource_new
27
28
  if params[model_symbol]
28
29
  set_resource model_class.new(resource_params)
@@ -38,7 +39,7 @@ module Rows::Resources
38
39
 
39
40
  def resource_update
40
41
  if Rails::VERSION::MAJOR >= 4
41
- # return true unless params[model_symbol]
42
+ # return true unless params[model_symbol]
42
43
  resource.update(resource_params)
43
44
  else
44
45
  resource.update_attributes(resource_params)
@@ -50,31 +51,33 @@ module Rows::Resources
50
51
  end
51
52
 
52
53
  def resource_columns
53
- return model_class.column_headers if model_class.respond_to?(:column_headers)
54
- return ['to_s'] unless model_class.respond_to?(:content_columns)
55
- ['id'] + model_class.content_columns.collect{|c| c.name }
54
+ return model_class.column_headers if model_class.respond_to?(:column_headers)
55
+ return ['to_s'] unless model_class.respond_to?(:content_columns)
56
+
57
+ ['id'] + model_class.content_columns.collect(&:name)
56
58
  end
57
59
 
58
60
  def resource_whitelist
59
61
  raise "RowsController requires private method 'resource_whitelist' in controller <#{params[:controller]}>"
60
62
  end
61
63
 
62
- # Never trust parameters from the scary internet, only allow the white list through.
64
+ # Never trust parameters from the scary internet, only allow the
65
+ # white list through.
63
66
  def resource_params
64
67
  permits = resource_whitelist
65
68
  if params.respond_to?(:require)
66
69
  params.require(model_symbol).permit(permits)
67
70
  else
68
71
  pars = params[model_symbol] || {}
69
- pars.keys.each { |x|
70
- x = x.to_sym
71
- unless permits.include?(x) || permits.include?({x => []})
72
- pars.delete(x)
73
- p "** WARNING: model <#{model_name}> dropping params <#{x}>"
74
- end
72
+ # pars.keys.each { |x|
73
+ pars.each_key { |x|
74
+ x = x.to_sym
75
+ unless permits.include?(x) || permits.include?({x => []})
76
+ pars.delete(x)
77
+ p "** WARNING: model <#{model_name}> dropping params <#{x}>"
78
+ end
75
79
  }
76
80
  pars
77
81
  end
78
82
  end
79
-
80
83
  end
data/lib/rows/utils.rb CHANGED
@@ -1,20 +1,23 @@
1
- module Rows::Utils
1
+ # frozen_string_literal: true
2
+ # rubocop: disable all
2
3
 
3
- # formatting
4
+ module Rows::Utils
5
+ # formatting
4
6
  def resource_format(x)
5
7
  return '--'.html_safe if x.nil?
8
+
6
9
  bool = x.class == Time || x.class == Date || x.class == DateTime ||
7
- x.class == ActiveSupport::TimeWithZone
8
- return x.strftime('%d.%m.%Y').html_safe if bool
9
- # return I18n.l(x) if bool
10
- # return x.to_s.html_safe if x.class == Fixnum
11
- return x.to_s.html_safe if x.kind_of?(Integer)
10
+ x.class == ActiveSupport::TimeWithZone
11
+ return x.strftime('%d.%m.%Y').html_safe if bool
12
+
13
+ # return I18n.l(x) if bool
14
+ # return x.to_s.html_safe if x.class == Fixnum
15
+ return x.to_s.html_safe if x.is_a?(Integer)
12
16
  return 'X'.html_safe if x.class == TrueClass
13
17
  return '&ensp;'.html_safe if x.class == FalseClass
14
18
  return x.call if x.class == Proc
15
19
  return '---'.html_safe if x.empty?
16
- str = x.to_s
17
- return str.gsub(/\r*\n/, '<br/>')
18
- end
19
20
 
21
+ x.to_s.gsub(/\r*\n/, '<br/>')
22
+ end
20
23
  end
data/lib/rows/version.rb CHANGED
@@ -1,4 +1,13 @@
1
+ # rubocop: disable all
2
+
1
3
  module Rows
2
- VERSION = '2.2.1' # 2019-02-18
3
- # VERSION = '2.2.0'
4
+ VERSION = '3.0.4' # 2021-06-24
5
+ # VERSION = '3.0.3' # 2020-07-14
6
+ # VERSION = '3.0.2' # 2020-04-27
7
+ # VERSION = '3.0.1' # 2020-03-01
8
+ # VERSION = '3.0.0' # 2019-10-03
9
+ # VERSION = '2.2.3' # 2019-10-03
10
+ # VERSION = '2.2.2' # 2019-05-06
11
+ # VERSION = '2.2.1' # 2019-02-18
12
+ # VERSION = '2.2.0'
4
13
  end
@@ -0,0 +1,12 @@
1
+ # rubocop: disable all
2
+
3
+ module Rows
4
+ VERSION = '3.0.3' # 2020-07-14
5
+ # VERSION = '3.0.2' # 2020-04-27
6
+ # VERSION = '3.0.1' # 2020-03-01
7
+ # VERSION = '3.0.0' # 2019-10-03
8
+ # VERSION = '2.2.3' # 2019-10-03
9
+ # VERSION = '2.2.2' # 2019-05-06
10
+ # VERSION = '2.2.1' # 2019-02-18
11
+ # VERSION = '2.2.0'
12
+ end
@@ -1,8 +1,10 @@
1
+ # rubocop: disable all
2
+
1
3
  require 'rows/engine'
2
4
 
3
5
  module Rows
4
6
  end
5
7
 
6
- I18n.load_path << File.expand_path('../rows/locales/en.yml', __FILE__)
7
- I18n.load_path << File.expand_path('../rows/locales/de.yml', __FILE__)
8
+ I18n.load_path << File.expand_path('rows/locales/en.yml', __dir__)
9
+ I18n.load_path << File.expand_path('rows/locales/de.yml', __dir__)
8
10
  I18n.reload!
metadata CHANGED
@@ -1,57 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rows_controller
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 3.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dittmar Krall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-18 00:00:00.000000000 Z
11
+ date: 2021-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '12'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '12'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: appraisal
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '2'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '2'
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: combustion
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  description: YourController < RowsController ( < ApplicationController).
56
98
  email:
57
99
  - dittmar.krall@matique.de
@@ -85,11 +127,13 @@ files:
85
127
  - lib/rows/resources.rb
86
128
  - lib/rows/utils.rb
87
129
  - lib/rows/version.rb
130
+ - lib/rows/version.rb.bak
88
131
  - lib/rows_controller.rb
89
132
  homepage: http://matique.de
90
133
  licenses:
91
134
  - MIT
92
- metadata: {}
135
+ metadata:
136
+ source_code_uri: https://github.com/matique/rows_controller
93
137
  post_install_message:
94
138
  rdoc_options: []
95
139
  require_paths:
@@ -105,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
149
  - !ruby/object:Gem::Version
106
150
  version: '0'
107
151
  requirements: []
108
- rubygems_version: 3.0.1
152
+ rubygems_version: 3.2.6
109
153
  signing_key:
110
154
  specification_version: 4
111
155
  summary: RowsController DRYs your controllers.