yeshoua_crm 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 +7 -0
- data/Rakefile +11 -0
- data/lib/yeshoua_crm.rb +87 -0
- data/lib/yeshoua_crm/acts_as_draftable/draft.rb +40 -0
- data/lib/yeshoua_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +154 -0
- data/lib/yeshoua_crm/acts_as_list/list.rb +282 -0
- data/lib/yeshoua_crm/acts_as_taggable/rcrm_acts_as_taggable.rb +350 -0
- data/lib/yeshoua_crm/acts_as_taggable/tag.rb +81 -0
- data/lib/yeshoua_crm/acts_as_taggable/tag_list.rb +111 -0
- data/lib/yeshoua_crm/acts_as_taggable/tagging.rb +16 -0
- data/lib/yeshoua_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
- data/lib/yeshoua_crm/acts_as_votable/rcrm_acts_as_votable.rb +80 -0
- data/lib/yeshoua_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
- data/lib/yeshoua_crm/acts_as_votable/votable.rb +323 -0
- data/lib/yeshoua_crm/acts_as_votable/vote.rb +28 -0
- data/lib/yeshoua_crm/acts_as_votable/voter.rb +131 -0
- data/lib/yeshoua_crm/assets_manager.rb +43 -0
- data/lib/yeshoua_crm/currency.rb +439 -0
- data/lib/yeshoua_crm/currency/formatting.rb +224 -0
- data/lib/yeshoua_crm/currency/heuristics.rb +151 -0
- data/lib/yeshoua_crm/currency/loader.rb +24 -0
- data/lib/yeshoua_crm/helpers/external_assets_helper.rb +17 -0
- data/lib/yeshoua_crm/helpers/form_tag_helper.rb +123 -0
- data/lib/yeshoua_crm/helpers/tags_helper.rb +13 -0
- data/lib/yeshoua_crm/helpers/vote_helper.rb +35 -0
- data/lib/yeshoua_crm/liquid/drops/cells_drop.rb +86 -0
- data/lib/yeshoua_crm/liquid/drops/issues_drop.rb +66 -0
- data/lib/yeshoua_crm/liquid/drops/news_drop.rb +54 -0
- data/lib/yeshoua_crm/liquid/drops/users_drop.rb +72 -0
- data/lib/yeshoua_crm/liquid/filters/arrays.rb +177 -0
- data/lib/yeshoua_crm/liquid/filters/base.rb +208 -0
- data/lib/yeshoua_crm/money_helper.rb +65 -0
- data/lib/yeshoua_crm/version.rb +3 -0
- data/test/acts_as_draftable/draft_test.rb +29 -0
- data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +185 -0
- data/test/acts_as_taggable/rcrm_acts_as_taggable_test.rb +345 -0
- data/test/acts_as_taggable/tag_list_test.rb +34 -0
- data/test/acts_as_taggable/tag_test.rb +72 -0
- data/test/acts_as_taggable/tagging_test.rb +15 -0
- data/test/acts_as_viewed/rcrm_acts_as_viewed_test.rb +47 -0
- data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
- data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
- data/test/acts_as_votable/votable_test.rb +507 -0
- data/test/acts_as_votable/voter_test.rb +296 -0
- data/test/currency_test.rb +292 -0
- data/test/liquid/drops/issues_drop_test.rb +34 -0
- data/test/liquid/drops/news_drop_test.rb +38 -0
- data/test/liquid/drops/projects_drop_test.rb +44 -0
- data/test/liquid/drops/uses_drop_test.rb +36 -0
- data/test/liquid/filters/arrays_filter_test.rb +24 -0
- data/test/liquid/filters/base_filter_test.rb +63 -0
- data/test/liquid/liquid_helper.rb +32 -0
- data/test/models/issue.rb +14 -0
- data/test/models/news.rb +3 -0
- data/test/models/project.rb +8 -0
- data/test/models/user.rb +11 -0
- data/test/models/vote_classes.rb +33 -0
- data/test/money_helper_test.rb +12 -0
- data/test/schema.rb +121 -0
- data/test/tags_helper_test.rb +29 -0
- data/test/test_helper.rb +66 -0
- data/test/vote_helper_test.rb +28 -0
- data/yeshoua_crm.gemspec +28 -0
- metadata +206 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module YeshouaCrm
|
3
|
+
class Currency
|
4
|
+
module Formatting
|
5
|
+
def self.included(base)
|
6
|
+
[
|
7
|
+
[:thousands_separator, :delimiter, ","],
|
8
|
+
[:decimal_mark, :separator, "."]
|
9
|
+
].each do |method, name, character|
|
10
|
+
define_i18n_method(method, name, character)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.define_i18n_method(method, name, character)
|
15
|
+
define_method(method) do
|
16
|
+
if self.class.use_i18n
|
17
|
+
begin
|
18
|
+
I18n.t name, :scope => "number.currency.format", :raise => true
|
19
|
+
rescue I18n::MissingTranslationData
|
20
|
+
I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
currency.send(method) || character
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method name, method
|
27
|
+
end
|
28
|
+
|
29
|
+
def format(value, currency, *rules)
|
30
|
+
# support for old format parameters
|
31
|
+
rules = normalize_formatting_rules(rules)
|
32
|
+
if currency
|
33
|
+
rules = self.localize_formatting_rules(rules, currency)
|
34
|
+
rules = self.translate_formatting_rules(rules, currency.code) if rules[:translate]
|
35
|
+
rules[:decimal_mark] = currency.decimal_mark if rules[:decimal_mark].nil?
|
36
|
+
rules[:decimal_places] = currency.decimal_places
|
37
|
+
rules[:subunit_to_unit] = currency.subunit_to_unit
|
38
|
+
rules[:thousands_separator] = currency.thousands_separator if rules[:thousands_separator].nil?
|
39
|
+
end
|
40
|
+
rules = Currency.default_formatting_rules.merge(rules){|key, v1, v2| v2.nil? ? v1 : v2}
|
41
|
+
|
42
|
+
# if fractional == 0
|
43
|
+
if rules[:display_free].respond_to?(:to_str)
|
44
|
+
return rules[:display_free]
|
45
|
+
elsif rules[:display_free]
|
46
|
+
return "free"
|
47
|
+
end
|
48
|
+
# end
|
49
|
+
|
50
|
+
symbol_value = currency.try(:symbol) || ""
|
51
|
+
|
52
|
+
formatted = value.abs.to_s
|
53
|
+
|
54
|
+
# if rules[:rounded_infinite_precision]
|
55
|
+
if currency
|
56
|
+
formatted.gsub!(/#{rules[:decimal_mark]}/, '.') unless '.' == rules[:decimal_mark]
|
57
|
+
formatted = ((BigDecimal(formatted) * currency.subunit_to_unit).round / BigDecimal(currency.subunit_to_unit.to_s)).to_s("F")
|
58
|
+
formatted.gsub!(/\..*/) do |decimal_part|
|
59
|
+
decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1)
|
60
|
+
decimal_part
|
61
|
+
end
|
62
|
+
formatted.gsub!(/\./, rules[:decimal_mark]) unless '.' == rules[:decimal_mark]
|
63
|
+
end
|
64
|
+
|
65
|
+
sign = value < 0 ? '-' : ''
|
66
|
+
|
67
|
+
if rules[:no_cents] || (rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0)
|
68
|
+
formatted = "#{formatted.to_i}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# thousands_separator_value = currency.thousands_separator
|
72
|
+
# Determine thousands_separator
|
73
|
+
if rules.has_key?(:thousands_separator)
|
74
|
+
thousands_separator_value = rules[:thousands_separator] || ''
|
75
|
+
end
|
76
|
+
decimal_mark = rules[:decimal_mark]
|
77
|
+
# Apply thousands_separator
|
78
|
+
formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value),
|
79
|
+
"\\1#{thousands_separator_value}")
|
80
|
+
|
81
|
+
symbol_position = symbol_position_from(rules, currency) if currency
|
82
|
+
|
83
|
+
if rules[:sign_positive] == true && (value >= 0)
|
84
|
+
sign = '+'
|
85
|
+
end
|
86
|
+
|
87
|
+
if rules[:sign_before_symbol] == true
|
88
|
+
sign_before = sign
|
89
|
+
sign = ''
|
90
|
+
end
|
91
|
+
|
92
|
+
if symbol_value && !symbol_value.empty?
|
93
|
+
symbol_value = "<span class=\"currency_symbol\">#{symbol_value}</span>" if rules[:html_wrap_symbol]
|
94
|
+
formatted = if symbol_position == :before
|
95
|
+
symbol_space = rules[:symbol_before_without_space] === false ? " " : ""
|
96
|
+
"#{sign_before}#{symbol_value}#{symbol_space}#{sign}#{formatted}"
|
97
|
+
else
|
98
|
+
symbol_space = rules[:symbol_after_without_space] ? "" : " "
|
99
|
+
"#{sign_before}#{sign}#{formatted}#{symbol_space}#{symbol_value}"
|
100
|
+
end
|
101
|
+
else
|
102
|
+
formatted="#{sign_before}#{sign}#{formatted}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# apply_decimal_mark_from_rules(formatted, rules)
|
106
|
+
|
107
|
+
if rules[:with_currency]
|
108
|
+
formatted << " "
|
109
|
+
formatted << '<span class="currency">' if rules[:html]
|
110
|
+
formatted << currency.to_s
|
111
|
+
formatted << '</span>' if rules[:html]
|
112
|
+
end
|
113
|
+
formatted
|
114
|
+
end
|
115
|
+
|
116
|
+
def default_formatting_rules
|
117
|
+
{
|
118
|
+
:decimal_mark =>".",
|
119
|
+
:thousands_separator => ",",
|
120
|
+
:subunit_to_unit => 100
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def regexp_format(formatted, rules, decimal_mark, symbol_value)
|
125
|
+
regexp_decimal = Regexp.escape(decimal_mark)
|
126
|
+
if rules[:south_asian_number_formatting]
|
127
|
+
/(\d+?)(?=(\d\d)+(\d)(?:\.))/
|
128
|
+
else
|
129
|
+
# Symbols may contain decimal marks (E.g "դր.")
|
130
|
+
if formatted.sub(symbol_value.to_s, "") =~ /#{regexp_decimal}/
|
131
|
+
/(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
|
132
|
+
else
|
133
|
+
/(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def translate_formatting_rules(rules, iso_code)
|
139
|
+
begin
|
140
|
+
rules[:symbol] = I18n.t iso_code, :scope => "number.currency.symbol", :raise => true
|
141
|
+
rescue I18n::MissingTranslationData
|
142
|
+
# Do nothing
|
143
|
+
end
|
144
|
+
rules
|
145
|
+
end
|
146
|
+
|
147
|
+
def localize_formatting_rules(rules, currency)
|
148
|
+
if currency.iso_code == "JPY" && I18n.locale == :ja
|
149
|
+
rules[:symbol] = "円" unless rules[:symbol] == false
|
150
|
+
rules[:symbol_position] = :after
|
151
|
+
rules[:symbol_after_without_space] = true
|
152
|
+
elsif currency.iso_code == "CHF"
|
153
|
+
rules[:symbol_before_without_space] = false
|
154
|
+
end
|
155
|
+
rules
|
156
|
+
end
|
157
|
+
|
158
|
+
def symbol_value_from(rules)
|
159
|
+
if rules.has_key?(:symbol)
|
160
|
+
if rules[:symbol] === true
|
161
|
+
symbol
|
162
|
+
elsif rules[:symbol]
|
163
|
+
rules[:symbol]
|
164
|
+
else
|
165
|
+
""
|
166
|
+
end
|
167
|
+
elsif rules[:html]
|
168
|
+
currency.html_entity == '' ? currency.symbol : currency.html_entity
|
169
|
+
elsif rules[:disambiguate] and currency.disambiguate_symbol
|
170
|
+
currency.disambiguate_symbol
|
171
|
+
else
|
172
|
+
symbol
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def symbol_position_from(rules, currency)
|
177
|
+
if rules.has_key?(:symbol_position)
|
178
|
+
if [:before, :after].include?(rules[:symbol_position])
|
179
|
+
return rules[:symbol_position]
|
180
|
+
else
|
181
|
+
raise ArgumentError, ":symbol_position must be ':before' or ':after'"
|
182
|
+
end
|
183
|
+
elsif currency.symbol_first?
|
184
|
+
:before
|
185
|
+
else
|
186
|
+
:after
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
# Cleans up formatting rules.
|
193
|
+
#
|
194
|
+
# @param [Hash] rules
|
195
|
+
#
|
196
|
+
# @return [Hash]
|
197
|
+
def normalize_formatting_rules(rules)
|
198
|
+
if rules.size == 0
|
199
|
+
rules = {}
|
200
|
+
elsif rules.size == 1
|
201
|
+
rules = rules.pop
|
202
|
+
rules = { rules => true } if rules.is_a?(Symbol)
|
203
|
+
end
|
204
|
+
rules[:decimal_mark] = rules[:separator] || rules[:decimal_mark]
|
205
|
+
rules[:thousands_separator] = rules[:delimiter] || rules[:thousands_separator]
|
206
|
+
rules
|
207
|
+
end
|
208
|
+
|
209
|
+
# Applies decimal mark from rules to formatted
|
210
|
+
#
|
211
|
+
# @param [String] formatted
|
212
|
+
# @param [Hash] rules
|
213
|
+
def apply_decimal_mark_from_rules(formatted, rules)
|
214
|
+
if rules.has_key?(:decimal_mark) && rules[:decimal_mark]
|
215
|
+
# && rules[:decimal_mark] != decimal_mark
|
216
|
+
|
217
|
+
regexp_decimal = Regexp.escape(rules[:decimal_mark])
|
218
|
+
formatted.sub!(/(.*)(#{regexp_decimal})(.*)\Z/,
|
219
|
+
"\\1#{rules[:decimal_mark]}\\3")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module YeshouaCrm
|
2
|
+
class Currency
|
3
|
+
module Heuristics
|
4
|
+
|
5
|
+
# An robust and efficient algorithm for finding currencies in
|
6
|
+
# text. Using several algorithms it can find symbols, iso codes and
|
7
|
+
# even names of currencies.
|
8
|
+
# Although not recommendable, it can also attempt to find the given
|
9
|
+
# currency in an entire sentence
|
10
|
+
#
|
11
|
+
# Returns: Array (matched results)
|
12
|
+
def analyze(str)
|
13
|
+
return Analyzer.new(str, search_tree).process
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Build a search tree from the currency database
|
19
|
+
def search_tree
|
20
|
+
@_search_tree ||= {
|
21
|
+
:by_symbol => currencies_by_symbol,
|
22
|
+
:by_iso_code => currencies_by_iso_code,
|
23
|
+
:by_name => currencies_by_name
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def currencies_by_symbol
|
28
|
+
{}.tap do |r|
|
29
|
+
table.each do |dummy, c|
|
30
|
+
symbol = (c[:symbol]||"").downcase
|
31
|
+
symbol.chomp!('.')
|
32
|
+
(r[symbol] ||= []) << c
|
33
|
+
|
34
|
+
(c[:alternate_symbols]||[]).each do |ac|
|
35
|
+
ac = ac.downcase
|
36
|
+
ac.chomp!('.')
|
37
|
+
(r[ac] ||= []) << c
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def currencies_by_iso_code
|
44
|
+
{}.tap do |r|
|
45
|
+
table.each do |dummy,c|
|
46
|
+
(r[c[:iso_code].downcase] ||= []) << c
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def currencies_by_name
|
52
|
+
{}.tap do |r|
|
53
|
+
table.each do |dummy,c|
|
54
|
+
name_parts = c[:name].downcase.split
|
55
|
+
name_parts.each {|part| part.chomp!('.')}
|
56
|
+
|
57
|
+
# construct one branch per word
|
58
|
+
root = r
|
59
|
+
while name_part = name_parts.shift
|
60
|
+
root = (root[name_part] ||= {})
|
61
|
+
end
|
62
|
+
|
63
|
+
# the leaf is a currency
|
64
|
+
(root[:value] ||= []) << c
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Analyzer
|
70
|
+
attr_reader :search_tree, :words
|
71
|
+
attr_accessor :str, :currencies
|
72
|
+
|
73
|
+
def initialize str, search_tree
|
74
|
+
@str = (str||'').dup
|
75
|
+
@search_tree = search_tree
|
76
|
+
@currencies = []
|
77
|
+
end
|
78
|
+
|
79
|
+
def process
|
80
|
+
format
|
81
|
+
return [] if str.empty?
|
82
|
+
|
83
|
+
search_by_symbol
|
84
|
+
search_by_iso_code
|
85
|
+
search_by_name
|
86
|
+
|
87
|
+
prepare_reply
|
88
|
+
end
|
89
|
+
|
90
|
+
def format
|
91
|
+
str.gsub!(/[\r\n\t]/,'')
|
92
|
+
str.gsub!(/[0-9][\.,:0-9]*[0-9]/,'')
|
93
|
+
str.gsub!(/[0-9]/, '')
|
94
|
+
str.downcase!
|
95
|
+
@words = str.split
|
96
|
+
@words.each {|word| word.chomp!('.'); word.chomp!(',') }
|
97
|
+
end
|
98
|
+
|
99
|
+
def search_by_symbol
|
100
|
+
words.each do |word|
|
101
|
+
if found = search_tree[:by_symbol][word]
|
102
|
+
currencies.concat(found)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def search_by_iso_code
|
108
|
+
words.each do |word|
|
109
|
+
if found = search_tree[:by_iso_code][word]
|
110
|
+
currencies.concat(found)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def search_by_name
|
116
|
+
# remember, the search tree by name is a construct of branches and leaf!
|
117
|
+
# We need to try every combination of words within the sentence, so we
|
118
|
+
# end up with a x^2 equation, which should be fine as most names are either
|
119
|
+
# one or two words, and this is multiplied with the words of given sentence
|
120
|
+
|
121
|
+
search_words = words.dup
|
122
|
+
|
123
|
+
while search_words.length > 0
|
124
|
+
root = search_tree[:by_name]
|
125
|
+
|
126
|
+
search_words.each do |word|
|
127
|
+
if root = root[word]
|
128
|
+
if root[:value]
|
129
|
+
currencies.concat(root[:value])
|
130
|
+
end
|
131
|
+
else
|
132
|
+
break
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
search_words.delete_at(0)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def prepare_reply
|
141
|
+
codes = currencies.map do |currency|
|
142
|
+
currency[:iso_code]
|
143
|
+
end
|
144
|
+
codes.uniq!
|
145
|
+
codes.sort!
|
146
|
+
codes
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module YeshouaCrm
|
2
|
+
class Currency
|
3
|
+
module Loader
|
4
|
+
DATA_PATH = File.expand_path("../../../../config", __FILE__)
|
5
|
+
|
6
|
+
# Loads and returns the currencies stored in JSON files in the config directory.
|
7
|
+
#
|
8
|
+
# @return [Hash]
|
9
|
+
def load_currencies
|
10
|
+
currencies = parse_currency_file("currency_iso.json")
|
11
|
+
# currencies.merge! parse_currency_file("currency_non_iso.json")
|
12
|
+
# currencies.merge! parse_currency_file("currency_backwards_compatible.json")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parse_currency_file(filename)
|
18
|
+
json = File.read("#{DATA_PATH}/#{filename}")
|
19
|
+
json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
|
20
|
+
JSON.parse(json, :symbolize_names => true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module YeshouaCrm
|
2
|
+
module ExternalAssetsHelper
|
3
|
+
include ActionView::Helpers::JavaScriptHelper
|
4
|
+
|
5
|
+
def select2_assets
|
6
|
+
return if @select2_tag_included
|
7
|
+
@select2_tag_included = true
|
8
|
+
javascript_include_tag('select2', :plugin => 'yeshoua_crm') + stylesheet_link_tag('select2', :plugin => 'yeshoua_crm')
|
9
|
+
end
|
10
|
+
|
11
|
+
def chartjs_assets
|
12
|
+
return if @chartjs_tag_included
|
13
|
+
@chartjs_tag_included = true
|
14
|
+
javascript_include_tag('Chart.bundle.min', :plugin => 'yeshoua_crm')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module YeshouaCrm
|
2
|
+
module FormTagHelper
|
3
|
+
# Allows include select2 into your views.
|
4
|
+
#
|
5
|
+
# ==== Examples
|
6
|
+
# select2_tag 'city_id', '<option value="1">Lisbon</option>...'
|
7
|
+
# select2_tag 'city_id', options_for_select(...)
|
8
|
+
# select2_tag 'tag_list', nil, :multiple => true, :data => [{ id: 0, text: 'deal' }, ...], :tags => true, :include_hidden => false %>
|
9
|
+
# select2_tag 'tag_list', options_for_select(...), :multiple => true, :style => 'width: 100%;', :url => '/tags', :placeholder => '+ add tag', :tags => true %>
|
10
|
+
#
|
11
|
+
# You may use select_tag options and additional options.
|
12
|
+
#
|
13
|
+
# ==== Additional options
|
14
|
+
# * <tt>:url</tt> Allows searches for remote data using the ajax.
|
15
|
+
# * <tt>:data</tt> Load dropdown options from a local array if +url+ option not set.
|
16
|
+
# * <tt>:placeholder</tt> Supports displaying a placeholder value.
|
17
|
+
# * <tt>:include_hidden</tt> Adds hidden field after select when +multiple+ option true. Default value true.
|
18
|
+
#
|
19
|
+
# <b>Note:</b> The HTML specification says when +multiple+ parameter passed to select and all options got deselected
|
20
|
+
# web browsers do not send any value to server.
|
21
|
+
#
|
22
|
+
# In case if you don't want the helper to generate this hidden field you can specify
|
23
|
+
# <tt>include_hidden: false</tt> option.
|
24
|
+
#
|
25
|
+
# Also aliased as: select2
|
26
|
+
#
|
27
|
+
# select2 'city_id', options_for_select(...)
|
28
|
+
#
|
29
|
+
def select2_tag(name, option_tags = nil, options = {})
|
30
|
+
id = sanitize_to_id(name)
|
31
|
+
placeholder = options[:placeholder] || 'Select ...'
|
32
|
+
|
33
|
+
content_for(:header_tags) { select2_assets }
|
34
|
+
result = select_tag(name, option_tags, options)
|
35
|
+
if options[:multiple] && options.fetch(:include_hidden, true)
|
36
|
+
result << hidden_field_tag("#{name}[]", '')
|
37
|
+
end
|
38
|
+
|
39
|
+
result << javascript_tag(<<-JS)
|
40
|
+
$(function () {
|
41
|
+
$('select#' + '#{id}').select2({
|
42
|
+
#{select2_data_source_options(options)},
|
43
|
+
#{select2_tags_options(options)},
|
44
|
+
placeholder: '#{placeholder}'
|
45
|
+
});
|
46
|
+
});
|
47
|
+
JS
|
48
|
+
end
|
49
|
+
|
50
|
+
alias select2 select2_tag
|
51
|
+
|
52
|
+
# Transforms select filter field into select2
|
53
|
+
#
|
54
|
+
# ==== Examples
|
55
|
+
# transform_to_select2 'issue_tags', url: auto_complete_tags_url
|
56
|
+
# transform_to_select2 'manager_id', format_state: 'formatStateWithAvatar', min_input_length: 1, url: '/managers'
|
57
|
+
#
|
58
|
+
# ==== Options
|
59
|
+
# * <tt>:url</tt> Defines URL to search remote data using the ajax.
|
60
|
+
# * <tt>:format_state</tt> Defines template of search results in the drop-down.
|
61
|
+
# * <tt>:min_input_length</tt> Minimum number of characters required to start a search. Default value 0.
|
62
|
+
# * <tt>:width</tt> Sets the width of the control. Default value '60%'.
|
63
|
+
#
|
64
|
+
def transform_to_select2(field, options = {})
|
65
|
+
return if field.empty?
|
66
|
+
|
67
|
+
result = ''.html_safe
|
68
|
+
unless @transform_to_select2_included
|
69
|
+
result << javascript_include_tag('select2_helpers', plugin: 'yeshoua_crm')
|
70
|
+
@transform_to_select2_included = true
|
71
|
+
end
|
72
|
+
|
73
|
+
result << javascript_tag(<<-JS)
|
74
|
+
select2Filters['#{field}'] = {
|
75
|
+
url: '#{options[:url].to_s}',
|
76
|
+
formatState: #{options.fetch(:format_state, 'undefined')},
|
77
|
+
minimumInputLength: #{options.fetch(:min_input_length, 0)},
|
78
|
+
width: '#{options.fetch(:with, '60%')}'
|
79
|
+
};
|
80
|
+
JS
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def select2_data_source_options(options = {})
|
86
|
+
if options[:url].to_s.empty?
|
87
|
+
"data: #{options.fetch(:data, []).to_json}"
|
88
|
+
else
|
89
|
+
"ajax: {
|
90
|
+
url: '#{options[:url]}',
|
91
|
+
dataType: 'json',
|
92
|
+
delay: 250,
|
93
|
+
data: function (params) {
|
94
|
+
return { q: params.term };
|
95
|
+
},
|
96
|
+
processResults: function (data, params) {
|
97
|
+
return { results: data };
|
98
|
+
},
|
99
|
+
cache: true
|
100
|
+
}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def select2_tags_options(options = {})
|
105
|
+
if options.fetch(:tags, false)
|
106
|
+
"tags: true,
|
107
|
+
tokenSeparators: [','],
|
108
|
+
createTag: function (params) {
|
109
|
+
var term = $.trim(params.term);
|
110
|
+
if (term === '' || term.indexOf(',') > -1) {
|
111
|
+
return null; // Return null to disable tag creation
|
112
|
+
}
|
113
|
+
return {
|
114
|
+
id: term,
|
115
|
+
text: term
|
116
|
+
}
|
117
|
+
}"
|
118
|
+
else
|
119
|
+
'tags: false'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|