combo_box 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3"
4
+ gem "jquery-rails"
5
+
6
+ group :development do
7
+ gem "jeweler", "~> 1.6.4"
8
+ gem "rcov", ">= 0"
9
+ end
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,6 @@
1
+ # encoding: utf-8
2
+ module ComboBox
3
+ # Required for assets pipeline
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ 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,6 @@
1
+ module ComboBox
2
+ # @private
3
+ module Helpers
4
+ autoload :FormTagHelper, 'combo_box/helpers/form_tag_helper'
5
+ end
6
+ 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: []