Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 12 commits
  • 31 files changed
  • 0 commit comments
  • 2 contributors
Commits on Mar 26, 2012
@duritong duritong Introduce multi-assignments
Forms can now also render assignements for has_and_belongs_to_many
and has_many relations. The StandardFormBuilder will render you
a simple multi-select collection_select which you can stylize further.
Note: From version 3.2 on Rails automatically fixes the empty params
      list problem for multi-select lists -> no need to handle that
      in the controller.
06beba1
Commits on Mar 28, 2012
@duritong duritong make respond_with extensible
dry_crud's new and show actions weren't easy extensible with
additional formats. By making respond_with extensible we make it
convinient to add additional format handlers to these actions.
8054d39
Commits on Mar 29, 2012
Pascal Zumkehr open up dependencies upwards cdb3f6a
Pascal Zumkehr open up dependencies upwards c12370d
Pascal Zumkehr add rubyracer development dependency 01cf6b9
@duritong duritong Properly build the nested resources
If we have more than resource we should properly follow it up to
the top level.
d2b5bb6
Pascal Zumkehr content_tag_nested, little refactorings 7a41c47
Pascal Zumkehr Merge remote branch 'duritong/master' a9f93f3
Pascal Zumkehr use has_many helpers in test app 4daaae0
Pascal Zumkehr define named instance variable for crud entry/entries fd907af
Pascal Zumkehr get rid of @entry/@entries instance variables, use helper methods ins…
…tead
40ee335
Pascal Zumkehr fix time zone handling for timestamps, returning for cancel link 3a48709
Showing with 420 additions and 139 deletions.
  1. +1 −0  Gemfile
  2. +6 −0 README.rdoc
  3. +4 −4 Rakefile
  4. +1 −1  dry_crud.gemspec
  5. +2 −2 lib/generators/dry_crud/templates/app/assets/stylesheets/sample.scss
  6. +5 −5 lib/generators/dry_crud/templates/app/controllers/crud_controller.rb
  7. +35 −7 lib/generators/dry_crud/templates/app/controllers/list_controller.rb
  8. +1 −1  lib/generators/dry_crud/templates/app/helpers/crud_helper.rb
  9. +2 −2 lib/generators/dry_crud/templates/app/helpers/list_helper.rb
  10. +43 −12 lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb
  11. +71 −18 lib/generators/dry_crud/templates/app/helpers/standard_helper.rb
  12. +4 −12 lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb
  13. +40 −2 lib/generators/dry_crud/templates/test/crud_test_model.rb
  14. +31 −20 lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb
  15. +46 −23 lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb
  16. +2 −0  lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb
  17. +2 −0  lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb
  18. +60 −0 lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb
  19. +40 −0 lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb
  20. +1 −0  test/templates/Gemfile
  21. +0 −7 test/templates/app/controllers/admin/cities_controller.rb
  22. +1 −0  test/templates/app/views/admin/cities/_attrs.html.erb
  23. +1 −0  test/templates/app/views/admin/cities/_attrs.html.haml
  24. +1 −0  test/templates/app/views/admin/cities/_form.html.erb
  25. +1 −0  test/templates/app/views/admin/cities/_form.html.haml
  26. +1 −4 test/templates/app/views/admin/cities/_list.html.erb
  27. +1 −3 test/templates/app/views/admin/cities/_list.html.haml
  28. +2 −2 test/templates/app/views/people/_attrs.html.erb
  29. +11 −10 test/templates/test/functional/admin/cities_controller_test.rb
  30. +1 −1  test/templates/test/functional/admin/countries_controller_test.rb
  31. +3 −3 test/templates/test/functional/people_controller_test.rb
