flexselect 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Emmanuel Oga for Broadspire, Inc
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,28 @@
1
+ = flexselect
2
+
3
+ This gem is just a convenient packaging of the excellent
4
+
5
+ jQuery plugin flexselect.
6
+
7
+ http://github.com/rmm5t/jquery-flexselect/
8
+
9
+ A very little modification was made to the flexselect()
10
+ function arguments for making it more convenient to use with Rails.
11
+
12
+ In addition it provides an ActiveRecord class method to configure
13
+ certain attributes for autocompletion.
14
+
15
+
16
+ == Note on Patches/Pull Requests
17
+
18
+ * Fork the project.
19
+ * Make your feature addition or bug fix.
20
+ * Add tests for it. This is important so I don't break it in a
21
+ future version unintentionally.
22
+ * Commit, do not mess with rakefile, version, or history.
23
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
24
+ * Send me a pull request. Bonus points for topic branches.
25
+
26
+ == Copyright
27
+
28
+ Copyright (c) 2010 Emmanuel Oga for Broadspire, Inc. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "flexselect"
8
+ gem.summary = %Q{A jQuery plugin and an activerecord extension to provide auto-complete on html select tags}
9
+ gem.description = %Q{For rails applications, requires jQuery}
10
+ gem.email = "EmmanuelOga@gmail.com"
11
+ gem.homepage = "http://github.com/EmmanuelOga/flexselect"
12
+ gem.authors = ["Emmanuel Oga"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_development_dependency "activerecord", ">= 0"
16
+ gem.add_development_dependency "sqlite3-ruby", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ end
35
+
36
+ task :spec => :check_dependencies
37
+
38
+ task :default => :spec
39
+
40
+ begin
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
43
+ rescue LoadError
44
+ task :yardoc do
45
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
46
+ end
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,19 @@
1
+ Description:
2
+ Just copies the javascript and css stylesheets you need to the public directory.
3
+ NOTE: you need to include jQuery on your html page for the plugin to work.
4
+
5
+ Example:
6
+ ./script/generate flexselect
7
+
8
+ This will create:
9
+
10
+ public/stylesheets/flexselect.css
11
+ public/javascripts/jquery.flexselect.js
12
+ public/javascripts/liquidmetal.js
13
+
14
+ Later, on your template:
15
+
16
+ <%= stylesheet_link_tag "flexselect.css" %>
17
+ <%= javascript_include_tag "jquery.js", "liquidmetal.js", "jquery.flexselect.js" %>
18
+
19
+ You get the idea.
@@ -0,0 +1,9 @@
1
+ class FlexselectGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ m.template "flexselect.css" , "public/stylesheets/flexselect.css"
5
+ m.template "jquery.flexselect.js" , "public/javascripts/jquery.flexselect.js"
6
+ m.template "liquidmetal.js" , "public/javascripts/liquidmetal.js"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ .flexselect_dropdown {
2
+ display: none;
3
+ position: absolute;
4
+ z-index: 999;
5
+ margin: 0;
6
+ padding: 0;
7
+ border: 1px solid WindowFrame;
8
+ max-height: 200px;
9
+ overflow-x: hidden;
10
+ overflow-y: auto;
11
+ background-color: Window;
12
+ color: WindowText;
13
+ color: #777;
14
+ text-align: left;
15
+ box-shadow: 0 6px 12px #ccc;
16
+ -webkit-box-shadow: 0 6px 12px #ccc;
17
+ }
18
+
19
+ .flexselect_dropdown ul {
20
+ width: 100%;
21
+ list-style-position: outside;
22
+ list-style: none;
23
+ padding: 0;
24
+ margin: 0;
25
+ }
26
+
27
+ .flexselect_dropdown li {
28
+ margin: 0px;
29
+ padding: 2px 5px;
30
+ cursor: pointer;
31
+ display: block;
32
+ width: 100%;
33
+ font: Menu;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .flexselect_selected {
38
+ background-color: Highlight;
39
+ color: HighlightText;
40
+ }
@@ -0,0 +1,284 @@
1
+ /*
2
+ * flexselect: a jQuery plugin, version: %RELEASE_VERSION% (%RELEASE_DATE%)
3
+ * @requires jQuery v1.3 or later
4
+ *
5
+ * FlexSelect is a jQuery plugin that makes it easy to convert a select box into
6
+ * a Quicksilver-style, autocompleting, flex matching selection tool.
7
+ *
8
+ * For usage and examples, visit:
9
+ * http://rmm5t.github.com/jquery-flexselect/
10
+ *
11
+ * Licensed under the MIT:
12
+ * http://www.opensource.org/licenses/mit-license.php
13
+ *
14
+ * Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
15
+ *
16
+ * Modified by Emmanuel Oga, see for details on
17
+ *
18
+ * http://github.com/EmmanuelOga/flexselect
19
+ *
20
+ * Copyright (c) 2010, Emmanuel Oga for Broadspire Inc.
21
+ */
22
+ (function($) {
23
+ $.flexselect = function(select, options) { this.init(select, options); };
24
+
25
+ $.extend($.flexselect.prototype, {
26
+ settings: {
27
+ allowMismatch: false,
28
+ selectedClass: "flexselect_selected",
29
+ dropdownClass: "flexselect_dropdown",
30
+ inputIdTransform: function(id) { return id + "_flexselect"; },
31
+ inputNameTransform: function(name) { return; },
32
+ dropdownIdTransform: function(id) { return id + "_flexselect_dropdown"; }
33
+ },
34
+ select: null,
35
+ input: null,
36
+ hidden: null,
37
+ dropdown: null,
38
+ dropdownList: null,
39
+ cache: [],
40
+ results: [],
41
+ lastAbbreviation: null,
42
+ abbreviationBeforeFocus: null,
43
+ selectedIndex: 0,
44
+ picked: false,
45
+ dropdownMouseover: false, // Workaround for poor IE behaviors
46
+
47
+ init: function(select, options) {
48
+ $.extend(this.settings, options);
49
+ this.select = $(select);
50
+ this.preloadCache();
51
+ this.renderControls();
52
+ this.wire();
53
+ },
54
+
55
+ preloadCache: function() {
56
+ this.cache = this.select.children("option").map(function() {
57
+ return { name: $.trim($(this).text()), value: $(this).val(), score: 0.0 };
58
+ });
59
+ },
60
+
61
+ renderControls: function() {
62
+ var selected = this.select.children("option:selected");
63
+
64
+ this.hidden = $("<input type='hidden'/>").attr({
65
+ id: this.select.attr("id"),
66
+ name: this.select.attr("name")
67
+ }).val(selected.val());
68
+
69
+ this.input = $("<input type='text' autocomplete='off' />").attr({
70
+ id: this.settings.inputIdTransform(this.select.attr("id")),
71
+ name: this.settings.inputNameTransform(this.select.attr("name")),
72
+ accesskey: this.select.attr("accesskey"),
73
+ tabindex: this.select.attr("tabindex"),
74
+ style: this.select.attr("style")
75
+ }).addClass(this.select.attr("class")).val($.trim(selected.text()));
76
+
77
+ this.dropdown = $("<div></div>").attr({
78
+ id: this.settings.dropdownIdTransform(this.select.attr("id"))
79
+ }).addClass(this.settings.dropdownClass);
80
+ this.dropdownList = $("<ul></ul>");
81
+ this.dropdown.append(this.dropdownList);
82
+
83
+ this.select.after(this.input).after(this.hidden).remove();
84
+ $("body").append(this.dropdown);
85
+ },
86
+
87
+ wire: function() {
88
+ var self = this;
89
+
90
+ this.input.click(function() {
91
+ self.lastAbbreviation = null;
92
+ self.focus();
93
+ });
94
+
95
+ this.input.mouseup(function(event) {
96
+ // This is so Safari selection actually occurs.
97
+ event.preventDefault();
98
+ });
99
+
100
+ this.input.focus(function() {
101
+ self.abbreviationBeforeFocus = self.input.val();
102
+ self.input.select();
103
+ if (!self.picked) self.filterResults();
104
+ });
105
+
106
+ this.input.blur(function() {
107
+ if (!self.dropdownMouseover) {
108
+ self.hide();
109
+ if (!self.picked) self.reset();
110
+ }
111
+ });
112
+
113
+ this.dropdownList.mouseover(function (event) {
114
+ if (event.target.tagName == "LI") {
115
+ var rows = self.dropdown.find("li");
116
+ self.markSelected(rows.index($(event.target)));
117
+ }
118
+ });
119
+ this.dropdownList.mouseleave(function () {
120
+ self.markSelected(-1);
121
+ });
122
+ this.dropdownList.mouseup(function (event) {
123
+ self.pickSelected();
124
+ self.focusAndHide();
125
+ });
126
+ this.dropdown.mouseover(function (event) {
127
+ self.dropdownMouseover = true;
128
+ });
129
+ this.dropdown.mouseleave(function (event) {
130
+ self.dropdownMouseover = false;
131
+ });
132
+ this.dropdown.mousedown(function (event) {
133
+ event.preventDefault();
134
+ });
135
+
136
+ this.input.keyup(function(event) {
137
+ switch (event.keyCode) {
138
+ case 13: // return
139
+ event.preventDefault();
140
+ self.pickSelected();
141
+ self.focusAndHide();
142
+ break;
143
+ case 27: // esc
144
+ event.preventDefault();
145
+ self.reset();
146
+ self.focusAndHide();
147
+ break;
148
+ default:
149
+ self.filterResults();
150
+ break;
151
+ }
152
+ });
153
+
154
+ this.input.keydown(function(event) {
155
+ switch (event.keyCode) {
156
+ case 9: // tab
157
+ self.pickSelected();
158
+ self.hide();
159
+ break;
160
+ case 33: // pgup
161
+ event.preventDefault();
162
+ self.markFirst();
163
+ break;
164
+ case 34: // pgedown
165
+ event.preventDefault();
166
+ self.markLast();
167
+ break;
168
+ case 38: // up
169
+ event.preventDefault();
170
+ self.moveSelected(-1);
171
+ break;
172
+ case 40: // down
173
+ event.preventDefault();
174
+ self.moveSelected(1);
175
+ break;
176
+ case 13: // return
177
+ case 27: // esc
178
+ event.preventDefault();
179
+ event.stopPropagation();
180
+ break;
181
+ }
182
+ });
183
+ },
184
+
185
+ filterResults: function() {
186
+ var abbreviation = this.input.val();
187
+ if (abbreviation == this.lastAbbreviation) return;
188
+
189
+ var results = [];
190
+ $.each(this.cache, function() {
191
+ this.score = LiquidMetal.score(this.name, abbreviation);
192
+ if (this.score > 0.0) results.push(this);
193
+ });
194
+ this.results = results;
195
+
196
+ this.sortResults();
197
+ this.renderDropdown();
198
+ this.markFirst();
199
+ this.lastAbbreviation = abbreviation;
200
+ this.picked = false;
201
+ },
202
+
203
+ sortResults: function() {
204
+ this.results.sort(function(a, b) { return b.score - a.score; });
205
+ },
206
+
207
+ renderDropdown: function() {
208
+ var dropdownBorderWidth = this.dropdown.outerWidth() - this.dropdown.innerWidth();
209
+ var inputOffset = this.input.offset();
210
+ this.dropdown.css({
211
+ width: (this.input.outerWidth() - dropdownBorderWidth) + "px",
212
+ top: (inputOffset.top + this.input.outerHeight()) + "px",
213
+ left: inputOffset.left + "px"
214
+ });
215
+
216
+ var list = this.dropdownList.html("");
217
+ $.each(this.results, function() {
218
+ // list.append($("<li/>").html(this.name + " <small>[" + Math.round(this.score*100)/100 + "]</small>"));
219
+ list.append($("<li/>").html(this.name));
220
+ });
221
+ this.dropdown.show();
222
+ },
223
+
224
+ markSelected: function(n) {
225
+ if (n > this.results.length) return;
226
+
227
+ var rows = this.dropdown.find("li");
228
+ rows.removeClass(this.settings.selectedClass);
229
+ this.selectedIndex = n;
230
+
231
+ if (n >= 0) $(rows[n]).addClass(this.settings.selectedClass);
232
+ },
233
+
234
+ pickSelected: function() {
235
+ var selected = this.results[this.selectedIndex];
236
+ if (selected) {
237
+ this.input.val(selected.name);
238
+ this.hidden.val(selected.value);
239
+ this.picked = true;
240
+ } else if (this.settings.allowMismatch) {
241
+ this.hidden.val("");
242
+ } else {
243
+ this.reset();
244
+ }
245
+ },
246
+
247
+ hide: function() {
248
+ this.dropdown.hide();
249
+ this.lastAbbreviation = null;
250
+ },
251
+
252
+ moveSelected: function(n) { this.markSelected(this.selectedIndex+n); },
253
+ markFirst: function() { this.markSelected(0); },
254
+ markLast: function() { this.markSelected(this.results.length - 1); },
255
+ reset: function() { this.input.val(this.abbreviationBeforeFocus); },
256
+ focus: function() { this.input.focus(); },
257
+ focusAndHide: function() { this.focus(); this.hide(); }
258
+ });
259
+
260
+ $.fn.flexselect = function() {
261
+ // EOGA: Little modification for the flexselect gem.
262
+ // Allow the user to provide just two options,
263
+ // name of the activerecord class and name of the attribute to autocomplete.
264
+ // The third parameter can contain the options object as before.
265
+ var options;
266
+
267
+ if (arguments.length == 1) { var object_opt = arguments[0]; } else { var array_opt = arguments; }
268
+
269
+ this.each(function() {
270
+ if (array_opt) {
271
+ var transf = function(id) { return array_opt[0] + "[" + array_opt[1] + "]"; }
272
+ options = { allowMismatch: true, inputIdTransform: transf };
273
+
274
+ // Allow additional options to follow the string params
275
+ if (array_opt[2]) $.extend(options, array_opt[2]);
276
+ } else {
277
+ options = object_opt;
278
+ }
279
+
280
+ if (this.tagName == "SELECT") new $.flexselect(this, options);
281
+ });
282
+ return this;
283
+ };
284
+ })(jQuery);
@@ -0,0 +1,88 @@
1
+ /*
2
+ * LiquidMetal, version: 0.1 (2009-02-05)
3
+ *
4
+ * A mimetic poly-alloy of Quicksilver's scoring algorithm, essentially
5
+ * LiquidMetal.
6
+ *
7
+ * For usage and examples, visit:
8
+ * http://github.com/rmm5t/liquidmetal
9
+ *
10
+ * Licensed under the MIT:
11
+ * http://www.opensource.org/licenses/mit-license.php
12
+ *
13
+ * Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
14
+ */
15
+ var LiquidMetal = function() {
16
+ var SCORE_NO_MATCH = 0.0;
17
+ var SCORE_MATCH = 1.0;
18
+ var SCORE_TRAILING = 0.8;
19
+ var SCORE_TRAILING_BUT_STARTED = 0.9;
20
+ var SCORE_BUFFER = 0.85;
21
+
22
+ return {
23
+ score: function(string, abbreviation) {
24
+ // Short circuits
25
+ if (abbreviation.length == 0) return SCORE_TRAILING;
26
+ if (abbreviation.length > string.length) return SCORE_NO_MATCH;
27
+
28
+ var scores = this.buildScoreArray(string, abbreviation);
29
+
30
+ var sum = 0.0;
31
+ for (var i in scores) {
32
+ sum += scores[i];
33
+ }
34
+
35
+ return (sum / scores.length);
36
+ },
37
+
38
+ buildScoreArray: function(string, abbreviation) {
39
+ var scores = new Array(string.length);
40
+ var lower = string.toLowerCase();
41
+ var chars = abbreviation.toLowerCase().split("");
42
+
43
+ var lastIndex = -1;
44
+ var started = false;
45
+ for (var i in chars) {
46
+ var c = chars[i];
47
+ var index = lower.indexOf(c, lastIndex+1);
48
+ if (index < 0) return fillArray(scores, SCORE_NO_MATCH);
49
+ if (index == 0) started = true;
50
+
51
+ if (isNewWord(string, index)) {
52
+ scores[index-1] = 1;
53
+ fillArray(scores, SCORE_BUFFER, lastIndex+1, index-1);
54
+ }
55
+ else if (isUpperCase(string, index)) {
56
+ fillArray(scores, SCORE_BUFFER, lastIndex+1, index);
57
+ }
58
+ else {
59
+ fillArray(scores, SCORE_NO_MATCH, lastIndex+1, index);
60
+ }
61
+
62
+ scores[index] = SCORE_MATCH;
63
+ lastIndex = index;
64
+ }
65
+
66
+ var trailingScore = started ? SCORE_TRAILING_BUT_STARTED : SCORE_TRAILING;
67
+ fillArray(scores, trailingScore, lastIndex+1);
68
+ return scores;
69
+ }
70
+ };
71
+
72
+ function isUpperCase(string, index) {
73
+ var c = string.charAt(index);
74
+ return ("A" <= c && c <= "Z");
75
+ }
76
+
77
+ function isNewWord(string, index) {
78
+ var c = string.charAt(index-1);
79
+ return (c == " " || c == "\t");
80
+ }
81
+
82
+ function fillArray(array, value, from, to) {
83
+ from = Math.max(from || 0, 0);
84
+ to = Math.min(to || array.length, array.length);
85
+ for (var i = from; i < to; i++) { array[i] = value; }
86
+ return array;
87
+ }
88
+ }();
data/install.rb ADDED
@@ -0,0 +1,3 @@
1
+ puts "run the generator to create the js and css files you need to include in your html page."
2
+ puts
3
+ puts "script/generate flexselect"
data/lib/flexselect.rb ADDED
@@ -0,0 +1,55 @@
1
+ module Flexselect
2
+
3
+ # Creates an virtual attribute to handle new values for
4
+ # an autocomplete select tag handled by flexquery and a callback
5
+ # to update the attribute using the virtual one.
6
+ #
7
+ # The name of the virtual attribute and the callback is derived
8
+ # from the name of the attribute:
9
+ #
10
+ # attr_flex_selector :thing
11
+ #
12
+ # produces a virtual attribute named "flex_thing_input" and
13
+ # the private method "callback_for_flex_thing_input" for
14
+ # callback.
15
+ #
16
+ # Options:
17
+ #
18
+ # attr :: The attribute to autocomple
19
+ #
20
+ # :on :: ActiveRecord callback to use for updating the attribute.
21
+ # Defaults to :before_validation_on_create
22
+ #
23
+ # :if :: A Proc that is called before updating the attribute.
24
+ # defaults to lambda { |virtual_attribute_value| virtual_attribute_value.present? }
25
+ #
26
+ # &block :: If provided, is called to get the new value for the attribute.
27
+ # Defaults to lambda{ |virtual_attribute_value| virtual_attribute_value }
28
+ #
29
+ def attr_flex_selector(attr, options = {}, &block)
30
+ virtual_attribute = "flex_#{ attr }_input"
31
+ callback_method = "callback_for_#{ virtual_attribute }"
32
+
33
+ attr_accessor virtual_attribute
34
+
35
+ if_lambda = options[:if] || lambda { |new| new.present? }
36
+ action_lambda = block || lambda { |new| new }
37
+
38
+ # A new Module allows to redefine method retaining the old one for "super" usage.
39
+ mod = Module.new do
40
+ define_method(callback_method) do
41
+ value = send(virtual_attribute)
42
+
43
+ if if_lambda.bind(self).call(value)
44
+ send "#{attr}=", action_lambda.bind(self).call(value)
45
+ end
46
+ end
47
+ private(callback_method)
48
+ end
49
+
50
+ include mod
51
+
52
+ send(options[:on] || :before_validation_on_create, callback_method)
53
+ end
54
+
55
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ ActiveRecord::Base.extend Flexselect
@@ -0,0 +1,9 @@
1
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
2
+
3
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
4
+
5
+ ActiveRecord::Schema.define do
6
+ create_table "things" do |t|
7
+ t.string :a, :b, :c, :d
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class Thing < ActiveRecord::Base
4
+
5
+ attr_flex_selector :a
6
+
7
+ attr_flex_selector :b, :on => :after_save
8
+
9
+ attr_flex_selector :c, :if => lambda { |val| val && (val.to_i % 2 == 0) }
10
+
11
+ attr_flex_selector :d do |new|
12
+ [a, b, c, flex_d_input, new].join("-")
13
+ end
14
+ end
15
+
16
+ describe "Flexselect" do
17
+ it "should create an virtual attribute derived from the actual one" do
18
+ Thing.new.should respond_to("flex_a_input")
19
+ Thing.new.should respond_to("flex_a_input=")
20
+ end
21
+
22
+ describe "with defaults" do
23
+ it "should update the attribute with the value on the virtual attribute if it is present" do
24
+ thing = Thing.create! :a => "a", :flex_a_input => "flex a", :b => "unrelated b"
25
+ thing.a.should == "flex a"
26
+ end
27
+
28
+ it "should not update the attribute if the virtual one is not present" do
29
+ thing = Thing.create! :a => "a"
30
+ thing.a.should == "a"
31
+ end
32
+ end
33
+
34
+ describe "using an :on option" do
35
+ it "should determine the calling time of the callback using the provided option" do
36
+ thing = Thing.new :b => "b", :flex_b_input => "flex b"
37
+
38
+ lambda do
39
+ thing.save
40
+ end.should change(thing, :b).from("b").to("flex b")
41
+ end
42
+ end
43
+
44
+ describe "with an :if option" do
45
+ it "should only update the attribute if the provided lambda returns true" do
46
+ thing = Thing.create! :c => "c", :flex_c_input => "1"
47
+ thing.c.should == "c"
48
+
49
+ thing = Thing.create! :c => "c", :flex_c_input => "2"
50
+ thing.c.should == "2"
51
+ end
52
+ end
53
+
54
+ describe "with a custom block" do
55
+ it "should use the custom block to update the attribute value" do
56
+ thing = Thing.create! :a => "a", :b => "b", :c => "c", :d => "d", :flex_d_input => "flex d"
57
+ thing.d.should == "a-b-c-flex d-flex d"
58
+ end
59
+ end
60
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require 'flexselect'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'active_record_helper'
10
+
11
+ # Rails hook.
12
+ require(File.join(File.dirname(__FILE__), '..', 'init.rb'))
13
+
14
+ Spec::Runner.configure do |config|
15
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :flexselect do
3
+ # # Task goes here
4
+ # end
data/uninstall.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Uninstall hook code here
2
+ puts "Goodbye."
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flexselect
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Emmanuel Oga
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-07 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: sqlite3-ruby
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: For rails applications, requires jQuery
56
+ email: EmmanuelOga@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - LICENSE
63
+ - README.rdoc
64
+ files:
65
+ - .document
66
+ - .gitignore
67
+ - LICENSE
68
+ - README.rdoc
69
+ - Rakefile
70
+ - VERSION
71
+ - generators/flexselect/USAGE
72
+ - generators/flexselect/flexselect_generator.rb
73
+ - generators/flexselect/templates/flexselect.css
74
+ - generators/flexselect/templates/jquery.flexselect.js
75
+ - generators/flexselect/templates/liquidmetal.js
76
+ - install.rb
77
+ - lib/flexselect.rb
78
+ - rails/init.rb
79
+ - spec/active_record_helper.rb
80
+ - spec/flexselect_spec.rb
81
+ - spec/spec.opts
82
+ - spec/spec_helper.rb
83
+ - tasks/flexselect_tasks.rake
84
+ - uninstall.rb
85
+ has_rdoc: true
86
+ homepage: http://github.com/EmmanuelOga/flexselect
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options:
91
+ - --charset=UTF-8
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ version:
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ version:
106
+ requirements: []
107
+
108
+ rubyforge_project:
109
+ rubygems_version: 1.3.5
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: A jQuery plugin and an activerecord extension to provide auto-complete on html select tags
113
+ test_files:
114
+ - spec/spec_helper.rb
115
+ - spec/flexselect_spec.rb
116
+ - spec/active_record_helper.rb