flexselect 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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