yeshua_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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +11 -0
  3. data/lib/yeshoua_crm.rb +87 -0
  4. data/lib/yeshua_crm/acts_as_draftable/draft.rb +40 -0
  5. data/lib/yeshua_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +154 -0
  6. data/lib/yeshua_crm/acts_as_list/list.rb +282 -0
  7. data/lib/yeshua_crm/acts_as_taggable/rcrm_acts_as_taggable.rb +350 -0
  8. data/lib/yeshua_crm/acts_as_taggable/tag.rb +81 -0
  9. data/lib/yeshua_crm/acts_as_taggable/tag_list.rb +111 -0
  10. data/lib/yeshua_crm/acts_as_taggable/tagging.rb +16 -0
  11. data/lib/yeshua_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
  12. data/lib/yeshua_crm/acts_as_votable/rcrm_acts_as_votable.rb +80 -0
  13. data/lib/yeshua_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
  14. data/lib/yeshua_crm/acts_as_votable/votable.rb +323 -0
  15. data/lib/yeshua_crm/acts_as_votable/vote.rb +28 -0
  16. data/lib/yeshua_crm/acts_as_votable/voter.rb +131 -0
  17. data/lib/yeshua_crm/assets_manager.rb +43 -0
  18. data/lib/yeshua_crm/currency/formatting.rb +224 -0
  19. data/lib/yeshua_crm/currency/heuristics.rb +151 -0
  20. data/lib/yeshua_crm/currency/loader.rb +24 -0
  21. data/lib/yeshua_crm/currency.rb +439 -0
  22. data/lib/yeshua_crm/helpers/external_assets_helper.rb +17 -0
  23. data/lib/yeshua_crm/helpers/form_tag_helper.rb +123 -0
  24. data/lib/yeshua_crm/helpers/tags_helper.rb +13 -0
  25. data/lib/yeshua_crm/helpers/vote_helper.rb +35 -0
  26. data/lib/yeshua_crm/liquid/drops/cells_drop.rb +86 -0
  27. data/lib/yeshua_crm/liquid/drops/issues_drop.rb +66 -0
  28. data/lib/yeshua_crm/liquid/drops/news_drop.rb +54 -0
  29. data/lib/yeshua_crm/liquid/drops/users_drop.rb +72 -0
  30. data/lib/yeshua_crm/liquid/filters/arrays.rb +177 -0
  31. data/lib/yeshua_crm/liquid/filters/base.rb +208 -0
  32. data/lib/yeshua_crm/money_helper.rb +65 -0
  33. data/lib/yeshua_crm/version.rb +3 -0
  34. data/test/acts_as_draftable/draft_test.rb +29 -0
  35. data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +185 -0
  36. data/test/acts_as_taggable/rcrm_acts_as_taggable_test.rb +345 -0
  37. data/test/acts_as_taggable/tag_list_test.rb +34 -0
  38. data/test/acts_as_taggable/tag_test.rb +72 -0
  39. data/test/acts_as_taggable/tagging_test.rb +15 -0
  40. data/test/acts_as_viewed/rcrm_acts_as_viewed_test.rb +47 -0
  41. data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
  42. data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
  43. data/test/acts_as_votable/votable_test.rb +507 -0
  44. data/test/acts_as_votable/voter_test.rb +296 -0
  45. data/test/currency_test.rb +292 -0
  46. data/test/liquid/drops/issues_drop_test.rb +34 -0
  47. data/test/liquid/drops/news_drop_test.rb +38 -0
  48. data/test/liquid/drops/projects_drop_test.rb +44 -0
  49. data/test/liquid/drops/uses_drop_test.rb +36 -0
  50. data/test/liquid/filters/arrays_filter_test.rb +24 -0
  51. data/test/liquid/filters/base_filter_test.rb +63 -0
  52. data/test/liquid/liquid_helper.rb +32 -0
  53. data/test/models/issue.rb +14 -0
  54. data/test/models/news.rb +3 -0
  55. data/test/models/project.rb +8 -0
  56. data/test/models/user.rb +11 -0
  57. data/test/models/vote_classes.rb +33 -0
  58. data/test/money_helper_test.rb +12 -0
  59. data/test/schema.rb +121 -0
  60. data/test/tags_helper_test.rb +29 -0
  61. data/test/test_helper.rb +66 -0
  62. data/test/vote_helper_test.rb +28 -0
  63. data/yeshua_crm.gemspec +28 -0
  64. metadata +206 -0
