combo_box 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +66 -0
- data/VERSION +1 -0
- data/lib/assets/javascripts/combo_box.js +42 -0
- data/lib/assets/stylesheets/combo_box.css +53 -0
- data/lib/combo_box/action_controller.rb +85 -0
- data/lib/combo_box/engine.rb +6 -0
- data/lib/combo_box/generator.rb +202 -0
- data/lib/combo_box/helpers/form_tag_helper.rb +82 -0
- data/lib/combo_box/helpers.rb +6 -0
- data/lib/combo_box/railtie.rb +13 -0
- data/lib/combo_box.rb +11 -0
- metadata +109 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Brice Texier
|
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.rdoc
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= ComboBox
|
2
|
+
|
3
|
+
Adds helpers for Rails views and controller in order to manage 'dynamic select'. It uses jQuery UI as support for inobtrusive use in forms. It's not the classic Autocompleter, its use is limited to belongs_to reflections.
|
4
|
+
|
5
|
+
== Rails 3.0
|
6
|
+
|
7
|
+
ComboBox requires the gem +jquery-rails+, so if you don't already use it:
|
8
|
+
rails generate jquery:install --ui
|
9
|
+
|
10
|
+
Then install +combo_box+ in your app like +jquery-rails+:
|
11
|
+
rails generate combo_box:install
|
12
|
+
|
13
|
+
Then the javascript and/or stylesheet files must be included in the application layout:
|
14
|
+
<%= javascript_include_tag('combo_box') -%>
|
15
|
+
<%= stylesheet_link_tag('combo_box') -%>
|
16
|
+
|
17
|
+
== Rails 3.1
|
18
|
+
|
19
|
+
Nothing to install. With the asset pipeline, there is no need to run generators or copy files. So for stylesheets:
|
20
|
+
# app/assets/stylesheets/application.css
|
21
|
+
*= require combo_box
|
22
|
+
|
23
|
+
And for javascripts:
|
24
|
+
# app/assets/javascripts/application.js
|
25
|
+
*= require combo_box
|
26
|
+
|
27
|
+
== Usage
|
28
|
+
|
29
|
+
Classic usage in resource forms:
|
30
|
+
|
31
|
+
# app/controllers/orders_controller.rb
|
32
|
+
class OrdersController < ApplicationController
|
33
|
+
search_for :client
|
34
|
+
end
|
35
|
+
|
36
|
+
# app/views/orders/_form.html.haml
|
37
|
+
combo_box :order, :client
|
38
|
+
|
39
|
+
Usage in non-resource forms with personalized controller method:
|
40
|
+
|
41
|
+
# app/controllers/orders_controller.rb
|
42
|
+
class OrdersController < ApplicationController
|
43
|
+
def search_for_orders
|
44
|
+
orders = Order.find_all_by_number(params[:term])
|
45
|
+
render orders.collect{|r| {:label=>r.number, :id=>r.id}}.to_json
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# app/views/orders/search.html.haml
|
50
|
+
combo_box_tag "search", :search_for_orders
|
51
|
+
|
52
|
+
== Contributing to ComboBox
|
53
|
+
|
54
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
55
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
56
|
+
* Fork the project
|
57
|
+
* Start a feature/bugfix branch
|
58
|
+
* Commit and push until you are happy with your contribution
|
59
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
60
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
61
|
+
|
62
|
+
== Copyright
|
63
|
+
|
64
|
+
Copyright (c) 2011 Brice Texier. See LICENSE.txt for
|
65
|
+
further details.
|
66
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,42 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery Combo Box
|
3
|
+
*/
|
4
|
+
(function ($) {
|
5
|
+
|
6
|
+
// Initializes combo-box controls
|
7
|
+
$.initializeComboBoxes = function () {
|
8
|
+
var element = $(this);
|
9
|
+
if (this.alreadyBound !== 'true') {
|
10
|
+
element.comboBoxCache = element.val();
|
11
|
+
element.valueField = $('#' + element.attr('data-value-container'))[0];
|
12
|
+
if ($.isEmptyObject(element.valueField)) {
|
13
|
+
alert('An input ' + element.id + ' with a "data-combo-box" attribute must contain a "data-value-container" attribute (#' + element.attr('data-value-container') + ')');
|
14
|
+
}
|
15
|
+
element.maxSize = parseInt(element.attr('data-max-size'), 10);
|
16
|
+
if (isNaN(element.maxSize) || element.maxSize === 0) { element.maxSize = 64; }
|
17
|
+
element.size = (element.comboBoxCache.length < 32 ? 32 : element.comboBoxCache.length > element.maxSize ? element.maxSize : element.comboBoxCache.length);
|
18
|
+
|
19
|
+
element.autocomplete({
|
20
|
+
source: element.attr('data-combo-box'),
|
21
|
+
minLength: 1,
|
22
|
+
select: function (event, ui) {
|
23
|
+
var selected = ui.item;
|
24
|
+
element.valueField.value = selected.id;
|
25
|
+
element.comboBoxCache = selected.label;
|
26
|
+
element.attr("size", (element.comboBoxCache.length < 32 ? 32 : element.comboBoxCache.length > element.maxSize ? element.maxSize : element.comboBoxCache.length));
|
27
|
+
$(element.valueField).trigger("emulated:change");
|
28
|
+
return true;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
this.alreadyBound = 'true';
|
32
|
+
}
|
33
|
+
return false;
|
34
|
+
};
|
35
|
+
// Bind elements with the method
|
36
|
+
// $('input[data-combo-box]').ready($.initializeComboBoxes);
|
37
|
+
// $('input[data-combo-box]').ready($.initializeComboBoxes);
|
38
|
+
$(document).ready(function(event) {
|
39
|
+
$('input[data-combo-box]').each($.initializeComboBoxes);
|
40
|
+
});
|
41
|
+
$('input[data-combo-box]').ajaxStop($.initializeComboBoxes);
|
42
|
+
})(jQuery);
|
@@ -0,0 +1,53 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery UI Autocomplete 1.8.16
|
3
|
+
*
|
4
|
+
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
5
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
6
|
+
* http://jquery.org/license
|
7
|
+
*
|
8
|
+
* http://docs.jquery.com/UI/Autocomplete#theming
|
9
|
+
*/
|
10
|
+
.ui-autocomplete { position: absolute; cursor: default; }
|
11
|
+
|
12
|
+
/* workarounds */
|
13
|
+
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
|
14
|
+
|
15
|
+
/*
|
16
|
+
* jQuery UI Menu 1.8.16
|
17
|
+
*
|
18
|
+
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
19
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
20
|
+
* http://jquery.org/license
|
21
|
+
*
|
22
|
+
* http://docs.jquery.com/UI/Menu#theming
|
23
|
+
*/
|
24
|
+
.ui-menu {
|
25
|
+
list-style:none;
|
26
|
+
padding: 2px;
|
27
|
+
margin: 0;
|
28
|
+
display:block;
|
29
|
+
float: left;
|
30
|
+
}
|
31
|
+
.ui-menu .ui-menu {
|
32
|
+
margin-top: -3px;
|
33
|
+
}
|
34
|
+
.ui-menu .ui-menu-item {
|
35
|
+
margin:0;
|
36
|
+
padding: 0;
|
37
|
+
zoom: 1;
|
38
|
+
float: left;
|
39
|
+
clear: left;
|
40
|
+
width: 100%;
|
41
|
+
}
|
42
|
+
.ui-menu .ui-menu-item a {
|
43
|
+
text-decoration:none;
|
44
|
+
display:block;
|
45
|
+
padding:.2em .4em;
|
46
|
+
line-height:1.5;
|
47
|
+
zoom:1;
|
48
|
+
}
|
49
|
+
.ui-menu .ui-menu-item a.ui-state-hover,
|
50
|
+
.ui-menu .ui-menu-item a.ui-state-active {
|
51
|
+
font-weight: normal;
|
52
|
+
margin: -1px;
|
53
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ComboBox
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
def self.included(base) # :nodoc:
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
|
11
|
+
# Generates a default action which is the resource for a combo_box.
|
12
|
+
# It generates an helper which takes in account selected columns for displaying.
|
13
|
+
#
|
14
|
+
# @macro [new] options
|
15
|
+
# @param [Hash] options Options to build controller action
|
16
|
+
# @option options [Array] :columns The columns which are used for search and display
|
17
|
+
# All the content columns are used by default.
|
18
|
+
# A column can be a Symbol/String with its name or Hash with keys (+:name+,
|
19
|
+
# +:filter+, +:interpolation_key+)
|
20
|
+
# @option options [Array,Hash] :conditions Default conditions used in the search query
|
21
|
+
# @option options [String, Hash, Array] :joins To make a join like in +find+
|
22
|
+
# @option options [Integer] :limit (80) Maximum count of items in results
|
23
|
+
# @option options [String] :partial Specify a partial for HTML autocompleter
|
24
|
+
# @option options [String] :filter ('%X%') Filter format used to build search query.
|
25
|
+
# Specific filters can be specified for each column.
|
26
|
+
#
|
27
|
+
# @overload search_for(name, model, options={})
|
28
|
+
# Defines a controller method +search_for_NAME+ which searches for records
|
29
|
+
# of the class +MODEL+.
|
30
|
+
# @param [Symbol] name Name of the datasource
|
31
|
+
# @param [String, Symbol] name Name of the model to use for searching
|
32
|
+
# @macro options
|
33
|
+
#
|
34
|
+
# @overload search_for(name, options={})
|
35
|
+
# Defines a controller method +search_for_NAME+ which searches for records
|
36
|
+
# of the class +NAME+.
|
37
|
+
# @param [Symbol] name
|
38
|
+
# Name of the datasource. This name is used to find the model name
|
39
|
+
# @macro options
|
40
|
+
#
|
41
|
+
# @overload search_for(options={})
|
42
|
+
# Defines a controller method +search_for+ which searches for records corresponding to the
|
43
|
+
# resource controller name. +OrdersController#search_for+ searches for orders.
|
44
|
+
# @macro options
|
45
|
+
#
|
46
|
+
# @example Search clients with Person model
|
47
|
+
# # app/controller/orders_controller.rb
|
48
|
+
# class OrdersController < ApplicationController
|
49
|
+
# ...
|
50
|
+
# search_for :clients, :person
|
51
|
+
# ...
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @example Search all accounts where name contains search and number starts with search
|
55
|
+
# # app/controller/orders_controller.rb
|
56
|
+
# class PeopleController < ApplicationController
|
57
|
+
# ...
|
58
|
+
# search_for :accounts, :columns=>[:name, 'number:X%']
|
59
|
+
# ...
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @example Search for orders among all others
|
63
|
+
# # app/controller/orders_controller.rb
|
64
|
+
# class OrdersController < ApplicationController
|
65
|
+
# ...
|
66
|
+
# search_for
|
67
|
+
# ...
|
68
|
+
# end
|
69
|
+
def search_for(*args)
|
70
|
+
options = args.delete_at(-1) if args[-1].is_a? Hash
|
71
|
+
name, model = args[0], args[1]
|
72
|
+
action_name = "#{__method__}#{'_'+name.to_s if name}"
|
73
|
+
model = model || name || controller_name
|
74
|
+
if [String, Symbol].include?(model.class)
|
75
|
+
model = model.to_s.classify.constantize
|
76
|
+
end
|
77
|
+
generator = Generator::Base.new(self, action_name, model, options)
|
78
|
+
class_eval(generator.controller_action, "#{__FILE__}:#{__LINE__}")
|
79
|
+
ActionView::Base.send(:class_eval, generator.view_code, "#{__FILE__}:#{__LINE__}")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module ComboBox
|
2
|
+
module Generator
|
3
|
+
|
4
|
+
# Column = Struct.new('Column', :name, :filter, :interpolation_key, :full_name)
|
5
|
+
|
6
|
+
class Column
|
7
|
+
attr_reader :name, :filter, :interpolation_key, :column
|
8
|
+
|
9
|
+
# @@count = 0
|
10
|
+
|
11
|
+
def initialize(model, name, options={})
|
12
|
+
@model = model
|
13
|
+
@name = name.to_s
|
14
|
+
@filter = options.delete(:filter) || "%X%"
|
15
|
+
@interpolation_key = options.delete(:interpolation_key) || @name.gsub(/\W/, '_')
|
16
|
+
mdl = @model
|
17
|
+
@through = (options.delete(:through) || []).collect do |reflection|
|
18
|
+
unless model.reflections[reflection.to_sym]
|
19
|
+
raise Exception.new("Model #{model.name} has no reflections #{reflection.inspect}")
|
20
|
+
end
|
21
|
+
model = model.reflections[reflection.to_sym].class_name.constantize
|
22
|
+
reflection.to_sym
|
23
|
+
end
|
24
|
+
@column = foreign_model.columns_hash[@name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def sql_name
|
28
|
+
return "#{foreign_model.table_name}.#{@name}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def value_code(record='record')
|
32
|
+
code = ""
|
33
|
+
value = "#{record}#{'.'+@through.join('.') unless @through.empty?}.#{name}"
|
34
|
+
@through.each_index do |i|
|
35
|
+
code << "#{record}.#{@through[0..i].join('.')}.nil? ? '' : "
|
36
|
+
end
|
37
|
+
if [:date, :datetime, :timestamp].include? self.type
|
38
|
+
code = "(#{code}#{value}.nil? ? '' : ::I18n.localize(#{value}))"
|
39
|
+
else
|
40
|
+
code = "(#{code}#{value}).to_s"
|
41
|
+
end
|
42
|
+
return code
|
43
|
+
end
|
44
|
+
|
45
|
+
def type
|
46
|
+
@column.type
|
47
|
+
end
|
48
|
+
|
49
|
+
def foreign_model
|
50
|
+
model = @model
|
51
|
+
for reflection in @through
|
52
|
+
model = model.reflections[reflection].class_name.constantize
|
53
|
+
end
|
54
|
+
return model
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class Base
|
61
|
+
|
62
|
+
attr_accessor :action_name, :controller, :options
|
63
|
+
|
64
|
+
def initialize(controller, action_name, model, options={})
|
65
|
+
@controller = controller
|
66
|
+
@action_name = action_name.to_sym
|
67
|
+
@options = (options.is_a?(Hash) ? options : {})
|
68
|
+
@model = model
|
69
|
+
columns = @options.delete(:columns)
|
70
|
+
columns ||= @model.content_columns.collect{|x| x.name.to_sym}.delete_if{|c| [:lock_version, :created_at, :updated_at].include?(c)}
|
71
|
+
columns = [columns] unless columns.is_a? Array
|
72
|
+
# Normalize columns
|
73
|
+
@columns = columns.collect do |c|
|
74
|
+
c = c.to_s.split(/\:/) if [String, Symbol].include? c.class
|
75
|
+
c = if c.is_a? Hash
|
76
|
+
Column.new(@model, c.delete(:name), c)
|
77
|
+
elsif c.is_a? Array
|
78
|
+
sliced = c[0].split('.')
|
79
|
+
Column.new(@model, sliced[-1], :filter=>c[1], :interpolation_key=>c[2], :through=>sliced[0..-2])
|
80
|
+
else
|
81
|
+
raise Exception.new("Bad column: #{c.inspect}")
|
82
|
+
end
|
83
|
+
c
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
def controller_code()
|
90
|
+
foreign_record = @model.name.underscore
|
91
|
+
foreign_records = foreign_record.pluralize
|
92
|
+
foreign_records = "many_#{foreign_records}" if foreign_record == foreign_records
|
93
|
+
|
94
|
+
query = []
|
95
|
+
parameters = ''
|
96
|
+
if @options[:conditions].is_a? Hash
|
97
|
+
@options[:conditions].each do |key, value|
|
98
|
+
query << (key.is_a?(Symbol) ? @model.table_name+"."+key.to_s : key.to_s)+'=?'
|
99
|
+
parameters += ', ' + sanitize_conditions(value)
|
100
|
+
end
|
101
|
+
elsif @options[:conditions].is_a? Array
|
102
|
+
conditions = @options[:conditions]
|
103
|
+
if conditions[0].is_a?(String) # SQL
|
104
|
+
query << conditions[0].to_s
|
105
|
+
parameters += ', '+conditions[1..-1].collect{|p| sanitize_conditions(p)}.join(', ') if conditions.size>1
|
106
|
+
else
|
107
|
+
raise Exception.new("First element of an Array can only be String or Symbol.")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# select = "#{@model.table_name}.id AS id"
|
112
|
+
# for c in @columns
|
113
|
+
# select << ", #{c.sql_name} AS #{c.short_name}"
|
114
|
+
# end
|
115
|
+
|
116
|
+
code = ""
|
117
|
+
code << "search, conditions = params[:term], [#{query.join(' AND ').inspect+parameters}]\n"
|
118
|
+
code << "words = search.to_s.mb_chars.downcase.strip.normalize.split(/[\\s\\,]+/)\n"
|
119
|
+
code << "if words.size > 0\n"
|
120
|
+
code << " conditions[0] << '#{' AND ' if query.size>0}('\n"
|
121
|
+
code << " words.each_index do |index|\n"
|
122
|
+
code << " word = words[index].to_s\n"
|
123
|
+
code << " conditions[0] << ') AND (' if index > 0\n"
|
124
|
+
if ActiveRecord::Base.connection.adapter_name == "MySQL"
|
125
|
+
code << " conditions[0] << "+@columns.collect{|column| "LOWER(CAST(#{column.sql_name} AS CHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
|
126
|
+
else
|
127
|
+
code << " conditions[0] << "+@columns.collect{|column| "LOWER(CAST(#{column.sql_name} AS VARCHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
|
128
|
+
end
|
129
|
+
code << " conditions += ["+@columns.collect{|column| column.filter.inspect.gsub('X', '"+word+"').gsub(/(^\"\"\+|\+\"\"\+|\+\"\")/, '')}.join(", ")+"]\n"
|
130
|
+
code << " end\n"
|
131
|
+
code << " conditions[0] << ')'\n"
|
132
|
+
code << "end\n"
|
133
|
+
|
134
|
+
# joins = @options[:joins] ? ", :joins=>"+@options[:joins].inspect : ""
|
135
|
+
# order = ", :order=>"+@columns.collect{|column| "#{column[0]} ASC"}.join(', ').inspect
|
136
|
+
# limit = ", :limit=>"+(@options[:limit]||80).to_s
|
137
|
+
joins = @options[:joins] ? ".joins(#{@options[:joins].inspect}).include(#{@options[:joins].inspect})" : ""
|
138
|
+
order = ".order("+@columns.collect{|c| "#{c.sql_name} ASC"}.join(', ').inspect+")"
|
139
|
+
limit = ".limit(#{@options[:limit]||80})"
|
140
|
+
|
141
|
+
partial = @options[:partial]
|
142
|
+
|
143
|
+
html = "<ul><% for #{foreign_record} in #{foreign_records} -%><li id='<%=#{foreign_record}.id-%>'>"
|
144
|
+
html << "<% content = item_label_for_#{@action_name}_in_#{@controller.controller_name}(#{foreign_record})-%>"
|
145
|
+
# html << "<%content="+#{foreign_record}.#{field.item_label}+" -%>"
|
146
|
+
# html << "<%content="+@columns.collect{|column| "#{foreign_record}['#{column[2]}'].to_s"}.join('+", "+')+" -%>"
|
147
|
+
if partial
|
148
|
+
html << "<%=render(:partial=>#{partial.inspect}, :locals =>{:#{foreign_record}=>#{foreign_record}, :content=>content, :search=>search})-%>"
|
149
|
+
else
|
150
|
+
html << "<%=highlight(content, search)-%>"
|
151
|
+
end
|
152
|
+
html << '</li><%end-%></ul>'
|
153
|
+
|
154
|
+
code << "#{foreign_records} = #{@model.name}.where(conditions)"+joins+order+limit+"\n"
|
155
|
+
# Render HTML is old Style
|
156
|
+
code << "respond_to do |format|\n"
|
157
|
+
code << " format.html { render :inline=>#{html.inspect}, :locals=>{:#{foreign_records}=>#{foreign_records}, :search=>search} }\n"
|
158
|
+
code << " format.json { render :json=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{item_label(foreign_record)}, :id=>#{foreign_record}.id}}.to_json }\n"
|
159
|
+
code << " format.yaml { render :yaml=>#{foreign_records}.collect{|#{foreign_record}| {'label'=>#{item_label(foreign_record)}, 'id'=>#{foreign_record}.id}}.to_yaml }\n"
|
160
|
+
code << " format.xml { render :xml=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{item_label(foreign_record)}, :id=>#{foreign_record}.id}}.to_xml }\n"
|
161
|
+
code << "end\n"
|
162
|
+
return code
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def controller_action()
|
167
|
+
code = "def #{@action_name}\n"
|
168
|
+
code << self.controller_code.strip.gsub(/^/, ' ')+"\n"
|
169
|
+
code << "end\n"
|
170
|
+
# list = code.split("\n"); list.each_index{|x| puts((x+1).to_s.rjust(4)+": "+list[x])}
|
171
|
+
return code
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def view_code()
|
176
|
+
record = 'record'
|
177
|
+
code = "def item_label_for_#{@action_name}_in_#{@controller.controller_name}(#{record})\n"
|
178
|
+
code << " if #{record}.is_a? #{@model.name}\n"
|
179
|
+
code << " return #{item_label(record)}\n"
|
180
|
+
code << " else\n"
|
181
|
+
if Rails.env == "development"
|
182
|
+
code << " return '[UnfoundRecord]'\n"
|
183
|
+
else
|
184
|
+
code << " return ''\n"
|
185
|
+
end
|
186
|
+
code << " end\n"
|
187
|
+
code << "end\n"
|
188
|
+
return code
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
def item_label(record, options={})
|
194
|
+
return "::I18n.translate('views.combo_boxes.#{@controller.controller_name}.#{@action_name}', "+@columns.collect{|c| ":#{c.interpolation_key}=>#{c.value_code(record)}"}.join(', ')+", :default=>'"+@columns.collect{|c| "%{#{c.interpolation_key}}"}.join(', ')+"')"
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ComboBox
|
2
|
+
module Helpers
|
3
|
+
module FormTagHelper
|
4
|
+
|
5
|
+
|
6
|
+
# Returns the list of columns to use in the combo_box for displaying
|
7
|
+
# the current item
|
8
|
+
# @param [String, Symbol] action_name Name of the 'search_for' action
|
9
|
+
# @param [String, Symbol] controller_name Name of the controller of the 'search_for' action
|
10
|
+
#
|
11
|
+
# @return [Array] Lists of symbols corresponding to the 'search_for' columns's names
|
12
|
+
def search_columns_for(action, controller=nil)
|
13
|
+
method_name = "search_columns_for_#{action}_in_#{controller}"
|
14
|
+
if self.respond_to?(method_name)
|
15
|
+
return self.send(method_name)
|
16
|
+
else
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Returns a text field which has the same behavior of +select+ but with a search
|
23
|
+
# action which permits to find easily in very long lists...
|
24
|
+
#
|
25
|
+
# @param [Symbol] object_name Name of the used instance variable
|
26
|
+
# @param [Symbol] method Attribute to control
|
27
|
+
# @param [Symbol,String,Hash] choices
|
28
|
+
# Name of data source like specified in `search_for` or a specific URL
|
29
|
+
# in its String form (like `"orders#search_for"`) or in its Hash form
|
30
|
+
# @param [Hash] options Options to build the control
|
31
|
+
# @param [Hash] html_options Extra-attributes to add to the tags
|
32
|
+
#
|
33
|
+
# @return [String] HTML code of the tags
|
34
|
+
def combo_box(object_name, method, choices = nil, options = {}, html_options = {})
|
35
|
+
object = instance_variable_get("@#{object_name}")
|
36
|
+
if choices.nil?
|
37
|
+
choices = {:action=>"search_for_#{method}"}
|
38
|
+
elsif choices.is_a?(Symbol)
|
39
|
+
choices = {:action=>"search_for_#{choices}"}
|
40
|
+
elsif choices.is_a?(String)
|
41
|
+
action = choices.split(/\#+/)
|
42
|
+
choices = {:action=>action[1], :controller=>action[0]}
|
43
|
+
end
|
44
|
+
choices[:controller] ||= controller.controller_name
|
45
|
+
html = ""
|
46
|
+
html << tag(:input, :type=>:text, "data-combo-box"=>url_for(choices.merge(:format=>:json)), "data-value-container"=>"#{object_name}_#{method}", :value=>send("item_label_for_#{choices[:action]}_in_#{choices[:controller]}", object.send(method)), :size=>html_options.delete(:size)||32)
|
47
|
+
html << hidden_field(object_name, method, html_options)
|
48
|
+
return html.html_safe
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a text field which has the same behavior of +select+ but with a search
|
52
|
+
# action which permits to find easily in very long lists.
|
53
|
+
#
|
54
|
+
# @param [Symbol] name Name of the field
|
55
|
+
# @param [Symbol,String,Hash] choices
|
56
|
+
# Name of data source like specified in `search_for` or a specific URL
|
57
|
+
# in its String form (like `"orders#search_for"`) or in its Hash form
|
58
|
+
# @param [Hash] options Options to build the control
|
59
|
+
# @param [Hash] html_options Extra-attributes to add to the tags
|
60
|
+
#
|
61
|
+
# @option options [String] label Default label to display
|
62
|
+
#
|
63
|
+
# @return [String] HTML code of the tags
|
64
|
+
def combo_box_tag(name, choices = nil, options={}, html_options = {})
|
65
|
+
if choices.nil? or choices == controller_name.to_sym
|
66
|
+
choices = {:action=>"search_for"}
|
67
|
+
elsif choices.is_a?(Symbol)
|
68
|
+
choices = {:action=>"search_for_#{choices}"}
|
69
|
+
elsif choices.is_a?(String)
|
70
|
+
action = choices.split(/\#+/)
|
71
|
+
choices = {:action=>action[1], :controller=>action[0]}
|
72
|
+
end
|
73
|
+
choices[:controller] ||= controller.controller_name
|
74
|
+
html = ""
|
75
|
+
html << tag(:input, :type=>:text, "data-combo-box"=>url_for(choices.merge(:format=>:json)), "data-value-container"=>name, :size=>html_options.delete(:size)||32, :value=>options.delete(:label))
|
76
|
+
html << hidden_field_tag(name, html_options.delete(:value), html_options)
|
77
|
+
return html.html_safe
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ComboBox
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
initializer 'formize.initialize' do
|
5
|
+
ActiveSupport.on_load(:action_view) do
|
6
|
+
include ComboBox::Helpers::FormTagHelper
|
7
|
+
end
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
include ComboBox::ActionController
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/combo_box.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'combo_box/railtie' if defined?(::Rails)
|
2
|
+
require 'combo_box/engine' if defined?(::Rails)
|
3
|
+
|
4
|
+
# :include: ../README.rdoc
|
5
|
+
module ComboBox
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
|
8
|
+
autoload :Helpers
|
9
|
+
autoload :Generator
|
10
|
+
autoload :ActionController
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: combo_box
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brice Texier
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: &19442160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *19442160
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: jquery-rails
|
27
|
+
requirement: &19441680 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *19441680
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &19441200 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *19441200
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rcov
|
49
|
+
requirement: &19440720 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *19440720
|
58
|
+
description: Adds helpers for Rails views and controller in order to manage 'dynamic
|
59
|
+
select'. It uses jQuery UI as support for inobtrusive use in forms. It's not the
|
60
|
+
classic Autocompleter, its use is limited to belongs_to reflections.
|
61
|
+
email: brice.texier@ekylibre.org
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files:
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.rdoc
|
67
|
+
files:
|
68
|
+
- Gemfile
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.rdoc
|
71
|
+
- VERSION
|
72
|
+
- lib/assets/javascripts/combo_box.js
|
73
|
+
- lib/assets/stylesheets/combo_box.css
|
74
|
+
- lib/combo_box.rb
|
75
|
+
- lib/combo_box/action_controller.rb
|
76
|
+
- lib/combo_box/engine.rb
|
77
|
+
- lib/combo_box/generator.rb
|
78
|
+
- lib/combo_box/helpers.rb
|
79
|
+
- lib/combo_box/helpers/form_tag_helper.rb
|
80
|
+
- lib/combo_box/railtie.rb
|
81
|
+
homepage: http://github.com/burisu/combo_box
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
hash: -1073113084261701318
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.8.7
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: Adds the combo_box control in Rails 3 based on jQuery UI
|
109
|
+
test_files: []
|