vogue 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +113 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/vogue.rb +49 -0
- data/lib/vogue/dropdowns.rb +9 -0
- data/lib/vogue/form_builder.rb +241 -0
- data/lib/vogue/helpers.rb +29 -0
- data/lib/vogue/partial_locator.rb +22 -0
- data/lib/vogue/resource_controller_extensions.rb +36 -0
- data/lib/vogue/template_locator.rb +27 -0
- data/test/README +243 -0
- data/test/Rakefile +10 -0
- data/test/app/controllers/application_controller.rb +10 -0
- data/test/app/controllers/pants_controller.rb +7 -0
- data/test/app/controllers/posts_controller.rb +13 -0
- data/test/app/controllers/priorities_controller.rb +3 -0
- data/test/app/helpers/application_helper.rb +3 -0
- data/test/app/helpers/posts_helper.rb +2 -0
- data/test/app/helpers/priorities_helper.rb +2 -0
- data/test/app/models/post.rb +3 -0
- data/test/app/models/priority.rb +2 -0
- data/test/app/views/layouts/standard/_header.html.erb +3 -0
- data/test/app/views/layouts/standard/_specific.html.erb +1 -0
- data/test/app/views/layouts/standard/_sub_header.html.erb +3 -0
- data/test/app/views/layouts/standard/new.html.erb +5 -0
- data/test/app/views/posts/_form.html.erb +3 -0
- data/test/app/views/posts/_specific.html.erb +1 -0
- data/test/config/boot.rb +110 -0
- data/test/config/database.yml +22 -0
- data/test/config/environment.rb +48 -0
- data/test/config/environments/development.rb +17 -0
- data/test/config/environments/production.rb +28 -0
- data/test/config/environments/test.rb +28 -0
- data/test/config/initializers/backtrace_silencers.rb +7 -0
- data/test/config/initializers/inflections.rb +10 -0
- data/test/config/initializers/mime_types.rb +5 -0
- data/test/config/initializers/new_rails_defaults.rb +21 -0
- data/test/config/initializers/session_store.rb +15 -0
- data/test/config/locales/en.yml +5 -0
- data/test/config/routes.rb +47 -0
- data/test/db/development.sqlite3 +0 -0
- data/test/db/migrate/20100330233049_create_posts.rb +14 -0
- data/test/db/migrate/20100330233402_create_priorities.rb +13 -0
- data/test/db/schema.rb +28 -0
- data/test/db/seeds.rb +12 -0
- data/test/db/test.sqlite3 +0 -0
- data/test/lib/dropdowns.rb +7 -0
- data/test/log/development.log +1728 -0
- data/test/log/production.log +0 -0
- data/test/log/server.log +0 -0
- data/test/log/test.log +638 -0
- data/test/public/404.html +30 -0
- data/test/public/422.html +30 -0
- data/test/public/500.html +30 -0
- data/test/public/favicon.ico +0 -0
- data/test/public/images/rails.png +0 -0
- data/test/public/index.html +275 -0
- data/test/public/javascripts/application.js +2 -0
- data/test/public/javascripts/controls.js +963 -0
- data/test/public/javascripts/dragdrop.js +973 -0
- data/test/public/javascripts/effects.js +1128 -0
- data/test/public/javascripts/prototype.js +4320 -0
- data/test/public/robots.txt +5 -0
- data/test/script/about +4 -0
- data/test/script/console +3 -0
- data/test/script/dbconsole +3 -0
- data/test/script/destroy +3 -0
- data/test/script/generate +3 -0
- data/test/script/performance/benchmarker +3 -0
- data/test/script/performance/profiler +3 -0
- data/test/script/plugin +3 -0
- data/test/script/runner +3 -0
- data/test/script/server +3 -0
- data/test/test/blueprint.rb +8 -0
- data/test/test/fixtures/posts.yml +7 -0
- data/test/test/fixtures/priorities.yml +7 -0
- data/test/test/functional/pants_controller_test.rb +15 -0
- data/test/test/functional/posts_controller_test.rb +27 -0
- data/test/test/functional/priorities_controller_test.rb +8 -0
- data/test/test/performance/browsing_test.rb +9 -0
- data/test/test/test_helper.rb +39 -0
- data/test/test/unit/helpers/posts_helper_test.rb +4 -0
- data/test/test/unit/helpers/priorities_helper_test.rb +4 -0
- data/test/test/unit/post_test.rb +8 -0
- data/test/test/unit/priority_test.rb +8 -0
- data/vogue.gemspec +163 -0
- 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.
|
data/README.markdown
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -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
|
data/lib/vogue.rb
ADDED
@@ -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,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
|