vogue 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +113 -0
  3. data/Rakefile +53 -0
  4. data/VERSION +1 -0
  5. data/lib/vogue.rb +49 -0
  6. data/lib/vogue/dropdowns.rb +9 -0
  7. data/lib/vogue/form_builder.rb +241 -0
  8. data/lib/vogue/helpers.rb +29 -0
  9. data/lib/vogue/partial_locator.rb +22 -0
  10. data/lib/vogue/resource_controller_extensions.rb +36 -0
  11. data/lib/vogue/template_locator.rb +27 -0
  12. data/test/README +243 -0
  13. data/test/Rakefile +10 -0
  14. data/test/app/controllers/application_controller.rb +10 -0
  15. data/test/app/controllers/pants_controller.rb +7 -0
  16. data/test/app/controllers/posts_controller.rb +13 -0
  17. data/test/app/controllers/priorities_controller.rb +3 -0
  18. data/test/app/helpers/application_helper.rb +3 -0
  19. data/test/app/helpers/posts_helper.rb +2 -0
  20. data/test/app/helpers/priorities_helper.rb +2 -0
  21. data/test/app/models/post.rb +3 -0
  22. data/test/app/models/priority.rb +2 -0
  23. data/test/app/views/layouts/standard/_header.html.erb +3 -0
  24. data/test/app/views/layouts/standard/_specific.html.erb +1 -0
  25. data/test/app/views/layouts/standard/_sub_header.html.erb +3 -0
  26. data/test/app/views/layouts/standard/new.html.erb +5 -0
  27. data/test/app/views/posts/_form.html.erb +3 -0
  28. data/test/app/views/posts/_specific.html.erb +1 -0
  29. data/test/config/boot.rb +110 -0
  30. data/test/config/database.yml +22 -0
  31. data/test/config/environment.rb +48 -0
  32. data/test/config/environments/development.rb +17 -0
  33. data/test/config/environments/production.rb +28 -0
  34. data/test/config/environments/test.rb +28 -0
  35. data/test/config/initializers/backtrace_silencers.rb +7 -0
  36. data/test/config/initializers/inflections.rb +10 -0
  37. data/test/config/initializers/mime_types.rb +5 -0
  38. data/test/config/initializers/new_rails_defaults.rb +21 -0
  39. data/test/config/initializers/session_store.rb +15 -0
  40. data/test/config/locales/en.yml +5 -0
  41. data/test/config/routes.rb +47 -0
  42. data/test/db/development.sqlite3 +0 -0
  43. data/test/db/migrate/20100330233049_create_posts.rb +14 -0
  44. data/test/db/migrate/20100330233402_create_priorities.rb +13 -0
  45. data/test/db/schema.rb +28 -0
  46. data/test/db/seeds.rb +12 -0
  47. data/test/db/test.sqlite3 +0 -0
  48. data/test/lib/dropdowns.rb +7 -0
  49. data/test/log/development.log +1728 -0
  50. data/test/log/production.log +0 -0
  51. data/test/log/server.log +0 -0
  52. data/test/log/test.log +638 -0
  53. data/test/public/404.html +30 -0
  54. data/test/public/422.html +30 -0
  55. data/test/public/500.html +30 -0
  56. data/test/public/favicon.ico +0 -0
  57. data/test/public/images/rails.png +0 -0
  58. data/test/public/index.html +275 -0
  59. data/test/public/javascripts/application.js +2 -0
  60. data/test/public/javascripts/controls.js +963 -0
  61. data/test/public/javascripts/dragdrop.js +973 -0
  62. data/test/public/javascripts/effects.js +1128 -0
  63. data/test/public/javascripts/prototype.js +4320 -0
  64. data/test/public/robots.txt +5 -0
  65. data/test/script/about +4 -0
  66. data/test/script/console +3 -0
  67. data/test/script/dbconsole +3 -0
  68. data/test/script/destroy +3 -0
  69. data/test/script/generate +3 -0
  70. data/test/script/performance/benchmarker +3 -0
  71. data/test/script/performance/profiler +3 -0
  72. data/test/script/plugin +3 -0
  73. data/test/script/runner +3 -0
  74. data/test/script/server +3 -0
  75. data/test/test/blueprint.rb +8 -0
  76. data/test/test/fixtures/posts.yml +7 -0
  77. data/test/test/fixtures/priorities.yml +7 -0
  78. data/test/test/functional/pants_controller_test.rb +15 -0
  79. data/test/test/functional/posts_controller_test.rb +27 -0
  80. data/test/test/functional/priorities_controller_test.rb +8 -0
  81. data/test/test/performance/browsing_test.rb +9 -0
  82. data/test/test/test_helper.rb +39 -0
  83. data/test/test/unit/helpers/posts_helper_test.rb +4 -0
  84. data/test/test/unit/helpers/priorities_helper_test.rb +4 -0
  85. data/test/test/unit/post_test.rb +8 -0
  86. data/test/test/unit/priority_test.rb +8 -0
  87. data/vogue.gemspec +163 -0
  88. metadata +201 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Sean St. Quentin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,113 @@