View
1  Gemfile
@@ -30,6 +30,7 @@ group :assets do
gem 'coffee-rails', '~> 3.2.1'
gem 'uglifier', '>= 1.0.3'
gem 'bootstrap-sass-rails'
+ gem 'therubyracer'
end
gem 'jquery-rails'
View
6 README.rdoc
@@ -181,6 +181,12 @@ Even +belongs_to+ associations are automatically rendered with a select field. B
Yes, it's bad practice to use finder logic in your views! Define the variable <tt>@hometowns</tt> in your controller instead (as shown in the example above), and you do not even have to specify the <tt>:list</tt> option.
+Optionally, also +has_and_belongs_to_many+ and +has_many+ associations can be rendered with a multi-select field. Similar to a +belongs_to+ association, all entries from the associated model are used, but can be overwritten using the <tt>:list</tt> option:
+ <%= f.has_and_belongs_to_many_field :visited_cities, :list => City.where(:is_touristic => true) %>
+
+And yes again, the same regarding bad practice for the +belongs_to+ finder logic in views applies here as well.
+
+<b>Note:</b> +has_and_belongs_to_many+ and +has_many+ associations are not automatically rendered in a form, as they are not included in the default_attrs list, and you have to explicitly include them. You might also want to stylize the multi-select form, for example with the jquery-ui base multiselect: http://www.quasipartikel.at/multiselect/
=== Nested Resources
View
8 Rakefile
@@ -85,12 +85,12 @@ namespace :test do
desc "Adds pagination to the test app"
task :add_pagination => :generate_crud do
list_ctrl = File.join(TEST_APP_ROOT, 'app', 'controllers', 'list_controller.rb')
- file_replace(list_ctrl, "@entries = list_entries",
- "@entries = list_entries.page(params[:page]).per(10)")
+ file_replace(list_ctrl, /def list_entries\n\s+model_scope\s*\n/,
+ "def list_entries\n model_scope.page(params[:page]).per(10)")
file_replace(File.join(TEST_APP_ROOT, 'app', 'views', 'list', 'index.html.erb'),
- "<%= render 'list' %>", "<%= paginate @entries %>\n\n<%= render 'list' %>")
+ "<%= render 'list' %>", "<%= paginate entries %>\n\n<%= render 'list' %>")
file_replace(File.join(TEST_APP_ROOT, 'app', 'views', 'list', 'index.html.haml'),
- "= render 'list'", "= paginate @entries\n\n= render 'list'")
+ "= render 'list'", "= paginate entries\n\n= render 'list'")
end
desc "Use Boostrap in the test app"
View
2  dry_crud.gemspec
@@ -16,7 +16,7 @@ DRY_CRUD_GEMSPEC = Gem::Specification.new do |spec|
Generates simple and extendable controller, views and helpers that support you to DRY up the CRUD code in your Rails project. Start with these elements and build a clean base to efficiently develop your application upon.
END
- spec.add_dependency 'rails', '~> 3.2'
+ spec.add_dependency 'rails', '>= 3.2'
readmes = FileList.new('*') do |list|
list.exclude(/(^|[^.a-z])[a-z]+/)
View
4 lib/generators/dry_crud/templates/app/assets/stylesheets/sample.scss
@@ -155,7 +155,7 @@ input[type=number] {
width: 80px;
}
-textarea {
+textarea, select[multiple] {
width: 180px;
height: 80px;
}
@@ -237,7 +237,7 @@ a.brand:hover {
display: block;
float: left;
margin: 0;
- padding: 8px 12px 8px;
+ padding: 8px 12px 6px;
text-decoration: none;
}
View
10 lib/generators/dry_crud/templates/app/controllers/crud_controller.rb
@@ -28,16 +28,16 @@ class CrudController < ListController
# Show one entry of this model.
# GET /entries/1
# GET /entries/1.json
- def show
- respond_with entry
+ def show(&block)
+ customizable_respond_with(entry, block)
end
# Display a form to create a new entry of this model.
# GET /entries/new
# GET /entries/new.json
- def new
+ def new(&block)
assign_attributes
- respond_with entry
+ customizable_respond_with(entry, block)
end
# Create a new entry of this model from the passed params.
@@ -117,7 +117,7 @@ def destroy(&block)
# Main accessor method for the handled model entry.
def entry
- @entry ||= params[:id] ? find_entry : build_entry
+ get_model_ivar || set_model_ivar(params[:id] ? find_entry : build_entry)
end
# Creates a new model entry.
View
42 lib/generators/dry_crud/templates/app/controllers/list_controller.rb
@@ -6,7 +6,7 @@
# the user the same list as he left it.
class ListController < ApplicationController
- helper_method :model_class, :models_label, :path_args
+ helper_method :model_class, :models_label, :entries, :path_args
delegate :model_class, :models_label, :to => 'self.class'
@@ -18,14 +18,18 @@ class ListController < ApplicationController
# List all entries of this model.
# GET /entries
# GET /entries.json
- def index
- @entries = list_entries
- respond_with @entries
+ def index(&block)
+ customizable_respond_with(entries, block)
end
protected
+
+ # Helper method to access the entries to be displayed in the current index page in an uniform way.
+ def entries
+ get_model_ivar(true) || set_model_ivar(list_entries)
+ end
- # The entries to be displayed in the current index page.
+ # The base relation used to filter the entries
def list_entries
model_scope
end
@@ -44,8 +48,11 @@ def path_args(last)
end
# Convenience method to respond to various formats with the given object.
- def respond_with(object)
+ def customizable_respond_with(object, custom_block=nil)
respond_to do |format|
+ custom_block.call(format, object) if custom_block
+ return if performed?
+
format.html { render_with_callback action_name }
format.json { render :json => object }
end
@@ -57,6 +64,27 @@ def render_with_callback(action)
run_callbacks(:"render_#{action}")
render action unless performed?
end
+
+ # Get the instance variable named after the model_class.
+ # If the collection variable is required, pass true as the second argument.
+ def get_model_ivar(plural = false)
+ name = model_class.name.underscore
+ name = name.pluralize if plural
+ instance_variable_get(:"@#{name}")
+ end
+
+ # Sets an instance variable with the underscored class name if the given value.
+ # If the value is a collection, sets the plural name.
+ def set_model_ivar(value)
+ name = if value.respond_to?(:klass) # ActiveRecord::Relation
+ value.klass.name.pluralize
+ elsif value.respond_to?(:each) # Array
+ value.first.klass.name.pluralize
+ else
+ value.class.name
+ end
+ instance_variable_set(:"@#{name.underscore}", value)
+ end
class << self
# Callbacks
@@ -280,7 +308,7 @@ def parents
# Loads the parent entry for the given ActiveRecord class.
# By default, performs a find with the class_name_id param.
def parent_entry(clazz)
- clazz.find(params["#{clazz.name.underscore}_id"])
+ set_model_ivar(clazz.find(params["#{clazz.name.underscore}_id"]))
end
# An array of objects used in url_for and related functions.
View
2  lib/generators/dry_crud/templates/app/helpers/crud_helper.rb
@@ -12,7 +12,7 @@ def crud_form(*attrs, &block)
standard_form(path_args(entry), *attrs, &block)
end
- # Create a table of the @entries variable with the default or
+ # Create a table of the entries with the default or
# the passed attributes in its columns. An options hash may be given
# as the last argument.
def crud_table(*attrs, &block)
View
4 lib/generators/dry_crud/templates/app/helpers/list_helper.rb
@@ -3,14 +3,14 @@
# is included in CrudController.
module ListHelper
- # Create a table of the @entries variable with the default or
+ # Create a table of the entries with the default or
# the passed attributes in its columns. An options hash may be given
# as the last argument.
def list_table(*attrs, &block)
options = attrs.extract_options!
# only use default attrs if no attrs and no block are given
attributes = (block_given? || attrs.present?) ? attrs : default_attrs
- table(@entries, options) do |t|
+ table(entries, options) do |t|
t.sortable_attrs(*attributes)
yield t if block_given?
end
View
55 lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb
@@ -9,15 +9,14 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
attr_reader :template
- delegate :association, :column_type, :column_property, :captionize,
- :content_tag, :capture, :ta, :add_css_class, :to => :template
+ delegate :association, :column_type, :column_property, :captionize, :ta,
+ :content_tag, :safe_join, :capture, :add_css_class, :assoc_and_id_attr,
+ :to => :template
# Render multiple input fields together with a label for the given attributes.
def labeled_input_fields(*attrs)
options = attrs.extract_options!
- attrs.collect do |a|
- labeled_input_field(a, options.clone)
- end.join("\n").html_safe
+ safe_join(attrs) { |a| labeled_input_field(a, options.clone) }
end
# Render a corresponding input field for the given attribute.
@@ -29,6 +28,8 @@ def input_field(attr, html_options = {})
text_area(attr, html_options)
elsif belongs_to_association?(attr, type)
belongs_to_field(attr, html_options)
+ elsif has_many_association?(attr, type)
+ has_many_field(attr, html_options)
elsif attr.to_s.include?('password')
password_field(attr, html_options)
else
@@ -103,12 +104,23 @@ def datetime_field(attr, html_options = {})
def belongs_to_field(attr, html_options = {})
list = association_entries(attr, html_options)
if list.present?
- collection_select(attr, list, :id, :to_s, select_options(attr), html_options)
+ collection_select(attr, list, :id, :to_s, html_options[:multiple] ? {} : select_options(attr), html_options)
else
ta(:none_available, association(@object, attr))
end
end
+ # Render a multi select element for a :has_many or :has_and_belongs_to_many
+ # association defined by attr.
+ # Use additional html_options for the select element.
+ # To pass a custom element list, specify the list with the :list key or
+ # define an instance variable with the pluralized name of the association.
+ def has_many_field(attr, html_options = {})
+ html_options[:multiple] = true
+ add_css_class(html_options, 'multiselect')
+ belongs_to_field(attr, html_options)
+ end
+
# Renders a marker if the given attr has to be present.
def required_mark(attr)
required?(attr) ? REQUIRED_MARK : ''
@@ -127,10 +139,12 @@ def labeled(attr, caption_or_content = nil, content = nil, &block)
content = caption_or_content
caption_or_content = nil
end
- content_tag(:div,
- label(attr, caption_or_content, :class => 'control-label') +
- content_tag(:div, content, :class => 'controls'),
- :class => 'control-group')
+ caption_or_content ||= captionize(attr, @object.class)
+
+ content_tag(:div, :class => 'control-group') do
+ label(attr, caption_or_content, :class => 'control-label') +
+ content_tag(:div, content, :class => 'controls')
+ end
end
# Depending if the given attribute must be present, return
@@ -161,8 +175,19 @@ def respond_to?(name)
# Returns true if attr is a non-polymorphic belongs_to association,
# for which an input field may be automatically rendered.
def belongs_to_association?(attr, type)
+ association_kind?(attr, type, :belongs_to)
+ end
+
+ # Returns true if attr is a non-polymorphic has_many or
+ # has_and_belongs_to_many association, for which an input field
+ # may be automatically rendered.
+ def has_many_association?(attr, type)
+ association_kind?(attr, type, :has_and_belongs_to_many, :has_many)
+ end
+
+ def association_kind?(attr, type, *macros)
if type == :integer || type.nil?
- assoc = association(@object, attr, :belongs_to)
+ assoc = association(@object, attr, *macros)
assoc.present? && assoc.options[:polymorphic].nil?
else
false
@@ -184,10 +209,16 @@ def association_entries(attr, options)
list
end
+ def has_many_list(attr, options)
+ association_entries(attr, options) do
+ find_has_many_association(@object, attr)
+ end
+ end
+
# Returns true if the given attribute must be present.
def required?(attr)
attr = attr.to_s
- attr, attr_id = attr.end_with?('_id') ? [attr[0..-4], attr] : [attr, "#{attr}_id"]
+ attr, attr_id = assoc_and_id_attr(attr)
validators = @object.class.validators_on(attr) +
@object.class.validators_on(attr_id)
validators.any? {|v| v.kind == :presence }
View
89 lib/generators/dry_crud/templates/app/helpers/standard_helper.rb
@@ -35,6 +35,8 @@ def format_attr(obj, attr)
send(format_attr_method, obj)
elsif assoc = association(obj, attr, :belongs_to)
format_assoc(obj, assoc)
+ elsif assoc = association(obj, attr, :has_many, :has_and_belongs_to_many)
+ format_many_assoc(obj, assoc)
else
format_type(obj, attr)
end
@@ -52,17 +54,18 @@ def labeled(label, content = nil, &block)
# Transform the given text into a form as used by labels or table headers.
def captionize(text, clazz = nil)
+ text = text.to_s
if clazz.respond_to?(:human_attribute_name)
- clazz.human_attribute_name(text)
+ clazz.human_attribute_name(text.end_with?('_ids') ? text[0..-5].pluralize : text)
else
- text.to_s.humanize.titleize
+ text.humanize.titleize
end
end
# Renders a list of attributes with label and value for a given object.
# Optionally surrounded with a div.
def render_attrs(obj, *attrs)
- attrs.collect { |a| labeled_attr(obj, a) }.join("\n").html_safe
+ safe_join(attrs) { |a| labeled_attr(obj, a) }
end
# Renders the formatted content of the given attribute with a label.
@@ -106,20 +109,40 @@ def standard_form(object, *attrs, &block)
form.labeled_input_fields(*attrs)
end
- content << content_tag(:div,
- form.button(ti(:"button.save"), :class => 'btn btn-primary') +
- ' ' +
- cancel_link(object),
- :class => 'form-actions')
+ content << content_tag(:div, :class => 'form-actions') do
+ form.button(ti(:"button.save"), :class => 'btn btn-primary') +
+ ' ' +
+ cancel_link(object)
+ end
content.html_safe
end
end
def cancel_link(object)
- link_to(ti(:"button.cancel"), polymorphic_path(object), :class => 'cancel')
+ link_to(ti(:"button.cancel"), polymorphic_path(object, :returning => true), :class => 'cancel')
end
-
+ # Renders a simple unordered list, which will
+ # simply render all passed items or yield them
+ # to your block.
+ def simple_list(items,ul_options={},&blk)
+ content_tag_nested(:ul, items, ul_options) do |item|
+ content_tag(:li, block_given? ? yield(item) : f(item))
+ end
+ end
+
+ # render a content tag with the collected contents rendered
+ # by &block for each item in collection.
+ def content_tag_nested(tag, collection, options = {}, &block)
+ content_tag(tag, safe_join(collection, &block), options)
+ end
+
+ # Overridden method that takes a block that is executed for each item in array
+ # before appending the results.
+ def safe_join(array, sep = $,, &block)
+ super(block_given? ? array.collect(&block) : array, sep)
+ end
+
######## ACTION LINKS ###################################################### :nodoc:
# A generic helper method to create action links.
@@ -220,7 +243,7 @@ def format_type(obj, attr)
case column_type(obj, attr)
when :time then f(val.to_time)
when :date then f(val.to_date)
- when :datetime, :timestamp then "#{f(val.to_date)} #{f(val.to_time)}"
+ when :datetime, :timestamp then "#{f(val.to_date)} #{f(val.time)}"
when :text then val.present? ? simple_format(h(val)) : EMPTY_STRING
when :decimal then f(val.to_s.to_f)
else f(val)
@@ -240,15 +263,33 @@ def column_property(obj, attr, property)
end
end
- # Formats an active record association
+ # Formats an active record belongs_to association
def format_assoc(obj, assoc)
- if assoc_val = obj.send(assoc.name)
- link_to_unless(no_assoc_link?(assoc, assoc_val), assoc_val, assoc_val)
+ if val = obj.send(assoc.name)
+ assoc_link(assoc, val)
+ else
+ ta(:no_entry, assoc)
+ end
+ end
+
+ # Formats an active record has_and_belongs_to_many or
+ # has_many association.
+ def format_many_assoc(obj, assoc)
+ values = obj.send(assoc.name)
+ if values.size == 1
+ assoc_link(assoc, values.first)
+ elsif values.present?
+ simple_list(values) { |val| assoc_link(assoc, val) }
else
ta(:no_entry, assoc)
end
end
+ # Renders a link to the given association entry.
+ def assoc_link(assoc, val)
+ link_to_unless(no_assoc_link?(assoc, val), val.to_s, val)
+ end
+
# Returns true if no link should be created when formatting the given association.
def no_assoc_link?(assoc, val)
!respond_to?("#{val.class.model_name.underscore}_path".to_sym)
@@ -259,12 +300,24 @@ def no_assoc_link?(assoc, val)
# is given, the association must be of this type, otherwise, any association
# is returned. Returns nil if no association (or not of the given macro) was
# found.
- def association(obj, attr, macro = nil)
+ def association(obj, attr, *macros)
if obj.class.respond_to?(:reflect_on_association)
- name = attr.to_s =~ /_id$/ ? attr.to_s[0..-4].to_sym : attr
+ name = assoc_and_id_attr(attr).first.to_sym
assoc = obj.class.reflect_on_association(name)
- assoc if assoc && (macro.nil? || assoc.macro == macro)
+ assoc if assoc && (macros.blank? || macros.include?(assoc.macro))
+ end
+ end
+
+ # Returns the name of the attr and it's corresponding field
+ def assoc_and_id_attr(attr)
+ attr = attr.to_s
+ attr, attr_id = if attr.end_with?('_id')
+ [attr[0..-4], attr]
+ elsif attr.end_with?('_ids')
+ [attr[0..-5].pluralize, attr]
+ else
+ [attr, "#{attr}_id"]
end
end
-end
+end
View
16 lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb
@@ -11,7 +11,7 @@ class StandardTableBuilder
# Delegate called methods to template.
# including StandardHelper would lead to problems with indirectly called methods.
delegate :content_tag, :format_attr, :column_type, :association,
- :captionize, :add_css_class, :to => :template
+ :captionize, :add_css_class, :content_tag_nested, :to => :template
def initialize(entries, template, options = {})
@entries = entries
@@ -58,7 +58,7 @@ def to_html
add_css_class options, 'table'
content_tag :table, options do
content_tag(:thead, html_header) +
- content_tag(:tbody, safe_join(entries) { |e| html_row(e) })
+ content_tag_nested(:tbody, entries) { |e| html_row(e) }
end
end
@@ -81,25 +81,17 @@ def attr_header(attr)
private
def html_header
- content_tag :tr do
- safe_join(cols) { |c| c.html_header }
- end
+ content_tag_nested(:tr, cols) { |c| c.html_header }
end
def html_row(entry)
- content_tag :tr do
- safe_join(cols) { |c| c.html_cell(entry) }
- end
+ content_tag_nested(:tr, cols) { |c| c.html_cell(entry) }
end
def entry_class
entries.first.class
end
- def safe_join(collection, &block)
- collection.collect(&block).join.html_safe
- end
-
# Helper class to store column information.
class Col < Struct.new(:header, :html_options, :template, :block) #:nodoc:
View
42 lib/generators/dry_crud/templates/test/crud_test_model.rb
@@ -2,6 +2,8 @@
class CrudTestModel < ActiveRecord::Base #:nodoc:
belongs_to :companion, :class_name => 'CrudTestModel'
+ has_and_belongs_to_many :others, :class_name => 'OtherCrudTestModel'
+ has_many :mores, :class_name => 'OtherCrudTestModel', :foreign_key => :more_id
validates :name, :presence => true
validates :rating, :inclusion => { :in => 1..10 }
@@ -18,6 +20,15 @@ def chatty
end
+class OtherCrudTestModel < ActiveRecord::Base #:nodoc:
+ has_and_belongs_to_many :others, :class_name => 'CrudTestModel'
+ belongs_to :more, :foreign_key => :more_id, :class_name => 'CrudTestModel'
+
+ def to_s
+ name
+ end
+end
+
# Controller for the dummy model.
class CrudTestModelsController < CrudController #:nodoc:
HANDLE_PREFIX = 'handle_'
@@ -46,6 +57,18 @@ def destroy
end
end
+ def show
+ super do |format, object|
+ format.html { render :text => 'custom html' } if object.name == 'BBBBB'
+ end
+ end
+
+ def index
+ super do |format, object|
+ format.js { render :text => 'index js'}
+ end
+ end
+
protected
def list_entries
@@ -60,7 +83,7 @@ def list_entries
# custom callback
def handle_name
- if @entry.name == 'illegal'
+ if entry.name == 'illegal'
flash[:error] = "illegal name"
return false
end
@@ -162,6 +185,14 @@ def setup_db
t.timestamps
end
end
+ ActiveRecord::Base.connection.create_table :other_crud_test_models, :force => true do |t|
+ t.string :name, :null => false, :limit => 50
+ t.integer :more_id
+ end
+ ActiveRecord::Base.connection.create_table :crud_test_models_other_crud_test_models, :force => true do |t|
+ t.belongs_to :crud_test_model
+ t.belongs_to :other_crud_test_model
+ end
CrudTestModel.reset_column_information
end
@@ -170,7 +201,7 @@ def setup_db
# Removes the crud_test_models table from the database.
def reset_db
c = ActiveRecord::Base.connection
- [:crud_test_models].each do |table|
+ [:crud_test_models, :other_crud_test_models, :crud_test_models_other_crud_test_models].each do |table|
if c.table_exists?(table)
c.drop_table(table) rescue nil
end
@@ -180,6 +211,7 @@ def reset_db
# Creates 6 dummy entries for the crud_test_models table.
def create_test_data
(1..6).inject(nil) {|prev, i| create(i, prev) }
+ (1..6).each {|i| create_other(i) }
end
# Fixture-style accessor method to get CrudTestModel instances by name
@@ -215,6 +247,12 @@ def create(index, companion)
:remarks => "#{c} #{str(index + 1)} #{str(index + 2)}\n" * (index % 3 + 1))
end
+ def create_other(index)
+ c = str(index)
+ others = CrudTestModel.all[index..(index+2)]
+ OtherCrudTestModel.create!(:name => c, :other_ids => others.collect(&:id), :more_id => others.first.try(:id))
+ end
+
def str(index)
(index + 64).chr * 5
end
View
51 lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb
@@ -8,13 +8,13 @@ def test_index
get :index, test_params
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
+ assert_present entries
end
def test_index_json
get :index, test_params(:format => 'json')
assert_response :success
- assert_present assigns(:entries)
+ assert_present entries
assert @response.body.starts_with?("[{"), @response.body
end
@@ -25,39 +25,39 @@ def test_index_search
get :index, test_params(:q => val[0..((val.size + 1)/ 2)])
assert_response :success
- assert_present assigns(:entries)
- assert assigns(:entries).include?(test_entry)
+ assert_present entries
+ assert entries.include?(test_entry)
end
def test_index_sort_asc
col = model_class.column_names.first
get :index, test_params(:sort => col, :sort_dir => 'asc')
assert_response :success
- assert_present assigns(:entries)
- sorted = assigns(:entries).sort_by &(col.to_sym)
- assert_equal sorted, assigns(:entries)
+ assert_present entries
+ sorted = entries.sort_by &(col.to_sym)
+ assert_equal sorted, entries
end
def test_index_sort_desc
col = model_class.column_names.first
get :index, test_params(:sort => col, :sort_dir => 'desc')
assert_response :success
- assert_present assigns(:entries)
- sorted = assigns(:entries).sort_by &(col.to_sym)
- assert_equal sorted.reverse, assigns(:entries)
+ assert_present entries
+ sorted = entries.sort_by &(col.to_sym)
+ assert_equal sorted.reverse, entries
end
def test_show
get :show, test_params(:id => test_entry.id)
assert_response :success
assert_template 'show'
- assert_equal test_entry, assigns(:entry)
+ assert_equal test_entry, entry
end
def test_show_json
get :show, test_params(:id => test_entry.id, :format => 'json')
assert_response :success
- assert_equal test_entry, assigns(:entry)
+ assert_equal test_entry, entry
assert @response.body.starts_with?("{")
end
@@ -71,15 +71,15 @@ def test_new
get :new, test_params
assert_response :success
assert_template 'new'
- assert assigns(:entry).new_record?
+ assert entry.new_record?
end
def test_create
assert_difference("#{model_class.name}.count") do
post :create, test_params(model_identifier => test_entry_attrs)
end
- assert_redirected_to_show assigns(:entry)
- assert ! assigns(:entry).new_record?
+ assert_redirected_to_show entry
+ assert ! entry.new_record?
assert_test_attrs_equal
end
@@ -95,7 +95,7 @@ def test_edit
get :edit, test_params(:id => test_entry.id)
assert_response :success
assert_template 'edit'
- assert_equal test_entry, assigns(:entry)
+ assert_equal test_entry, entry
end
def test_update
@@ -103,7 +103,7 @@ def test_update
put :update, test_params(:id => test_entry.id, model_identifier => test_entry_attrs)
end
assert_test_attrs_equal
- assert_redirected_to_show assigns(:entry)
+ assert_redirected_to_show entry
end
def test_update_json
@@ -141,7 +141,7 @@ def assert_redirected_to_show(entry)
def assert_test_attrs_equal
test_entry_attrs.each do |key, value|
- actual = assigns(:entry).send(key)
+ actual = entry.send(key)
assert_equal value, actual, "#{key} is expected to be <#{value.inspect}>, got <#{actual.inspect}>"
end
end
@@ -153,6 +153,14 @@ def model_class
def model_identifier
@controller.model_identifier
end
+
+ def entry
+ @controller.send(:entry)
+ end
+
+ def entries
+ @controller.send(:entries)
+ end
# Test object used in several tests
def test_entry
@@ -171,10 +179,13 @@ def test_params(params = {})
def nesting_params
params = {}
# for nested controllers, add parent ids to each request
- Array(@controller.nesting).collect do |p|
+ Array(@controller.nesting).reverse.inject(test_entry) do |parent, p|
if p.is_a?(Class) && p < ActiveRecord::Base
assoc = p.name.underscore
- params["#{assoc}_id"] = test_entry.send(:"#{assoc}_id")
+ params["#{assoc}_id"] = parent.send(:"#{assoc}_id")
+ parent.send(assoc)
+ else
+ parent
end
end
params
View
69 lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb
@@ -28,14 +28,23 @@ def test_setup
def test_index
super
- assert_equal 6, assigns(:entries).size
- assert_equal assigns(:entries).sort_by(&:name), assigns(:entries)
+ assert_equal 6, entries.size
+ assert_equal entries.sort_by(&:name), entries
assert_equal Hash.new, session[:list_params]
+ assert_equal entries, assigns(:crud_test_models)
+ assert_respond_to assigns(:crud_test_models), :klass
+ end
+
+ def test_index_js
+ get :index, test_params(:format => 'js')
+ assert_response :success
+ assert_equal 'index js', @response.body
+ assert_present entries
end
def test_index_search
super
- assert_equal 1, assigns(:entries).size
+ assert_equal 1, entries.size
assert_equal({:q => 'AAAA'}, session[:list_params]['/crud_test_models'])
end
@@ -43,18 +52,18 @@ def test_index_with_custom_options
get :index, :filter => true
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 2, assigns(:entries).size
- assert_equal assigns(:entries).sort_by(&:children).reverse, assigns(:entries)
+ assert_present entries
+ assert_equal 2, entries.size
+ assert_equal entries.sort_by(&:children).reverse, entries
end
def test_index_search_with_custom_options
get :index, :q => 'DDD', :filter => true
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 1, assigns(:entries).size
- assert_equal [CrudTestModel.find_by_name('BBBBB')], assigns(:entries)
+ assert_present entries
+ assert_equal 1, entries.size
+ assert_equal [CrudTestModel.find_by_name('BBBBB')], entries
assert_equal({:q => 'DDD'}, session[:list_params]['/crud_test_models'])
end
@@ -62,9 +71,9 @@ def test_sort_given_column
get :index, :sort => 'children', :sort_dir => 'asc'
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 6, assigns(:entries).size
- assert_equal CrudTestModel.all.sort_by(&:children), assigns(:entries)
+ assert_present entries
+ assert_equal 6, entries.size
+ assert_equal CrudTestModel.all.sort_by(&:children), entries
assert_equal({:sort => 'children', :sort_dir => 'asc'}, session[:list_params]['/crud_test_models'])
end
@@ -72,14 +81,14 @@ def test_sort_virtual_column
get :index, :sort => 'chatty', :sort_dir => 'desc'
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 6, assigns(:entries).size
+ assert_present entries
+ assert_equal 6, entries.size
assert_equal({:sort => 'chatty', :sort_dir => 'desc'}, session[:list_params]['/crud_test_models'])
sorted = CrudTestModel.all.sort_by(&:chatty)
# sort order is ambiguous, use index
- names = assigns(:entries).collect(&:name)
+ names = entries.collect(&:name)
assert names.index('BBBBB') < names.index('AAAAA')
assert names.index('BBBBB') < names.index('DDDDD')
assert names.index('EEEEE') < names.index('AAAAA')
@@ -92,9 +101,9 @@ def test_sort_with_search
get :index, :q => 'DDD', :sort => 'chatty', :sort_dir => 'asc'
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 3, assigns(:entries).size
- assert_equal ['CCCCC', 'DDDDD', 'BBBBB'], assigns(:entries).collect(&:name)
+ assert_present entries
+ assert_equal 3, entries.size
+ assert_equal ['CCCCC', 'DDDDD', 'BBBBB'], entries.collect(&:name)
assert_equal({:sort => 'chatty', :sort_dir => 'asc', :q => 'DDD'}, session[:list_params]['/crud_test_models'])
end
@@ -104,9 +113,9 @@ def test_index_returning
get :index, :returning => true
assert_response :success
assert_template 'index'
- assert_present assigns(:entries)
- assert_equal 3, assigns(:entries).size
- assert_equal ['BBBBB', 'DDDDD', 'CCCCC'], assigns(:entries).collect(&:name)
+ assert_present entries
+ assert_equal 3, entries.size
+ assert_equal ['BBBBB', 'DDDDD', 'CCCCC'], entries.collect(&:name)
assert_equal 'DDD', @controller.params[:q]
assert_equal 'chatty', @controller.params[:sort]
assert_equal 'desc', @controller.params[:sort_dir]
@@ -115,8 +124,20 @@ def test_index_returning
def test_new
super
assert assigns(:companions)
+ assert_equal @controller.send(:entry), assigns(:crud_test_model)
assert_equal [:before_render_new, :before_render_form], @controller.called_callbacks
end
+
+ def test_show
+ super
+ assert_equal @controller.send(:entry), assigns(:crud_test_model)
+ end
+
+ def test_show_with_custom
+ get :show, test_params(:id => crud_test_models(:BBBBB).id)
+ assert_response :success
+ assert_equal 'custom html', @response.body
+ end
def test_create
super
@@ -125,11 +146,13 @@ def test_create
def test_edit
super
+ assert_equal @controller.send(:entry), assigns(:crud_test_model)
assert_equal [:before_render_edit, :before_render_form], @controller.called_callbacks
end
def test_update
super
+ assert_equal @controller.send(:entry), assigns(:crud_test_model)
assert_equal [:before_update, :before_save, :after_save, :after_update], @controller.called_callbacks
end
@@ -144,10 +167,10 @@ def test_create_with_before_callback
post :create, :crud_test_model => {:name => 'illegal', :children => 2}
end
assert_template 'new'
- assert assigns(:entry).new_record?
+ assert entry.new_record?
assert assigns(:companions)
assert flash[:error].present?
- assert_equal 'illegal', assigns(:entry).name
+ assert_equal 'illegal', entry.name
assert_equal [:before_render_new, :before_render_form], @controller.called_callbacks
end
View
2  lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb
@@ -13,6 +13,8 @@ class CrudHelperTest < ActionView::TestCase
include StandardHelper
include ListHelper
include CrudTestHelper
+
+ attr_reader :entries
setup :reset_db, :setup_db, :create_test_data
teardown :reset_db
View
2  lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb
@@ -11,6 +11,8 @@ class ListHelperTest < ActionView::TestCase
include StandardHelper
include CrudTestHelper
include CustomAssertions
+
+ attr_reader :entries
setup :reset_db, :setup_db, :create_test_data
teardown :reset_db
View
60 lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb
@@ -53,6 +53,16 @@ def create_form
assert form.belongs_to_field(:companion_id).html_safe?
end
+ test "input_field dispatches has_and_belongs_to_many attr to select field" do
+ assert_equal form.has_many_field(:other_ids), form.input_field(:other_ids)
+ assert form.has_many_field(:other_ids).html_safe?
+ end
+
+ test "input_field dispatches has_many attr to select field" do
+ assert_equal form.has_many_field(:more_ids), form.input_field(:more_ids)
+ assert form.has_many_field(:more_ids).html_safe?
+ end
+
test "input_fields concats multiple fields" do
result = form.labeled_input_fields(:name, :remarks, :children)
assert result.html_safe?
@@ -93,6 +103,56 @@ def create_form
assert_equal 0, f.scan('</option>').size
end
+ test "has_and_belongs_to_many_field has all options by default" do
+ f = form.has_many_field(:other_ids)
+ assert_equal 6, f.scan('</option>').size
+ end
+
+ test "has_and_belongs_to_many_field with :list option" do
+ list = OtherCrudTestModel.all
+ f = form.has_many_field(:other_ids, :list => [list.first, list.second])
+ assert_equal 2, f.scan('</option>').size
+ end
+
+ test "has_and_belongs_to_many_field with instance variable" do
+ list = OtherCrudTestModel.all
+ @others = [list.first, list.second]
+ f = form.has_many_field(:other_ids)
+ assert_equal 2, f.scan('</option>').size
+ end
+
+ test "has_and_belongs_to_many_field with empty list" do
+ @others = []
+ f = form.has_many_field(:other_ids)
+ assert_match /none available/m, f
+ assert_equal 0, f.scan('</option>').size
+ end
+
+ test "has_many_field has all options by default" do
+ f = form.has_many_field(:more_ids)
+ assert_equal 6, f.scan('</option>').size
+ end
+
+ test "has_many_field with :list option" do
+ list = OtherCrudTestModel.all
+ f = form.has_many_field(:more_ids, :list => [list.first, list.second])
+ assert_equal 2, f.scan('</option>').size
+ end
+
+ test "has_many_field with instance variable" do
+ list = OtherCrudTestModel.all
+ @mores = [list.first, list.second]
+ f = form.has_many_field(:more_ids)
+ assert_equal 2, f.scan('</option>').size
+ end
+
+ test "has_many_field with empty list" do
+ @mores = []
+ f = form.has_many_field(:more_ids)
+ assert_match /none available/m, f
+ assert_equal 0, f.scan('</option>').size
+ end
+
test "string_field sets maxlength attribute if limit" do
assert_match /maxlength="50"/, form.string_field(:name)
end
View
40 lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb
@@ -140,7 +140,47 @@ def format_string_size(obj)
assert_equal "<p>AAAAA BBBBB CCCCC\n<br />AAAAA BBBBB CCCCC\n</p>", format_type(m, :remarks)
assert format_type(m, :remarks).html_safe?
end
+
+ test "format belongs to column without content" do
+ m = crud_test_models(:AAAAA)
+ assert_equal t(:'global.associations.no_entry'), format_attr(m, :companion)
+ end
+
+ test "format belongs to column with content" do
+ m = crud_test_models(:BBBBB)
+ assert_equal "AAAAA", format_attr(m, :companion)
+ end
+
+ test "format has_many column with content" do
+ m = crud_test_models(:CCCCC)
+ assert_equal "<ul><li>AAAAA</li><li>BBBBB</li></ul>", format_attr(m, :others)
+ end
+
+ test "content_tag_nested escapes safe correctly" do
+ html = content_tag_nested(:div, ['a', 'b']) { |e| content_tag(:span, e) }
+ assert_equal "<div><span>a</span><span>b</span></div>", html
+ end
+ test "content_tag_nested escapes unsafe correctly" do
+ html = content_tag_nested(:div, ['a', 'b']) { |e| "<#{e}>" }
+ assert_equal "<div>&lt;a&gt;&lt;b&gt;</div>", html
+ end
+
+ test "content_tag_nested without block" do
+ html = content_tag_nested(:div, ['a', 'b'])
+ assert_equal "<div>ab</div>", html
+ end
+
+ test "safe_join without block" do
+ html = safe_join(['<a>', '<b>'.html_safe])
+ assert_equal "&lt;a&gt;<b>", html
+ end
+
+ test "safe_join with block" do
+ html = safe_join(['a', 'b']) { |e| content_tag(:span, e) }
+ assert_equal "<span>a</span><span>b</span>", html
+ end
+
test "empty table should render message" do
result = table([]) { }
assert result.html_safe?
View
1  test/templates/Gemfile
@@ -25,6 +25,7 @@ group :assets do
gem 'coffee-rails' #, '~> 3.2.1'
gem 'uglifier' #, '>= 1.0.3'
gem 'bootstrap-sass-rails'
+ gem 'therubyracer'
end
gem 'jquery-rails'
View
7 test/templates/app/controllers/admin/cities_controller.rb
@@ -4,11 +4,4 @@ class Admin::CitiesController < AjaxController
self.search_columns = :name, 'countries.name'
- def show
- respond_to do |format|
- format.html { redirect_to_index flash.to_hash }
- format.json { render :json => entry }
- end
- end
-
end
View
1  test/templates/app/views/admin/cities/_attrs.html.erb
@@ -0,0 +1 @@
+<%= render_attrs @city, :people %>
View
1  test/templates/app/views/admin/cities/_attrs.html.haml
@@ -0,0 +1 @@
+= render_attrs @entry, :people
View
1  test/templates/app/views/admin/cities/_form.html.erb
@@ -2,4 +2,5 @@
<%= f.labeled(:name, 'Called') do %>
<%= f.input_field :name %>
<% end %>
+ <%= f.labeled_input_field :person_ids %>
<% end %>
View
1  test/templates/app/views/admin/cities/_form.html.haml
@@ -1,3 +1,4 @@
= crud_form do |f|
= f.labeled(:name, 'Called') do
= f.input_field :name
+ = f.labeled_input_field :person_ids
View
5 test/templates/app/views/admin/cities/_list.html.erb
@@ -1,6 +1,3 @@
<% @title = ti(:country_title, :country => @parents.last) -%>
-<%= crud_table *default_attrs do |t|
- action_col_edit(t)
- action_col_destroy(t)
- end %>
+<%= render 'crud/list' %>
View
4 test/templates/app/views/admin/cities/_list.html.haml
@@ -1,5 +1,3 @@
- @title = ti(:country_title, :country => @parents.last)
-= crud_table *default_attrs do |t|
- - action_col_edit(t)
- - action_col_destroy(t)
+= render 'crud/list'
View
4 test/templates/app/views/people/_attrs.html.erb
@@ -1,5 +1,5 @@
-<%= render_attrs @entry, *default_attrs %>
+<%= render_attrs @person, *default_attrs %>
<%= labeled(ti(:i_think_its), ti(:nice)) %>
<%= labeled(ti(:check_google)) do %>
- <%= link_to ti(:"link.maps"), "http://map.google.com/?q=#{@entry.name}" %>
+ <%= link_to ti(:"link.maps"), "http://map.google.com/?q=#{@person.name}" %>
<% end %>
View
21 test/templates/test/functional/admin/cities_controller_test.rb
@@ -13,27 +13,28 @@ def test_setup
def test_index
super
- assert_equal test_entry.country.cities.order('countries.code, cities.name').to_a, assigns(:entries).to_a
+ assert_equal test_entry.country.cities.order('countries.code, cities.name').to_a, entries.to_a
+ assert_equal @controller.send(:entries), assigns(:cities)
assert_equal [:admin, test_entry.country], @controller.send(:parents)
+ assert_equal test_entry.country, assigns(:country)
assert_equal test_entry.country, @controller.send(:parent)
assert_equal test_entry.country.cities, @controller.send(:model_scope)
assert_equal [:admin, test_entry.country, 2], @controller.send(:path_args, 2)
end
def test_show
- get :show, test_params(:id => test_entry.id)
- assert_redirected_to_index
- end
-
- def test_show_with_non_existing_id_raises_RecordNotFound #not
- get :show, test_params(:id => 9999)
- assert_redirected_to_index
+ super
+
+ assert_equal @controller.send(:entry), assigns(:city)
+ assert_equal [:admin, test_entry.country], @controller.send(:parents)
+ assert_equal test_entry.country, assigns(:country)
+ assert_equal test_entry.country, @controller.send(:parent)
end
-
+#
def test_create
super
- assert_equal test_entry.country, assigns(:entry).country
+ assert_equal test_entry.country, entry.country
end
def test_destroy_with_inhabitants
View
2  test/templates/test/functional/admin/countries_controller_test.rb
@@ -13,7 +13,7 @@ def test_setup
def test_index
super
- assert_equal Country.order('name').all, assigns(:entries)
+ assert_equal Country.order('name').all, entries
assert_equal [:admin], @controller.send(:parents)
assert_nil @controller.send(:parent)
assert_equal Country.scoped, @controller.send(:model_scope)
View
6 test/templates/test/functional/people_controller_test.rb
@@ -13,8 +13,8 @@ def test_setup
def test_index
super
- assert_equal 2, assigns(:entries).size
- assert_equal Person.includes(:city => :country).order('people.name, countries.code, cities.name').all, assigns(:entries)
+ assert_equal 2, entries.size
+ assert_equal Person.includes(:city => :country).order('people.name, countries.code, cities.name').all, entries
assert_equal [], @controller.send(:parents)
assert_nil @controller.send(:parent)
@@ -24,7 +24,7 @@ def test_index
def test_index_search
super
- assert_equal 1, assigns(:entries).size
+ assert_equal 1, entries.size
end
protected

No commit comments for this range

Something went wrong with that request. Please try again.