assoc_whisperer 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f848a3a5d1cb39a1d9d6e9c484e4172f37e359fb
4
+ data.tar.gz: bb2e200d77eeef53c57261290b41f038aa6019ca
5
+ SHA512:
6
+ metadata.gz: 73df66f03af82aff21ff584edd67092a1879925c60d94654d563218b94c6f6e40e0f797b15313b9db264f527f19db14db2a7a48d96d6ed9b42ed8c1025851534
7
+ data.tar.gz: 9803f688879fe6037989afebec937d80d6f7f22069b9c0949cd2b94e40c7ad5548ffb1ef5bdea214f6e5bba119914b701b2109a281b5ee954aed1425284c4fc4
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in assoc_whisperer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ondřej Želazko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # AssocWhisperer
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'assoc_whisperer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install assoc_whisperer
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/assoc_whisperer/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1 @@
1
+ (function(){var e,t=function(e,t){return function(){return e.apply(t,arguments)}};document.AssocWhispering={all:[],working:false,find:function(e){var t,n,r,i;i=document.AssocWhispering.all;for(n=0,r=i.length;n<r;n++){t=i[n];if(t._action===e){return t}}return null},selectTextFor:function(e,t){var n;n=document.AssocWhispering.find(t);n._last_input="§";return n.getList("",false,function(){return n.selectText(e)})},selectValueFor:function(e,t){var n;n=document.AssocWhispering.find(t);n._last_input="§";return n.getList("",false,function(){switch(e){case"#1":return n.select(n._list_tag.find("span:first"));default:return n.selectValue(e)}})}};$(document).ready(function(){return $(".assoc_whisperer").each(function(t,n){var r;r=new e(n);return document.AssocWhispering.all.push(r)})});e=function(){function e(e){this.setUnfilled=t(this.setUnfilled,this);this.selectText=t(this.selectText,this);this.selectValue=t(this.selectValue,this);this.select=t(this.select,this);this.removeList=t(this.removeList,this);this.deactivateList=t(this.deactivateList,this);this.showList=t(this.showList,this);this.getList=t(this.getList,this);this.controls_blur=t(this.controls_blur,this);this.dropdown_button_click=t(this.dropdown_button_click,this);this.text_field_keyup=t(this.text_field_keyup,this);this._tag=$(e);this._action=this._tag.attr("data-action");this._url=this._tag.attr("data-url");this._text_field=this._tag.children(".text_field");this._value_field=this._tag.children(".value_field");this._text_field.keyup(this.text_field_keyup);this._text_field.click(function(e){return function(){return e._text_field.select()}}(this));this._text_field.focus(function(e){return function(){return e._focus="text_field"}}(this));this._text_field.blur(this.controls_blur);this._tag.children(".dropdown_button").click(this.dropdown_button_click);this._is_filled=this._value_field.val()!==""}e.prototype._tag=null;e.prototype._text_field=null;e.prototype._value_field=null;e.prototype._list_tag=null;e.prototype._action=null;e.prototype._url=null;e.prototype._timeout=null;e.prototype._last_input="§";e.prototype._is_filled=false;e.prototype._focus=null;e.prototype.text_field_keyup=function(e){var t,n;if(e.keyCode===40){return}else if(e.keyCode===27){this.removeList();return}n=this._text_field.val();if(this._timeout){clearTimeout(this._timeout)}if(n!==this._last_input&&this._is_filled){this.setUnfilled()}if(n===""){this._last_input=n;return this.removeList()}else{t=function(e){return function(){return e.getList(n)}}(this);return this._timeout=setTimeout(t,700)}};e.prototype.dropdown_button_click=function(){this._last_input="§";if(this._is_filled){return this.getList("",true)}else{return this.getList(this._text_field.val(),true)}};e.prototype.controls_blur=function(){this._focus=null;return setTimeout(function(e){return function(){if(!e._focus){return e.removeList()}}}(this),100)};e.prototype.getList=function(e,t,n){if(t==null){t=false}if(n==null){n=null}if(e===this._last_input){return}this._last_input=e;this.deactivateList();document.AssocWhispering.working=true;return $.ajax(this._tag.attr("data-url"),{type:"GET",dataType:"html",data:{data_action:this._tag.attr("data-action"),input:e},error:this.removeList,success:function(e){return function(r){e.showList(r);if(t){e._list_tag.focus()}if(n){n()}return document.AssocWhispering.working=false}}(this)})};e.prototype.showList=function(e){var t;this.removeList();this._list_tag=$(e);this._list_tag.css("min-width",this._tag.width());this._list_tag.css("left",this._tag.offset().left);this._list_tag.css("top",this._tag.position().top+this._tag.outerHeight());this._tag.append(this._list_tag);this._list_tag.focus(function(e){return function(){return e._focus="list"}}(this));this._list_tag.blur(this.controls_blur);t=this._list_tag.find("span");return t.click(function(e){return function(t){return e.select($(t.currentTarget))}}(this))};e.prototype.deactivateList=function(){if(!this._list_tag){return}return this._list_tag.addClass("inactive")};e.prototype.removeList=function(){if(!this._list_tag){return}this._list_tag.detach();return this._list_tag=null};e.prototype.select=function(e){var t;if(e.length!==1){return}t=e.text();this._text_field.val(t);this._value_field.val(e.attr("data-value"));this._last_input=t;this._is_filled=true;this._text_field.removeClass("unfilled");return this.removeList()};e.prototype.selectValue=function(e){return this.select(this._list_tag.find("span[data-value="+e+"]"))};e.prototype.selectText=function(e){return this.select(this._list_tag.find('span:contains("'+e+'")'))};e.prototype.setUnfilled=function(){this._is_filled=false;this._value_field.val("");return this._text_field.addClass("unfilled")};return e}()}).call(this)
@@ -0,0 +1,177 @@
1
+ document.AssocWhispering = {
2
+ # Stores all instantiated Whisperer objects.
3
+ all: []
4
+ # Any active Whisperer#getList method holds this variable in true state.
5
+ working: false
6
+ # Return a Whisperer instance by its data-action attribute.
7
+ find: (action) ->
8
+ for w in document.AssocWhispering.all
9
+ return w if w._action==action
10
+ return null
11
+ # Makes a whisperer (found by its data-action) to select the given text, if present.
12
+ selectTextFor: (text, whisp_action) ->
13
+ w = document.AssocWhispering.find(whisp_action)
14
+ w._last_input = '§'
15
+ w.getList('', false, ->
16
+ w.selectText(text)
17
+ )
18
+ # Makes a whisperer (found by its data-action) to select the given value, if present.
19
+ # Selects the first value if the given value is '#1'.
20
+ selectValueFor: (value, whisp_action) ->
21
+ w = document.AssocWhispering.find(whisp_action)
22
+ w._last_input = '§'
23
+ w.getList('', false, ->
24
+ switch value
25
+ when '#1' then w.select(w._list_tag.find('span:first'))
26
+ else w.selectValue(value)
27
+ )
28
+ }
29
+
30
+ $(document).ready ->
31
+ # attach all whisperers to after document id loaded
32
+ $('.assoc_whisperer').each (i, el) ->
33
+ w = new Whisperer(el)
34
+ document.AssocWhispering.all.push(w)
35
+
36
+ # This is the class that is instantiated and hold behind a tag with 'assoc_whisperer' css class.
37
+ # It holds an action and url that is supposed to call when user changes the input text field.
38
+ # Request is sent 700ms after last input key hit.
39
+ # On success response attaches the list and shows it.
40
+ # One can click on the drop down button to have the list generated for empty input (ie. '' string).
41
+ class Whisperer
42
+ _tag: null
43
+ _text_field: null
44
+ _value_field: null
45
+ _list_tag: null
46
+ _action: null
47
+ _url: null
48
+
49
+ _timeout: null
50
+ _last_input: '§'
51
+ _is_filled: false
52
+ _focus: null
53
+
54
+ # Constructor takes the element tag, that it shoud hang to (and which holds all the settings).
55
+ constructor: (element) ->
56
+ @_tag = $(element)
57
+ @_action = @_tag.attr('data-action')
58
+ @_url = @_tag.attr('data-url')
59
+ @_text_field = @_tag.children('.text_field')
60
+ @_value_field = @_tag.children('.value_field')
61
+
62
+ @_text_field.keyup @text_field_keyup
63
+ @_text_field.click => @_text_field.select()
64
+ @_text_field.focus => @_focus = 'text_field'
65
+ @_text_field.blur @controls_blur
66
+ @_tag.children('.dropdown_button').click @dropdown_button_click
67
+
68
+ @_is_filled = @_value_field.val()!=''
69
+
70
+ ########################## E V E N T S ##############################################
71
+
72
+ # Catchs ever key pressed to send a request only after the last one (applying 700ms timeout).
73
+ text_field_keyup: (e) =>
74
+ if e.keyCode==40 #down
75
+ return
76
+ else if e.keyCode==27 #escape
77
+ @removeList()
78
+ return
79
+ input = @_text_field.val()
80
+ clearTimeout(@_timeout) if @_timeout
81
+ @setUnfilled() if input!=@_last_input && @_is_filled
82
+ if input==''
83
+ @_last_input = input
84
+ @removeList()
85
+ else
86
+ fnc = => @getList(input)
87
+ @_timeout = setTimeout(fnc, 700)
88
+
89
+ # A 'button' to show the whole menu list (like entering an empty input).
90
+ dropdown_button_click: =>
91
+ @_last_input = '§'
92
+ if @_is_filled
93
+ @getList('', true)
94
+ else
95
+ @getList(@_text_field.val(), true)
96
+
97
+ # Called by both text_field and list, to ensure that if focused anything else, the list hides itself.
98
+ controls_blur: =>
99
+ @_focus = null
100
+ setTimeout( =>
101
+ @removeList() unless @_focus
102
+ ,100)
103
+
104
+ ########################## L I S T ################################################
105
+
106
+ # An Ajax request sent to a server for given url with given action and input as params.
107
+ # Fires only if input changed. Before the request it visualy deactivates the list, hides it on error.
108
+ # If success, attaches the list and fires onShow callback, if defined.
109
+ getList: (input, focus=false, onShown=null) =>
110
+ return if input==@_last_input
111
+ @_last_input = input
112
+ @deactivateList()
113
+ document.AssocWhispering.working = true;
114
+ $.ajax(@_tag.attr('data-url'),
115
+ type: 'GET'
116
+ dataType: 'html'
117
+ data: {data_action: @_tag.attr('data-action'), input: input}
118
+ error: @removeList
119
+ success: (data) =>
120
+ @showList(data)
121
+ @_list_tag.focus() if focus
122
+ onShown() if onShown
123
+ document.AssocWhispering.working = false;
124
+ )
125
+
126
+ # Attaches a list to html document, within the Whisperer's tag and sets its position to be under the input text field.
127
+ showList: (data) =>
128
+ @removeList()
129
+ @_list_tag = $(data)
130
+ @_list_tag.css('min-width', @_tag.width())
131
+ @_list_tag.css('left', @_tag.offset().left)
132
+ @_list_tag.css('top', @_tag.position().top + @_tag.outerHeight())
133
+
134
+ @_tag.append(@_list_tag)
135
+ @_list_tag.focus => @_focus = 'list'
136
+ @_list_tag.blur @controls_blur
137
+
138
+ rows = @_list_tag.find('span')
139
+ rows.click (el) => @select($(el.currentTarget))
140
+
141
+ # Adds a class 'inactive' to list to make it visualy distinguishable.
142
+ deactivateList: =>
143
+ return unless @_list_tag
144
+ @_list_tag.addClass('inactive')
145
+
146
+ # Detaches list from html, ie. hides it.
147
+ removeList: =>
148
+ return unless @_list_tag
149
+ @_list_tag.detach()
150
+ @_list_tag = null
151
+
152
+ ########################## S E L E C T I O N ############################################
153
+
154
+ # Selects given row and sets its value to the hidden value field and removes the list.
155
+ select: (row) =>
156
+ return unless row.length==1
157
+ text = row.text()
158
+ @_text_field.val(text)
159
+ @_value_field.val(row.attr('data-value'))
160
+ @_last_input = text
161
+ @_is_filled = true
162
+ @_text_field.removeClass('unfilled')
163
+ @removeList()
164
+
165
+ # Selects row by given value.
166
+ selectValue: (value) =>
167
+ @select(@_list_tag.find('span[data-value='+value+']'))
168
+
169
+ # Selects row by given text label.
170
+ selectText: (text) =>
171
+ @select(@_list_tag.find('span:contains("'+text+'")'))
172
+
173
+ # Sets Whisperer to 'unfilled' state - no value has been selected (applies css class 'unfilled').
174
+ setUnfilled: =>
175
+ @_is_filled = false
176
+ @_value_field.val('')
177
+ @_text_field.addClass('unfilled')
@@ -0,0 +1,48 @@
1
+ .assoc_whisperer {
2
+ border: inset 2px lightgrey;
3
+ background-color: #e8d7d2;
4
+ }
5
+ .assoc_whisperer .text_field {
6
+ border: none;
7
+ background-color: transparent;
8
+ padding: 1px;
9
+ }
10
+ .assoc_whisperer .unfilled {
11
+ color: darkred;
12
+ }
13
+ .assoc_whisperer .dropdown_button {
14
+ padding-left: 2px;
15
+ padding-right: 2px;
16
+ border: 2px lightgrey outset;
17
+ background-color: gainsboro;
18
+ font-size: 0.9em;
19
+ -webkit-touch-callout: none;
20
+ -webkit-user-select: none;
21
+ -khtml-user-select: none;
22
+ -moz-user-select: none;
23
+ -ms-user-select: none;
24
+ user-select: none;
25
+ cursor: default;
26
+ }
27
+ .assoc_whisperer .list {
28
+ max-height: 300px;
29
+ overflow-y: scroll;
30
+ border: 1px solid gray;
31
+ position: absolute;
32
+ background-color: #e8d7d2;
33
+ font-weight: normal;
34
+ }
35
+ .assoc_whisperer .list span {
36
+ display: table-row;
37
+ text-align: center;
38
+ -webkit-touch-callout: none;
39
+ -webkit-user-select: none;
40
+ -khtml-user-select: none;
41
+ -moz-user-select: none;
42
+ -ms-user-select: none;
43
+ user-select: none;
44
+ cursor: default;
45
+ }
46
+ .assoc_whisperer .list span:hover {
47
+ background-color: #fffbb5;
48
+ }
@@ -0,0 +1,79 @@
1
+ module ActionView
2
+ module Helpers
3
+
4
+ module AssocWhispererHelper
5
+
6
+ # Createa an association whisperer tag, that will attach javascript object to self
7
+ # allowing user to select a text but stores something differend (mostly respective id).
8
+ #
9
+ # ==== Examples
10
+ # assoc_whisperer :task, :worker, :workers, size: 25, url: '/whisp'
11
+ # # => <span class="assoc_whisperer" data-action="workers" data-url="/whisp">
12
+ # <input class="value_field" id="task_worker_id" name="task[worker_id]" type="hidden" value="">
13
+ # <input autocomplete="off" class="text_field unfilled" id="task_worker_txt" name="task[worker_txt]" size="25" type="text" value="">
14
+ # <span class="dropdown_button">▾</span></span>
15
+ #
16
+ # Let's say your association looks like this:
17
+ #
18
+ # class Task
19
+ # belongs_to :worker, class_name: 'Worker', foreign_key: :manager
20
+ # end
21
+ # class Worker
22
+ # def full_name; end
23
+ # def manager_id; end
24
+ # end
25
+ #
26
+ # assoc_whisperer :task, :worker, :workers, name: :manager, value_method: :manager_id, text_method: :full_name
27
+ # # => <span class="assoc_whisperer" data-action="workers" data-url="">
28
+ # <input class="value_field" id="task_manager" name="task[manager]" type="hidden" value="">
29
+ # <input autocomplete="off" class="text_field unfilled" id="task_manager_txt" name="task[manager_txt]" size="12" type="text" value="">
30
+ # <span class="dropdown_button">▾</span></span>
31
+ #
32
+ # And the 'value_field' would be filled by +manager_id+ of +worker+ associated to +task+, 'text_field' by his +full_name+
33
+ #
34
+ def assoc_whisperer(object_name, method, data_action, options={})
35
+ wrapper_assoc_whisperer_assets
36
+
37
+ options[:url] = AssocWhisperer.def_url unless options.has_key? :url
38
+ Tags::AssocWhispererField.new(object_name, method, self, data_action, options).render
39
+ end
40
+
41
+ def assoc_whisperer_tag(name, data_action, options = {})
42
+ wrapper_assoc_whisperer_assets
43
+
44
+ sanitized_id = name.to_s.delete(']').gsub(/[^-a-zA-Z0-9:.]/, "_")
45
+ text_tag_name = name.to_s
46
+ text_tag_name.insert (text_tag_name[-1]==']' ? -2 : -1), '_txt'
47
+
48
+ content = %Q(<input class="value_field" id="#{sanitized_id}" name="#{name}" type="hidden")
49
+ content << %Q( value="#{options[:value]}">)
50
+ content << %Q(<input autocomplete="off" class="text_field#{' unfilled' if options[:value].blank?}")
51
+ content << %Q( id="#{sanitized_id}_txt" name="#{text_tag_name}" size="#{options[:size]||12}")
52
+ content << %Q( type="text" value="#{options[:text]}">)
53
+ content << %Q(<span class="dropdown_button">\u25BE</span>)
54
+ content_tag :span, content.html_safe, 'data-url' => (options[:url]||AssocWhisperer.def_url),
55
+ 'data-action' => data_action, 'class' => 'assoc_whisperer'
56
+ end
57
+
58
+ private
59
+
60
+ def wrapper_assoc_whisperer_assets
61
+ unless @assoc_whisp_attached
62
+ attach_method = AssocWhisperer.attach_assets_method
63
+ self.send attach_method if attach_method && self.respond_to?(attach_method)
64
+ @assoc_whisp_attached = true
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ class FormBuilder
71
+ def assoc_whisperer(method, data_action, options = {})
72
+ @template.assoc_whisperer @object_name, method, data_action, objectify_options(options)
73
+ end
74
+ end
75
+
76
+ autoload :AssocWhispererHelper
77
+ include AssocWhispererHelper
78
+ end
79
+ end
@@ -0,0 +1,88 @@
1
+ module ActionView
2
+ module Helpers
3
+ module Tags # :nodoc:
4
+ class AssocWhispererField < Base # :nodoc:
5
+
6
+ def initialize(object_name, method_name, template_object, action, options={})
7
+ @action = action
8
+ options[:value_method] = :id unless options.has_key? :value_method
9
+ options[:text_method] = :to_s unless options.has_key? :text_method
10
+ super object_name, method_name, template_object, options
11
+ end
12
+
13
+ def render
14
+ content = %Q(<input class="value_field" id="#{value_tag_id}" name="#{value_tag_name}" type="hidden")
15
+ content << %Q( value="#{value_tag_value}">)
16
+ content << %Q(<input autocomplete="off" class="text_field#{' unfilled' unless whispered_object}")
17
+ content << %Q( id="#{text_tag_id}" name="#{text_tag_name}" size="#{@options[:size]||12}")
18
+ content << %Q( type="text" value="#{text_tag_value}">)
19
+ content << %Q(<span class="dropdown_button">\u25BE</span>)
20
+ content_tag :span, content.html_safe, 'data-url' => @options[:url],
21
+ 'data-action' => @action, 'class' => 'assoc_whisperer'
22
+ end
23
+
24
+ private
25
+
26
+ def whispered_object
27
+ @whispered_object ||= value @object
28
+ end
29
+
30
+ def whispered_object_attribute(method)
31
+ wo = whispered_object
32
+ wo.send method if wo && wo.respond_to?(method)
33
+ end
34
+
35
+ def text_tag_name
36
+ "#{@object_name}[#{text_sanitized_method_name}]"
37
+ end
38
+
39
+ def text_tag_id
40
+ "#{sanitized_object_name}_#{text_sanitized_method_name}"
41
+ end
42
+
43
+ def text_tag_value
44
+ text = @options[:text_method]
45
+ text = if text.respond_to? :call
46
+ text.call whispered_object
47
+ else
48
+ whispered_object_attribute(text)
49
+ end
50
+ ERB::Util.html_escape text
51
+ end
52
+
53
+ def text_sanitized_method_name
54
+ return @text_sanitized_method_name if defined? @text_sanitized_method_name
55
+ wsmn = if @options[:name]
56
+ "#{@options[:name]}_txt"
57
+ else
58
+ "#{@method_name}_txt"
59
+ end
60
+ @text_sanitized_method_name = wsmn.sub(/\?$/,"")
61
+ end
62
+
63
+ def value_tag_name
64
+ "#{@object_name}[#{value_sanitized_method_name}]"
65
+ end
66
+
67
+ def value_tag_id
68
+ "#{sanitized_object_name}_#{value_sanitized_method_name}"
69
+ end
70
+
71
+ def value_tag_value
72
+ whispered_object_attribute @options[:value_method]
73
+ end
74
+
75
+ def value_sanitized_method_name
76
+ return @value_sanitized_method_name if defined? @value_sanitized_method_name
77
+ wsmn = if @options[:name]
78
+ @options[:name].to_s
79
+ else
80
+ "#{@method_name}_#{@options[:value_method]}"
81
+ end
82
+ @value_sanitized_method_name = wsmn.sub(/\?$/,"")
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,9 @@
1
+ <%
2
+ text_method = local_assigns[:text] || :to_s
3
+ val_method = local_assigns[:val] || :id
4
+ %>
5
+ <div tabindex=0 class="list">
6
+ <% @objects.each do |o| %>
7
+ <span data-value="<%= o.send val_method %>"><%= o.send text_method %></span>
8
+ <% end %>
9
+ </div>
@@ -0,0 +1,5 @@
1
+ <div tabindex=0 class="list">
2
+ <% @rows.each do |r| %>
3
+ <span data-value="<%= r.last %>"><%= r.first %></span>
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'assoc_whisperer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "assoc_whisperer"
8
+ spec.version = AssocWhisperer::VERSION
9
+ spec.authors = ["Ondřej Želazko"]
10
+ spec.email = ["zelazk.o@email.cz"]
11
+ spec.summary = %q{Rails tag assoc_whisperer for forms}
12
+ spec.description = %q{You can associate two models together, while user inputs e.g. name and server recieves id}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ module AssocWhisperer
3
+ VERSION = '1.0.0'
4
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require "assoc_whisperer/version"
3
+
4
+ module AssocWhisperer
5
+
6
+ class << self
7
+ attr_accessor :def_url, :attach_assets_method
8
+ end
9
+
10
+ module Rails
11
+ class Engine < ::Rails::Engine
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ AssocWhisperer.def_url = '/whisp'
18
+ AssocWhisperer.attach_assets_method = :attach_assoc_whisperer_assets
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: assoc_whisperer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ondřej Želazko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: You can associate two models together, while user inputs e.g. name and
42
+ server recieves id
43
+ email:
44
+ - zelazk.o@email.cz
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - app/assets/javascript/assoc_whisp.min.js
55
+ - app/assets/javascript/assoc_whispering.coffee
56
+ - app/assets/stylesheets/assoc_whisp_example.css
57
+ - app/helpers/action_view/helpers/assoc_whisperer_helper.rb
58
+ - app/helpers/action_view/helpers/tags/assoc_whisperer_field.rb
59
+ - app/views/assoc_whisperer/list.html.erb
60
+ - app/views/assoc_whisperer/list_array.html.erb
61
+ - assoc_whisperer.gemspec
62
+ - lib/assoc_whisperer.rb
63
+ - lib/assoc_whisperer/version.rb
64
+ homepage: ''
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Rails tag assoc_whisperer for forms
88
+ test_files: []