1
+ # Vogue
2
+
3
+ Vogue makes it easier for your to reduce your view code through template inheritance.
4
+
5
+ ## Template Inheritance
6
+
7
+ Vogue extends how Rails handles template searching, catching a ActionView::MissingTemplate error and attempting to find the view at a designated location. Locations can be designated per controller, and can be inherited so all your admin controllers can use different default templates.
8
+
9
+ Long story short, you only need to define a form partial for each controller in your administration views. Any time you want to override the default template file, just create the file in your controller's directory.
10
+
11
+ Your controllers can provide variables to modify the behaviour of your default views. These variables are accessed through a vogue helper.
12
+
13
+ class Admin::UserController < Admin::BaseController
14
+ resource_controller
15
+ vogue :name => "User Account", :root => "layouts/admin"
16
+ end
17
+
18
+ /app/views/layouts/admin/edit.html.erb
19
+
20
+ <h1><%= vogue :name %></h1>
21
+
22
+ <% form_for :object, object_url do |form| %>
23
+ <%= render "form", :form => form %>
24
+
25
+ <%= form.submit "Update" %>
26
+ <% end %>
27
+
28
+ /app/views/admin/users/_form.html.erb
29
+
30
+ <%= form.label :name %>
31
+ <%= form.text_field :name %>
32
+
33
+ With the extensions installed, a GET to /posts/1/edit would cause Rails to attempt to render /app/views/posts/edit.html.erb. If that cannot be found, resource_controller_views will make Rails attempt to render /app/views/common/edit.html.erb. The common edit helper will then attempt to render the "form" partial, which again uses the same mechanism as for standard views. In the second case however it finds the _form.html.erb partial in the controllers own directory, and so renders that.
34
+
35
+
36
+ ## Action Standardisation
37
+
38
+ We're not talking about using REST in your application. That was _sooo_ Rails 2.0 - so we assume you're already using that. We're talking about defining standard ways in which your application responds to XML, JSON, JS, or whatever requests.
39
+
40
+ XML/JSON POSTs should ALWAYS return either 201 or 422 errors, and should always return the object with its location, or the list of errors. Vogue was initially a fork of `resource_controller`, and so this action standardisation can work with resource_controller, or with normal rails.
41
+
42
+ Vogue provides a few extensions to resource_controller:
43
+
44
+ * Searchlogic integration
45
+ * Standard Action Sets
46
+ * Will Paginate integration
47
+
48
+ ### Standard Action Sets
49
+
50
+ Action Sets provide a standard way for you to define how your application responds to various mime-types. For example, many applications now want to provide an XML API, and having to continually provide this in each controller can be a pain.
51
+
52
+
53
+ Action Sets are defined in lib/action_sets/, and would appear like this for `resource_controller`:
54
+
55
+ class Admin::UsersController < Admin::BaseController
56
+ vogue :action_sets => [:js, :json, :xml, :rss]
57
+ end
58
+
59
+ module ActionSets::JsActionSet
60
+ def self.included(base)
61
+ base.class_eval do
62
+
63
+ index.wants.js
64
+ edit.wants.js
65
+ show.wants.js
66
+ update.wants.js
67
+ create.wants.js
68
+ destroy.wants.js
69
+ new_action.wants.js
70
+
71
+ create.failure.wants.js
72
+ update.failure.wants.js
73
+ end
74
+ end
75
+ end
76
+
77
+ For standard Rails, we're still working this out.
78
+
79
+ Simply put, they're just modules which get included in your controller, but having this as an explicit feature will make your think about using it. When this is combined with the Template Inheritance, you've suddenly got a very powerful framework that works with whatever you've designed.
80
+
81
+ ### Searchlogic Integration
82
+
83
+ @search = SomeBody.search(params[:search])
84
+ @somebodies = @search.find(:all)
85
+
86
+ Superfluous! Include resource_controller, and use the searchlogic gem in your config, and this will happen automatically for all indexes.
87
+
88
+ ### Will Paginate Integration
89
+
90
+ @somebodies = @search.paginate(:per_page => 20, :page => params[:page])
91
+
92
+ More superfluousness! Use the will_paginate gem in your config, and this will also happen automatically.
93
+
94
+ You can customise this by passing options into the resource_controller statement.
95
+
96
+ class SomeController < ApplicationController
97
+ vogue :per_page => 20
98
+ end
99
+
100
+ class SomeController < ApplicationController
101
+ vogue :pagination => false
102
+ end
103
+
104
+ Or in your config:
105
+
106
+ Vogue.pagination = false
107
+
108
+
109
+ ## Summary
110
+
111
+ Vogue is about setting your own style, keeping to that style, but letting you break out when required. All the tools are there for you to standardise and DRY some of the more repetitive code points.
112
+
113
+
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "vogue"
8
+ gem.summary = %Q{Rails view inheritance, and handy form builder extensions.}
9
+ gem.description = %Q{Vogue is a handy Rails gem which gives you more mechanisms for reducing your view and controller code as much as possible. It acheives this by encouraging providing additional helper methods in FormBuilder for working with related data, and providing template inheritance. }
10
+ gem.email = "sean.stquentin@gmail.com"
11
+ gem.homepage = "http://github.com/elseano/vogue"
12
+ gem.authors = ["Sean St. Quentin"]
13
+ gem.add_development_dependency "machinist", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "vogue #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,49 @@
1
+ begin
2
+ require_dependency 'application_controller'
3
+ rescue LoadError => e
4
+ require_dependency 'application'
5
+ end
6
+
7
+ module Vogue
8
+ # cattr_accessor :pagination
9
+ # self.pagination = defined?(WillPaginate)
10
+
11
+ module ActionControllerExtension
12
+ unloadable
13
+
14
+ # Adds vogue view inheritance to a controller.
15
+ #
16
+ # Options Include:
17
+ #
18
+ # :scaffold_root => The path which contains the default views to use if they're not present in the controllers view path. Can be an array.
19
+ #
20
+ # Notes:
21
+ # All options given to resource_controller are available to the views and controllers through the instance & class methods #vogue, which returns a hash.
22
+ # This can be useful to define things such as headings, etc at the view level.
23
+ def vogue(options = {})
24
+ if options.blank?
25
+ return respond_to?(:vogue_data) ? vogue_data : nil
26
+ else
27
+ cattr_accessor :vogue_data unless self.respond_to?(:vogue_data)
28
+ self.vogue_data = options
29
+
30
+ require File.join(File.dirname(__FILE__), "vogue/helpers")
31
+ require File.join(File.dirname(__FILE__), "vogue/partial_locator")
32
+ require File.join(File.dirname(__FILE__), "vogue/template_locator")
33
+
34
+ include Vogue::Helpers
35
+ include Vogue::TemplateLocator unless included_modules.include?(Vogue::TemplateLocator)
36
+ ActionView::Partials.send(:include, Vogue::PartialLocator) unless ActionView::Partials.included_modules.include?(Vogue::PartialLocator)
37
+
38
+ if defined?(ResourceController) && included_modules.include?(ResourceController) && !included_modules.include?(Vogue::ResourceControllerExtensions)
39
+ require File.join(File.dirname(__FILE__), "vogue/resource_contorller_extensions")
40
+ include Vogue::ResourceControllerExtensions
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ ActionController::Base.class_eval do
48
+ extend Vogue::ActionControllerExtension
49
+ end
@@ -0,0 +1,9 @@
1
+ module Vogue
2
+ class Dropdowns
3
+ attr_reader :template
4
+
5
+ def initialize(template)
6
+ @template = template
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,241 @@
1
+ module Vogue
2
+ # The Vogue FormBuilder adds a few handy methods for working with related data. Make your own FormBuilders inherit from this
3
+ # to gain all the Voguey goodness.
4
+ class FormBuilder < ActionView::Helpers::FormBuilder
5
+
6
+ # Pulls in selection options from methods defined on Dropdowns, the :from option defines the method name to use. In
7
+ # addition to the from option, many other options are supported which can enhance the functionality of the select field generated.
8
+ #
9
+ # :value_method => The method to use on the data source to obtain the value that should be returned to the server.
10
+ # :text_method => The method to use on the data source to obtain the display text that should be displayed to the user.
11
+ # :from => If a symbol, it calls the relevant method on IProperty::Render::Dropdowns, otherwise it accepts an array.
12
+ # :include_blank => A blank option will be included at the top of the list displaying the assigned string.
13
+ # :include_create => The assigned text will be displayed at the bottom of the list allowing the creation of a new value. This new value is returned to the server.
14
+ # :create_width => How wide the creation text box should be (defaults to 20em).
15
+ # :include_current => Includes the currently selected option at the top of the list. If a string, displays the string, replacing "~" with the current display text.
16
+ def select_field(method = nil, options = {})
17
+ source = options.delete(:from)
18
+ extra_html = []
19
+ html_options = Hash.new
20
+
21
+ id_method = options.delete(:value_method) || "id"
22
+ text_method = options.delete(:text_method) || "name"
23
+ value = object.try(method)
24
+ selections = nil
25
+
26
+ data = if source.is_a?(Symbol)
27
+ drop_down(source, options)
28
+ elsif source.is_a?(Array)
29
+ source
30
+ else
31
+ # Attempt to infer from the field name.
32
+ if reflection = object.class.reflect_on_association(method.to_sym)
33
+ drop_down(reflection.class_name.tableize, options)
34
+ else
35
+ raise "from must be either a symbol or an array"
36
+ end
37
+ end
38
+
39
+ # Check to see if the data is in a grouped format. That is, we're getting [["Group Name", [data...]], ["Another Group", [data...]]].
40
+ selections = if data.first.is_a?(Array) && data.first.last.is_a?(Array)
41
+
42
+ # Check to see if the grouped data has already been converted into an array (instead of a list of domain objects).
43
+ if data.first.last.first.is_a?(Array)
44
+ id_method = :last
45
+ text_method = :first
46
+ end
47
+
48
+ template.option_groups_from_collection_for_select(data, :last, :first, id_method, text_method, value)
49
+ else
50
+ # Check to see if its de-normalized (i.e, we're receiving data like this: [["Name", "Value"], ["Name", "Value"]])
51
+ if data.first.is_a?(Array)
52
+ id_method = :last
53
+ text_method = :first
54
+ elsif !data.first.is_a?(ActiveRecord::Base)
55
+ id_method = :to_s
56
+ text_method = :to_s
57
+ value = value.to_s
58
+ end
59
+
60
+ template.options_from_collection_for_select(data, id_method, text_method, value)
61
+ end
62
+
63
+ if options[:include_blank]
64
+ selections = template.content_tag(:option, options[:include_blank].is_a?(String) ? options[:include_blank] : nil, :value => "") + selections
65
+ end
66
+
67
+ if options[:include_create]
68
+ working_with_new_value = !value.blank? && !selections.any? { |a| a.last.to_s == value.to_s }
69
+
70
+ selections = selections + template.content_tag(:option, options[:include_create], :value => "-new-")
71
+
72
+ if !value.blank? && options[:include_current]
73
+ selections = template.content_tag(:option, options[:include_current].sub("~", value.send(text_method)), :value => value.send(id_method)) + selections
74
+ working_with_new_value = false
75
+ end
76
+
77
+ width = options[:create_width] || "20em"
78
+
79
+ result = []
80
+ if working_with_new_value
81
+ extra_html << template.tag(:input, :type => "text", :id => "#{@object_name}_#{method}_new_input", :name => "#{@object_name}[#{method}]", :style => "width: #{width}", :value => value)
82
+ else
83
+ html_options[:onchange] = "if($F(this) == '-new-') { $('#{@object_name}_#{method}_new').show(); $('#{@object_name}_#{method}').hide(); $('#{@object_name}_#{method}').writeAttribute('disabled', true); $('#{@object_name}_#{method}_new_input').writeAttribute('disabled', false); }"
84
+
85
+ input = template.tag(:input, :type => "text", :id => "#{@object_name}_#{method}_new_input", :name => "#{@object_name}[#{method}]", :style => "width: #{width}", :disabled => "disabled")
86
+ extra_html << template.content_tag(:div, input, :id => "#{@object_name}_#{method}_new", :style => "display: none")
87
+ end
88
+
89
+ result.join
90
+ else
91
+ if !value.blank? && options[:include_current]
92
+ selections = @template.content_tag(:option, options[:include_current].sub("~", value), :value => value) + selections
93
+ end
94
+ end
95
+
96
+ html_options[:onclick] = options.delete(:onclick) if options[:onclick]
97
+ html_options[:class] = options.delete(:class) if options[:class]
98
+ html_options[:style] = options.delete(:style) if options[:style]
99
+
100
+ template.select_tag("#{@object_name}[#{method}]", selections, html_options) + " " + extra_html.join
101
+ end
102
+
103
+ # Displays a list of checkboxes which can be ticked. #method must return an array.
104
+ # Check box items will display the name (or to_s) value of each element obtained by the :from option,
105
+ # of is a block is given, will yield to the block for custom rendering.
106
+ def check_box_array(method, options = {}, &contents)
107
+ data = options.delete(:from)
108
+ data = drop_down(data, options) if data.is_a?(Symbol)
109
+
110
+ columns = options.delete(:columns) || 1
111
+ output = ["<table style='width: 100%; border: 0; border-collapse: collapse'>"]
112
+ output << "<tr>"
113
+
114
+ value = object.send(method)
115
+
116
+ Array(data).each_with_index do |data_item, index|
117
+ if index % columns == 0 && index > 0
118
+ output << "</tr><tr>"
119
+ end
120
+
121
+ output << "<td>"
122
+ output << @template.check_box_tag("#{@object_name}[#{method}][]", data_item.id, value.include?(id_value))
123
+ output << " " + @template.capture(data_item, &contents)
124
+ output << "</td>"
125
+ end
126
+
127
+ output << "</tr></table>"
128
+ @template.concat output.join
129
+ end
130
+
131
+ # Presents an extendable array of fields for a has_many relationship on a model.
132
+ # The model must have accepts_nested_attributes_for defined on the relationship.
133
+ # Use this as you would use #fields_for, but the block within will be rendered many times.
134
+ #
135
+ # For Example:
136
+ # <% form.field_array_for :tasks do |task| %>
137
+ # Name: <%= task.text_field :name %><br/>
138
+ # Priority: <%= task.select_field :priority, :from => :priorities %>
139
+ # <% end %>
140
+ def field_array_for(method, options = {}, &standard_field)
141
+ method_id = "#{@object_name}_#{method}"
142
+ template_id = method_id + "_template"
143
+ array_id = method_id + "_array"
144
+ list_id = method_id + "_instance"
145
+ max_size = options.delete(:max_size)
146
+ container_tag = options.delete(:container_tag) || "div"
147
+ row_tag = options.delete(:row_tag) || "div"
148
+ controls_tag = options.delete(:controls_tag) || "span"
149
+
150
+ # Build the add and remove buttons.
151
+ adder = lambda do |show|
152
+ style = { :class => "field-array-add" }
153
+ style[:style] = "display: none" unless show
154
+
155
+ @template.link_to_function(@template.image_tag("actions/add.png", :class => "icon"), "#{list_id}.add(this)", style)
156
+ end
157
+
158
+ remover = @template.link_to_function(@template.image_tag("actions/delete.png", :class => "icon remove"), "#{list_id}.remove(this)")
159
+
160
+ standard_controls = lambda { |show_add| @template.content_tag(controls_tag, remover + adder.call(show_add)) }
161
+
162
+ # Make a list of all the current values, with their remove link and hidden add link.
163
+ output = []
164
+
165
+ fields_for(method) do |form|
166
+ contents = template.capture(form, &standard_field)
167
+ contents += form.hidden_field(:_destroy, :class => "deletion_marker") unless form.object.new_record?
168
+
169
+ if contents.include?("__controls__")
170
+ contents.sub!("__controls__", standard_controls.call(false))
171
+ else
172
+ contents += " " + standard_controls.call(false)
173
+ end
174
+
175
+ output << @template.content_tag(row_tag, contents, :class => "element")
176
+ end
177
+
178
+ new_object = object.class.reflect_on_association(method).klass.new
179
+ new_contents = nil
180
+
181
+ Rails.logger.debug("[DashFormBuilder] Generating fields for blank records...")
182
+ fields_for(method, new_object, :child_index => "NEW_RECORD") do |form|
183
+ new_contents = @template.capture(form, &standard_field)
184
+
185
+ if new_contents.include?("__controls__")
186
+ new_contents.sub!("__controls__", standard_controls.call(false))
187
+ else
188
+ new_contents += " " + standard_controls.call(false)
189
+ end
190
+
191
+ end
192
+
193
+ template = @template.content_tag(row_tag, new_contents, :class => "element")
194
+ blank_entry = @template.escape_javascript(template)
195
+
196
+ 0.upto(max_size || 0) do
197
+ output << template.gsub(/NEW_RECORD/, Time.now.to_i.to_s)
198
+ end
199
+
200
+ Rails.logger.debug("[DashFormBuilder] Rendering output...")
201
+ render_output = @template.javascript_tag("var #{list_id} = null;") + \
202
+ @template.content_tag(container_tag, output.to_s, :id => array_id, :class => "text_field_array") + \
203
+ @template.javascript_tag("#{list_id} = new DashList('#{array_id}', '#{blank_entry}'#{max_size.blank? ? '' : ',' + max_size.to_s});")
204
+
205
+ @template.concat render_output
206
+ end
207
+
208
+ # Handy if you want to put the controls for the field array somewhere other than the default.
209
+ #
210
+ # For Example:
211
+ # <% form.field_array_for :tasks do |task| %>
212
+ # <%= task.field_array_controls %>
213
+ # <%= task.text_field :name %>
214
+ # <% end %>
215
+ def field_array_controls
216
+ "__controls__"
217
+ end
218
+
219
+
220
+ protected
221
+
222
+ attr_reader :template, :object_name
223
+
224
+ # Override this if you're going to be using a different class name to Dropdowns.
225
+ def drop_downs_instance
226
+ @_vogue_drop_downs ||= ::Dropdowns.new(template)
227
+ end
228
+
229
+ # Fetch the drop down data, override this if you want to support a different method of obtaining this data.
230
+ def drop_down(source, options)
231
+ method_info = drop_downs_instance.method(source)
232
+
233
+ if method_info.arity == 1
234
+ drop_downs_instance.send(source, options)
235
+ else
236
+ drop_downs_instance.send(source)
237
+ end
238
+ end
239
+
240
+ end
241
+ end