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.
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/yeshoua_crm/acts_as_draftable/draft.rb +40 -0
  5. data/lib/yeshoua_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +154 -0
  6. data/lib/yeshoua_crm/acts_as_list/list.rb +282 -0
  7. data/lib/yeshoua_crm/acts_as_taggable/rcrm_acts_as_taggable.rb +350 -0
  8. data/lib/yeshoua_crm/acts_as_taggable/tag.rb +81 -0
  9. data/lib/yeshoua_crm/acts_as_taggable/tag_list.rb +111 -0
  10. data/lib/yeshoua_crm/acts_as_taggable/tagging.rb +16 -0
  11. data/lib/yeshoua_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
  12. data/lib/yeshoua_crm/acts_as_votable/rcrm_acts_as_votable.rb +80 -0
  13. data/lib/yeshoua_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
  14. data/lib/yeshoua_crm/acts_as_votable/votable.rb +323 -0
  15. data/lib/yeshoua_crm/acts_as_votable/vote.rb +28 -0
  16. data/lib/yeshoua_crm/acts_as_votable/voter.rb +131 -0
  17. data/lib/yeshoua_crm/assets_manager.rb +43 -0
  18. data/lib/yeshoua_crm/currency.rb +439 -0
  19. data/lib/yeshoua_crm/currency/formatting.rb +224 -0
  20. data/lib/yeshoua_crm/currency/heuristics.rb +151 -0
  21. data/lib/yeshoua_crm/currency/loader.rb +24 -0
  22. data/lib/yeshoua_crm/helpers/external_assets_helper.rb +17 -0
  23. data/lib/yeshoua_crm/helpers/form_tag_helper.rb +123 -0
  24. data/lib/yeshoua_crm/helpers/tags_helper.rb +13 -0
  25. data/lib/yeshoua_crm/helpers/vote_helper.rb +35 -0
  26. data/lib/yeshoua_crm/liquid/drops/cells_drop.rb +86 -0
  27. data/lib/yeshoua_crm/liquid/drops/issues_drop.rb +66 -0
  28. data/lib/yeshoua_crm/liquid/drops/news_drop.rb +54 -0
  29. data/lib/yeshoua_crm/liquid/drops/users_drop.rb +72 -0
  30. data/lib/yeshoua_crm/liquid/filters/arrays.rb +177 -0
  31. data/lib/yeshoua_crm/liquid/filters/base.rb +208 -0
  32. data/lib/yeshoua_crm/money_helper.rb +65 -0
  33. data/lib/yeshoua_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/yeshoua_crm.gemspec +28 -0
  64. metadata +206 -0