@@ -0,0 +1,28 @@
1
+ require 'yeshua_crm/helpers/vote_helper'
2
+
3
+ module YeshuaCrm
4
+ module ActsAsVotable
5
+ class Vote < ActiveRecord::Base
6
+ include Helpers::Words
7
+
8
+ if defined?(ProtectedAttributes) || ::ActiveRecord::VERSION::MAJOR < 4
9
+ attr_accessible :votable_id, :votable_type,
10
+ :voter_id, :voter_type,
11
+ :votable, :voter,
12
+ :vote_flag, :vote_scope,
13
+ :vote_ip
14
+ end
15
+
16
+ belongs_to :votable, :polymorphic => true
17
+ belongs_to :voter, :polymorphic => true
18
+
19
+ scope :up, lambda { where(:vote_flag => true) }
20
+ scope :down, lambda { where(:vote_flag => false) }
21
+ scope :for_type, lambda { |klass| where(:votable_type => klass.to_s) }
22
+ scope :by_type, lambda { |klass| where(:voter_type => klass.to_s) }
23
+
24
+ validates_presence_of :votable_id
25
+ validates_presence_of :voter_id
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,131 @@
1
+ module YeshuaCrm
2
+ module ActsAsVotable
3
+ module Voter
4
+ def self.included(base)
5
+ # allow user to define these
6
+ aliases = {
7
+ :vote_up_for => [:likes, :upvotes, :up_votes],
8
+ :vote_down_for => [:dislikes, :downvotes, :down_votes],
9
+ :unvote_for => [:unlike, :undislike],
10
+ :voted_on? => [:voted_for?],
11
+ :voted_up_on? => [:voted_up_for?, :liked?],
12
+ :voted_down_on? => [:voted_down_for?, :disliked?],
13
+ :voted_as_when_voting_on => [:voted_as_when_voted_on, :voted_as_when_voting_for, :voted_as_when_voted_for],
14
+ :find_up_voted_items => [:find_liked_items],
15
+ :find_down_voted_items => [:find_disliked_items]
16
+ }
17
+
18
+ base.class_eval do
19
+ has_many :votes, :class_name => 'YeshuaCrm::ActsAsVotable::Vote', :as => :voter, :dependent => :destroy do
20
+ def votables
21
+ includes(:votable).map(&:votable)
22
+ end
23
+ end
24
+
25
+ aliases.each do |method, links|
26
+ links.each do |new_method|
27
+ alias_method(new_method, method)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ # voting
34
+ def vote(args)
35
+ args[:votable].vote_by args.merge(:voter => self)
36
+ end
37
+
38
+ def vote_up_for(model = nil, args = {})
39
+ vote :votable => model, :vote_scope => args[:vote_scope], :vote => true
40
+ end
41
+
42
+ def vote_down_for(model = nil, args = {})
43
+ vote :votable => model, :vote_scope => args[:vote_scope], :vote => false
44
+ end
45
+
46
+ def unvote_for(model, args = {})
47
+ model.unvote :voter => self, :vote_scope => args[:vote_scope]
48
+ end
49
+
50
+ # results
51
+ def voted_on?(votable, args = {})
52
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
53
+ :vote_scope => args[:vote_scope])
54
+ !votes.empty?
55
+ end
56
+
57
+ def voted_up_on?(votable, args = {})
58
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
59
+ :vote_scope => args[:vote_scope], :vote_flag => true)
60
+ !votes.empty?
61
+ end
62
+
63
+ def voted_down_on?(votable, args = {})
64
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
65
+ :vote_scope => args[:vote_scope], :vote_flag => false)
66
+ !votes.empty?
67
+ end
68
+
69
+ def voted_as_when_voting_on(votable, args = {})
70
+ vote = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
71
+ :vote_scope => args[:vote_scope]).select(:vote_flag).last
72
+ return nil unless vote
73
+ vote.vote_flag
74
+ end
75
+
76
+ def find_votes(extra_conditions = {})
77
+ votes.where(extra_conditions)
78
+ end
79
+
80
+ def find_up_votes(args = {})
81
+ find_votes :vote_flag => true, :vote_scope => args[:vote_scope]
82
+ end
83
+
84
+ def find_down_votes(args = {})
85
+ find_votes :vote_flag => false, :vote_scope => args[:vote_scope]
86
+ end
87
+
88
+ def find_votes_for_class(klass, extra_conditions = {})
89
+ find_votes extra_conditions.merge(:votable_type => klass.name)
90
+ end
91
+
92
+ def find_up_votes_for_class(klass, args = {})
93
+ find_votes_for_class klass, :vote_flag => true, :vote_scope => args[:vote_scope]
94
+ end
95
+
96
+ def find_down_votes_for_class(klass, args = {})
97
+ find_votes_for_class klass, :vote_flag => false, :vote_scope => args[:vote_scope]
98
+ end
99
+
100
+ # Including polymporphic relations for eager loading
101
+ def include_objects
102
+ YeshuaCrm::ActsAsVotable::Vote.includes(:votable)
103
+ end
104
+
105
+ def find_voted_items(extra_conditions = {})
106
+ options = extra_conditions.merge :voter_id => id, :voter_type => self.class.base_class.name
107
+ include_objects.where(options).collect(&:votable)
108
+ end
109
+
110
+ def find_up_voted_items(extra_conditions = {})
111
+ find_voted_items extra_conditions.merge(:vote_flag => true)
112
+ end
113
+
114
+ def find_down_voted_items(extra_conditions = {})
115
+ find_voted_items extra_conditions.merge(:vote_flag => false)
116
+ end
117
+
118
+ def get_voted(klass, extra_conditions = {})
119
+ klass.joins(:votes_for).merge find_votes(extra_conditions)
120
+ end
121
+
122
+ def get_up_voted(klass)
123
+ klass.joins(:votes_for).merge find_up_votes
124
+ end
125
+
126
+ def get_down_voted(klass)
127
+ klass.joins(:votes_for).merge find_down_votes
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,43 @@
1
+ module YeshuaCrm
2
+ class AssetsManager
3
+ def self.install_assets
4
+ return unless Gem.loaded_specs['yeshua_crm']
5
+ source = File.join(Gem.loaded_specs['yeshua_crm'].full_gem_path, 'vendor', 'assets')
6
+ destination = File.join(Dir.pwd, 'public', 'plugin_assets', 'yeshua_crm')
7
+ return unless File.directory?(source)
8
+
9
+ source_files = Dir[source + '/**/*']
10
+ source_dirs = source_files.select { |d| File.directory?(d) }
11
+ source_files -= source_dirs
12
+
13
+ unless source_files.empty?
14
+ base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, ''))
15
+ begin
16
+ FileUtils.mkdir_p(base_target_dir)
17
+ rescue Exception => e
18
+ raise "Could not create directory #{base_target_dir}: " + e.message
19
+ end
20
+ end
21
+
22
+ source_dirs.each do |dir|
23
+ target_dir = File.join(destination, dir.gsub(source, ''))
24
+ begin
25
+ FileUtils.mkdir_p(target_dir)
26
+ rescue Exception => e
27
+ raise "Could not create directory #{target_dir}: " + e.message
28
+ end
29
+ end
30
+
31
+ source_files.each do |file|
32
+ begin
33
+ target = File.join(destination, file.gsub(source, ''))
34
+ unless File.exist?(target) && FileUtils.identical?(file, target)
35
+ FileUtils.cp(file, target)
36
+ end
37
+ rescue Exception => e
38
+ raise "Could not copy #{file} to #{target}: " + e.message
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,224 @@
1
+ # encoding: UTF-8
2
+ module YeshuaCrm
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 YeshuaCrm
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 YeshuaCrm
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