yeshoua_crm 1.0.0

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