@@ -0,0 +1,28 @@
1
+ require 'yeshoua_crm/helpers/vote_helper'
2
+
3
+ module YeshouaCrm
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 YeshouaCrm
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 => 'YeshouaCrm::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
+ YeshouaCrm::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 YeshouaCrm
2
+ class AssetsManager
3
+ def self.install_assets
4
+ return unless Gem.loaded_specs['yeshoua_crm']
5
+ source = File.join(Gem.loaded_specs['yeshoua_crm'].full_gem_path, 'vendor', 'assets')
6
+ destination = File.join(Dir.pwd, 'public', 'plugin_assets', 'yeshoua_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,439 @@
1
+ require "json"
2
+ require "yeshoua_crm/currency/loader"
3
+ require "yeshoua_crm/currency/heuristics"
4
+ require "yeshoua_crm/currency/formatting"
5
+
6
+ module YeshouaCrm
7
+ # Represents a specific currency unit.
8
+ #
9
+ # @see http://en.wikipedia.org/wiki/Currency
10
+ # @see http://iso4217.net/
11
+ class Currency
12
+ include Comparable
13
+ extend Enumerable
14
+ extend YeshouaCrm::Currency::Loader
15
+ extend YeshouaCrm::Currency::Heuristics
16
+ extend YeshouaCrm::Currency::Formatting
17
+
18
+
19
+ # Thrown when a Currency has been registered without all the attributes
20
+ # which are required for the current action.
21
+ class MissingAttributeError < StandardError
22
+ def initialize(method, currency, attribute)
23
+ super(
24
+ "Can't call Currency.#{method} - currency '#{currency}' is missing "\
25
+ "the attribute '#{attribute}'"
26
+ )
27
+ end
28
+ end
29
+
30
+ # Thrown when an unknown currency is requested.
31
+ class UnknownCurrency < ArgumentError;
32
+ end
33
+
34
+ class << self
35
+
36
+ # Lookup a currency with given +id+ an returns a +Currency+ instance on
37
+ # success, +nil+ otherwise.
38
+ #
39
+ # @param [String, Symbol, #to_s] id Used to look into +table+ and
40
+ # retrieve the applicable attributes.
41
+ #
42
+ # @return [Money::Currency]
43
+ #
44
+ # @example
45
+ # Money::Currency.find(:eur) #=> #<Money::Currency id: eur ...>
46
+ # Money::Currency.find(:foo) #=> nil
47
+ def find(id)
48
+ return nil if id == ""
49
+ id = id.to_s.downcase.to_sym
50
+ new(id)
51
+ rescue UnknownCurrency
52
+ nil
53
+ end
54
+
55
+ # Lookup a currency with given +num+ as an ISO 4217 numeric and returns an
56
+ # +Currency+ instance on success, +nil+ otherwise.
57
+ #
58
+ # @param [#to_s] num used to look into +table+ in +iso_numeric+ and find
59
+ # the right currency id.
60
+ #
61
+ # @return [Money::Currency]
62
+ #
63
+ # @example
64
+ # Money::Currency.find_by_iso_numeric(978) #=> #<Money::Currency id: eur ...>
65
+ # Money::Currency.find_by_iso_numeric('001') #=> nil
66
+ def find_by_iso_numeric(num)
67
+ num = num.to_s
68
+ id, _ = self.table.find { |key, currency| currency[:iso_numeric] == num }
69
+ new(id)
70
+ rescue UnknownCurrency
71
+ nil
72
+ end
73
+
74
+ # Wraps the object in a +Currency+ unless it's already a +Currency+
75
+ # object.
76
+ #
77
+ # @param [Object] object The object to attempt and wrap as a +Currency+
78
+ # object.
79
+ #
80
+ # @return [Money::Currency]
81
+ #
82
+ # @example
83
+ # c1 = Money::Currency.new(:usd)
84
+ # Money::Currency.wrap(nil) #=> nil
85
+ # Money::Currency.wrap(c1) #=> #<Money::Currency id: usd ...>
86
+ # Money::Currency.wrap("usd") #=> #<Money::Currency id: usd ...>
87
+ def wrap(object)
88
+ if object.nil?
89
+ nil
90
+ elsif object.is_a?(Currency)
91
+ object
92
+ else
93
+ Currency.new(object)
94
+ end
95
+ end
96
+
97
+ # List of known currencies.
98
+ #
99
+ # == monetary unit
100
+ # The standard unit of value of a currency, as the dollar in the United States or the peso in Mexico.
101
+ # http://www.answers.com/topic/monetary-unit
102
+ # == fractional monetary unit, subunit
103
+ # A monetary unit that is valued at a fraction (usually one hundredth) of the basic monetary unit
104
+ # http://www.answers.com/topic/fractional-monetary-unit-subunit
105
+ #
106
+ # See http://en.wikipedia.org/wiki/List_of_circulating_currencies and
107
+ # http://search.cpan.org/~tnguyen/Locale-Currency-Format-1.28/Format.pm
108
+ def table
109
+ @table ||= load_currencies
110
+ end
111
+
112
+ # List the currencies imported and registered
113
+ # @return [Array]
114
+ #
115
+ # @example
116
+ # Money::Currency.iso_codes()
117
+ # [#<Currency ..USD>, 'CAD', 'EUR']...
118
+ def all
119
+ table.keys.map do |curr|
120
+ c = Currency.new(curr)
121
+ if c.priority.nil?
122
+ raise MissingAttributeError.new(:all, c.id, :priority)
123
+ end
124
+ c
125
+ end.sort_by(&:priority)
126
+ end
127
+
128
+ # We need a string-based validator before creating an unbounded number of
129
+ # symbols.
130
+ # http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol#11
131
+ # https://github.com/RubyMoney/money/issues/132
132
+ #
133
+ # @return [Set]
134
+ def stringified_keys
135
+ @stringified_keys ||= stringify_keys
136
+ end
137
+
138
+ # Register a new currency
139
+ #
140
+ # @param curr [Hash] information about the currency
141
+ # @option priority [Numeric] a numerical value you can use to sort/group
142
+ # the currency list
143
+ # @option iso_code [String] the international 3-letter code as defined
144
+ # by the ISO 4217 standard
145
+ # @option iso_numeric [Integer] the international 3-digit code as
146
+ # defined by the ISO 4217 standard
147
+ # @option name [String] the currency name
148
+ # @option symbol [String] the currency symbol (UTF-8 encoded)
149
+ # @option subunit [String] the name of the fractional monetary unit
150
+ # @option subunit_to_unit [Numeric] the proportion between the unit and
151
+ # the subunit
152
+ # @option separator [String] character between the whole and fraction
153
+ # amounts
154
+ # @option delimiter [String] character between each thousands place
155
+ def register(curr)
156
+ key = curr.fetch(:iso_code).downcase.to_sym
157
+ table if !@table
158
+ @table[key] = curr
159
+ @stringified_keys = stringify_keys
160
+ end
161
+
162
+
163
+ # Unregister a currency.
164
+ #
165
+ # @param [Object] curr A Hash with the key `:iso_code`, or the ISO code
166
+ # as a String or Symbol.
167
+ #
168
+ # @return [Boolean] true if the currency previously existed, false
169
+ # if it didn't.
170
+ def unregister(curr)
171
+ if curr.is_a?(Hash)
172
+ key = curr.fetch(:iso_code).to_s.downcase.to_sym
173
+ else
174
+ key = curr.to_s.downcase.to_sym
175
+ end
176
+ existed = @table.delete(key)
177
+ @stringified_keys = stringify_keys
178
+ existed ? true : false
179
+ end
180
+
181
+
182
+ def each
183
+ all.each { |c| yield(c) }
184
+ end
185
+
186
+ private
187
+
188
+ def stringify_keys
189
+ table.keys.each_with_object(Set.new) { |k, set| set.add(k.to_s.downcase) }
190
+ end
191
+ end
192
+
193
+ # @!attribute [r] id
194
+ # @return [Symbol] The symbol used to identify the currency, usually THE
195
+ # lowercase +iso_code+ attribute.
196
+ # @!attribute [r] priority
197
+ # @return [Integer] A numerical value you can use to sort/group the
198
+ # currency list.
199
+ # @!attribute [r] iso_code
200
+ # @return [String] The international 3-letter code as defined by the ISO
201
+ # 4217 standard.
202
+ # @!attribute [r] iso_numeric
203
+ # @return [String] The international 3-numeric code as defined by the ISO
204
+ # 4217 standard.
205
+ # @!attribute [r] name
206
+ # @return [String] The currency name.
207
+ # @!attribute [r] symbol
208
+ # @return [String] The currency symbol (UTF-8 encoded).
209
+ # @!attribute [r] disambiguate_symbol
210
+ # @return [String] Alternative currency used if symbol is ambiguous
211
+ # @!attribute [r] html_entity
212
+ # @return [String] The html entity for the currency symbol
213
+ # @!attribute [r] subunit
214
+ # @return [String] The name of the fractional monetary unit.
215
+ # @!attribute [r] subunit_to_unit
216
+ # @return [Integer] The proportion between the unit and the subunit
217
+ # @!attribute [r] decimal_mark
218
+ # @return [String] The decimal mark, or character used to separate the
219
+ # whole unit from the subunit.
220
+ # @!attribute [r] The
221
+ # @return [String] character used to separate thousands grouping of the
222
+ # whole unit.
223
+ # @!attribute [r] symbol_first
224
+ # @return [Boolean] Should the currency symbol precede the amount, or
225
+ # should it come after?
226
+ # @!attribute [r] smallest_denomination
227
+ # @return [Integer] Smallest amount of cash possible (in the subunit of
228
+ # this currency)
229
+
230
+ attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
231
+ :disambiguate_symbol, :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
232
+ :thousands_separator, :symbol_first, :smallest_denomination
233
+
234
+ alias_method :separator, :decimal_mark
235
+ alias_method :delimiter, :thousands_separator
236
+ alias_method :eql?, :==
237
+
238
+ # Create a new +Currency+ object.
239
+ #
240
+ # @param [String, Symbol, #to_s] id Used to look into +table+ and retrieve
241
+ # the applicable attributes.
242
+ #
243
+ # @return [Money::Currency]
244
+ #
245
+ # @example
246
+ # Money::Currency.new(:usd) #=> #<Money::Currency id: usd ...>
247
+ def initialize(id)
248
+ id = id.to_s.downcase
249
+ unless self.class.stringified_keys.include?(id)
250
+ raise UnknownCurrency, "Unknown currency '#{id}'"
251
+ end
252
+ @id = id.to_sym
253
+ initialize_data!
254
+ end
255
+
256
+ # Compares +self+ with +other_currency+ against the value of +priority+
257
+ # attribute.
258
+ #
259
+ # @param [Money::Currency] other_currency The currency to compare to.
260
+ #
261
+ # @return [-1,0,1] -1 if less than, 0 is equal to, 1 if greater than
262
+ #
263
+ # @example
264
+ # c1 = Money::Currency.new(:usd)
265
+ # c2 = Money::Currency.new(:jpy)
266
+ # c1 <=> c2 #=> 1
267
+ # c2 <=> c1 #=> -1
268
+ # c1 <=> c1 #=> 0
269
+ def <=>(other_currency)
270
+ # <=> returns nil when one of the values is nil
271
+ comparison = (self.priority <=> other_currency.priority || 0) rescue 0
272
+
273
+ if comparison == 0
274
+ self.id.to_s <=> other_currency.id.to_s
275
+ else
276
+ comparison
277
+ end
278
+ end
279
+
280
+ # Compares +self+ with +other_currency+ and returns +true+ if the are the
281
+ # same or if their +id+ attributes match.
282
+ #
283
+ # @param [Money::Currency] other_currency The currency to compare to.
284
+ #
285
+ # @return [Boolean]
286
+ #
287
+ # @example
288
+ # c1 = Money::Currency.new(:usd)
289
+ # c2 = Money::Currency.new(:jpy)
290
+ # c1 == c1 #=> true
291
+ # c1 == c2 #=> false
292
+ def ==(other_currency)
293
+ self.equal?(other_currency) || compare_ids(other_currency)
294
+ end
295
+
296
+ def compare_ids(other_currency)
297
+ other_currency_id = if other_currency.is_a?(Currency)
298
+ other_currency.id.to_s.downcase
299
+ else
300
+ other_currency.to_s.downcase
301
+ end
302
+ self.id.to_s.downcase == other_currency_id
303
+ end
304
+
305
+ private :compare_ids
306
+
307
+ # Returns a Fixnum hash value based on the +id+ attribute in order to use
308
+ # functions like & (intersection), group_by, etc.
309
+ #
310
+ # @return [Fixnum]
311
+ #
312
+ # @example
313
+ # Money::Currency.new(:usd).hash #=> 428936
314
+ def hash
315
+ id.hash
316
+ end
317
+
318
+ # Returns a human readable representation.
319
+ #
320
+ # @return [String]
321
+ #
322
+ # @example
323
+ # Money::Currency.new(:usd) #=> #<Currency id: usd ...>
324
+ def inspect
325
+ "#<#{self.class.name} id: #{id}, priority: #{priority}, symbol_first: #{symbol_first}, thousands_separator: #{thousands_separator}, html_entity: #{html_entity}, decimal_mark: #{decimal_mark}, name: #{name}, symbol: #{symbol}, subunit_to_unit: #{subunit_to_unit}, exponent: #{exponent}, iso_code: #{iso_code}, iso_numeric: #{iso_numeric}, subunit: #{subunit}, smallest_denomination: #{smallest_denomination}>"
326
+ end
327
+
328
+ # Returns a string representation corresponding to the upcase +id+
329
+ # attribute.
330
+ #
331
+ # --
332
+ # DEV: id.to_s.upcase corresponds to iso_code but don't use ISO_CODE for consistency.
333
+ #
334
+ # @return [String]
335
+ #
336
+ # @example
337
+ # Money::Currency.new(:usd).to_s #=> "USD"
338
+ # Money::Currency.new(:eur).to_s #=> "EUR"
339
+ def to_s
340
+ id.to_s.upcase
341
+ end
342
+
343
+ # Returns a string representation corresponding to the upcase +id+
344
+ # attribute. Useful in cases where only implicit conversions are made.
345
+ #
346
+ # @return [String]
347
+ #
348
+ # @example
349
+ # Money::Currency.new(:usd).to_str #=> "USD"
350
+ # Money::Currency.new(:eur).to_str #=> "EUR"
351
+ def to_str
352
+ id.to_s.upcase
353
+ end
354
+
355
+ # Returns a symbol representation corresponding to the upcase +id+
356
+ # attribute.
357
+ #
358
+ # @return [Symbol]
359
+ #
360
+ # @example
361
+ # Money::Currency.new(:usd).to_sym #=> :USD
362
+ # Money::Currency.new(:eur).to_sym #=> :EUR
363
+ def to_sym
364
+ id.to_s.upcase.to_sym
365
+ end
366
+
367
+ # Conversation to +self+.
368
+ #
369
+ # @return [self]
370
+ def to_currency
371
+ self
372
+ end
373
+
374
+ # Returns currency symbol or iso code for currencies with no symbol.
375
+ #
376
+ # @return [String]
377
+ def code
378
+ symbol || iso_code
379
+ end
380
+
381
+ def symbol_first?
382
+ !!@symbol_first
383
+ end
384
+
385
+ # Returns the number of digits after the decimal separator.
386
+ #
387
+ # @return [Float]
388
+ def exponent
389
+ Math.log10(@subunit_to_unit) if @subunit_to_unit
390
+ end
391
+
392
+ # Cache decimal places for subunit_to_unit values. Common ones pre-cached.
393
+ def self.decimal_places_cache
394
+ @decimal_places_cache ||= {1 => 0, 10 => 1, 100 => 2, 1000 => 3}
395
+ end
396
+
397
+ # The number of decimal places needed.
398
+ #
399
+ # @return [Integer]
400
+ def decimal_places
401
+ cache[subunit_to_unit] ||= calculate_decimal_places(subunit_to_unit)
402
+ end
403
+
404
+ private
405
+
406
+ def cache
407
+ self.class.decimal_places_cache
408
+ end
409
+
410
+ # If we need to figure out how many decimal places we need we
411
+ # use repeated integer division.
412
+ def calculate_decimal_places(num)
413
+ i = 1
414
+ while num >= 10
415
+ num /= 10
416
+ i += 1 if num >= 10
417
+ end
418
+ i
419
+ end
420
+
421
+ def initialize_data!
422
+ data = self.class.table[@id]
423
+ @alternate_symbols = data[:alternate_symbols]
424
+ @decimal_mark = data[:decimal_mark]
425
+ @disambiguate_symbol = data[:disambiguate_symbol]
426
+ @html_entity = data[:html_entity]
427
+ @iso_code = data[:iso_code]
428
+ @iso_numeric = data[:iso_numeric]
429
+ @name = data[:name]
430
+ @priority = data[:priority]
431
+ @smallest_denomination = data[:smallest_denomination]
432
+ @subunit = data[:subunit]
433
+ @subunit_to_unit = data[:subunit_to_unit]
434
+ @symbol = data[:symbol]
435
+ @symbol_first = data[:symbol_first]
436
+ @thousands_separator = data[:thousands_separator]
437
+ end
438
+ end
439
+ end