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,208 @@
1
+ require 'uri'
2
+ require 'rack'
3
+ require 'json'
4
+ require 'date'
5
+ require 'liquid'
6
+
7
+ module YeshouaCrm
8
+ module Liquid
9
+ module Filters
10
+ module Base
11
+ include YeshouaCrm::MoneyHelper
12
+
13
+ def textilize(input)
14
+ RedCloth3.new(input).to_html
15
+ end
16
+
17
+ def default(input, value)
18
+ input.blank? ? value : input
19
+ end
20
+
21
+ def underscore(input)
22
+ input.to_s.gsub(' ', '_').gsub('/', '_').underscore
23
+ end
24
+
25
+ def dasherize(input)
26
+ input.to_s.gsub(' ', '-').gsub('/', '-').dasherize
27
+ end
28
+
29
+ def shuffle(array)
30
+ array.to_a.shuffle
31
+ end
32
+
33
+ def random(input)
34
+ rand(input.to_i)
35
+ end
36
+
37
+ def md5(input)
38
+ Digest::MD5.hexdigest(input) unless input.blank?
39
+ end
40
+
41
+ # example:
42
+ # {{ "http:://www.example.com?key=hello world" | encode }}
43
+ #
44
+ # => http%3A%3A%2F%2Fwww.example.com%3Fkey%3Dhello+world
45
+ def encode(input)
46
+ ::Rack::Utils.escape(input)
47
+ end
48
+
49
+ # example:
50
+ # {{ today | plus_days: 2 }}
51
+ def plus_days(input, distanse)
52
+ return '' if input.nil?
53
+ days = distanse.to_i
54
+ input.to_date + days.days rescue 'Invalid date'
55
+ end
56
+
57
+ # example:
58
+ # {{ today | date_range: '2015-12-12' }}
59
+ def date_range(input, distanse)
60
+ return '' if input.nil?
61
+ (input.to_date - distanse.to_date).to_i rescue 'Invalid date'
62
+ end
63
+
64
+ # example:
65
+ # {{ now | utc }}
66
+ def utc(input)
67
+ return '' if input.nil?
68
+ input.to_time.utc rescue 'Invalid date'
69
+ end
70
+
71
+ def modulo(input, operand)
72
+ apply_operation(input, operand, :%)
73
+ end
74
+
75
+ def round(input, n = 0)
76
+ result = to_number(input).round(to_number(n))
77
+ result = result.to_f if result.is_a?(BigDecimal)
78
+ result = result.to_i if n == 0
79
+ result
80
+ end
81
+
82
+ def ceil(input)
83
+ to_number(input).ceil.to_i
84
+ end
85
+
86
+ def floor(input)
87
+ to_number(input).floor.to_i
88
+ end
89
+
90
+ def currency(input, currency_code = 'BRL')
91
+ price_to_currency(input, currency_code, :converted => false)
92
+ end
93
+
94
+ def call_method(input, method_name)
95
+ if input.respond_to?(method_name)
96
+ input.method(method_name).call
97
+ end
98
+ end
99
+
100
+ def custom_field(input, field_name)
101
+ if input.respond_to?(:custom_fields)
102
+ input.custom_fields[field_name]
103
+ end
104
+ end
105
+
106
+ def attachment(input, file_name)
107
+ if input.respond_to?(:attachments)
108
+ if input.attachments.is_a?(Hash)
109
+ attachment = input.attachments[file_name]
110
+ else
111
+ attachment = input.attachments.detect{|a| a.file_name == file_name}
112
+ end
113
+ AttachmentDrop.new attachment if attachment
114
+ end
115
+ end
116
+
117
+ protected
118
+
119
+ # Convert an array of properties ('key:value') into a hash
120
+ # Ex: ['width:50', 'height:100'] => { :width => '50', :height => '100' }
121
+ def args_to_options(*args)
122
+ options = {}
123
+ args.flatten.each do |a|
124
+ if (a =~ /^(.*):(.*)$/)
125
+ options[$1.to_sym] = $2
126
+ end
127
+ end
128
+ options
129
+ end
130
+
131
+ # Write options (Hash) into a string according to the following pattern:
132
+ # <key1>="<value1>", <key2>="<value2", ...etc
133
+ def inline_options(options = {})
134
+ return '' if options.empty?
135
+ (options.stringify_keys.sort.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
136
+ end
137
+
138
+ def sort_input(input, property, order)
139
+ input.sort do |apple, orange|
140
+ apple_property = item_property(apple, property)
141
+ orange_property = item_property(orange, property)
142
+
143
+ if !apple_property.nil? && orange_property.nil?
144
+ - order
145
+ elsif apple_property.nil? && !orange_property.nil?
146
+ + order
147
+ else
148
+ apple_property <=> orange_property
149
+ end
150
+ end
151
+ end
152
+
153
+ def time(input)
154
+ case input
155
+ when Time
156
+ input.clone
157
+ when Date
158
+ input.to_time
159
+ when String
160
+ Time.parse(input) rescue Time.at(input.to_i)
161
+ when Numeric
162
+ Time.at(input)
163
+ else
164
+ raise Errors::InvalidDateError,
165
+ "Invalid Date: '#{input.inspect}' is not a valid datetime."
166
+ end.localtime
167
+ end
168
+
169
+ def groupable?(element)
170
+ element.respond_to?(:group_by)
171
+ end
172
+
173
+ def item_property(item, property)
174
+ if item.respond_to?(:to_liquid)
175
+ item.to_liquid[property.to_s]
176
+ elsif item.respond_to?(:data)
177
+ item.data[property.to_s]
178
+ else
179
+ item[property.to_s]
180
+ end
181
+ end
182
+
183
+ def as_liquid(item)
184
+ case item
185
+ when Hash
186
+ pairs = item.map { |k, v| as_liquid([k, v]) }
187
+ Hash[pairs]
188
+ when Array
189
+ item.map { |i| as_liquid(i) }
190
+ else
191
+ if item.respond_to?(:to_liquid)
192
+ liquidated = item.to_liquid
193
+ # prevent infinite recursion for simple types (which return `self`)
194
+ if liquidated == item
195
+ item
196
+ else
197
+ as_liquid(liquidated)
198
+ end
199
+ else
200
+ item
201
+ end
202
+ end
203
+ end
204
+ end
205
+ ::Liquid::Template.register_filter(YeshouaCrm::Liquid::Filters::Base)
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,65 @@
1
+ require 'action_view'
2
+
3
+ module YeshouaCrm
4
+ module MoneyHelper
5
+
6
+ def object_price(obj, price_field = :price, options = {})
7
+ options.merge!({:symbol => true})
8
+ price_to_currency(obj.try(price_field), obj.currency, options).to_s if obj.respond_to?(:currency)
9
+ end
10
+
11
+ def prices_collection_by_currency(prices_collection, options={})
12
+ return [] if prices_collection.blank? || prices_collection == 0
13
+ prices = prices_collection
14
+ prices.reject!{|k, v| v.to_i == 0} if options[:hide_zeros]
15
+ prices.collect{|k, v| content_tag(:span, price_to_currency(v, k, :symbol => true), :style => "white-space: nowrap;")}.compact
16
+ end
17
+
18
+ def deal_currency_icon(currency)
19
+ case currency.to_s.upcase
20
+ when 'EUR'
21
+ "icon-money-euro"
22
+ when 'GBP'
23
+ "icon-money-pound"
24
+ when 'JPY'
25
+ "icon-money-yen"
26
+ else
27
+ "icon-money-dollar"
28
+ end
29
+ end
30
+
31
+ def collection_for_currencies_select(default_currency = 'BRL', major_currencies = %w(BRL USD EUR GBP RUB CHF))
32
+ currencies = []
33
+ currencies << default_currency.to_s unless default_currency.blank?
34
+ currencies |= major_currencies
35
+ currencies.map do |c|
36
+ currency = YeshouaCrm::Currency.find(c)
37
+ ["#{currency.iso_code} (#{currency.symbol})", currency.iso_code] if currency
38
+ end.compact.uniq
39
+ end
40
+
41
+ def all_currencies
42
+ Currency.table.inject([]) do |array, (id, attributes)|
43
+ array ||= []
44
+ array << ["#{attributes[:name]}" + (attributes[:symbol].blank? ? "" : " (#{attributes[:symbol]})"), attributes[:iso_code]]
45
+ array
46
+ end.sort{|x, y| x[0] <=> y[0]}
47
+ end
48
+
49
+ def price_to_currency(price, currency="BRL", options={})
50
+ return '' if price.blank?
51
+
52
+ currency = "BRL" unless currency.is_a?(String)
53
+ currency = YeshouaCrm::Currency.find(currency)
54
+
55
+ return YeshouaCrm::Currency.format(price.to_f, currency, options)# if currency
56
+ price.to_s
57
+ end
58
+
59
+
60
+ end
61
+ end
62
+
63
+ unless ActionView::Base.included_modules.include?(YeshouaCrm::MoneyHelper)
64
+ ActionView::Base.send(:include, YeshouaCrm::MoneyHelper)
65
+ end
@@ -0,0 +1,3 @@
1
+ module YeshouaCrm
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class DraftTest < ActiveSupport::TestCase
4
+ def test_restore
5
+ issue = Issue.new(subject: 'some subject', description: 'some description')
6
+
7
+ issue.save_draft(current_user)
8
+ restored_issue = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id).restore
9
+
10
+ assert_equal issue.subject, restored_issue.subject
11
+ assert_equal issue.description, restored_issue.description
12
+ end
13
+
14
+ def test_restore_all
15
+ first_issue = Issue.new(subject: 'first subject')
16
+ first_issue.save_draft(current_user)
17
+ second_issue = Issue.new(subject: 'second subject')
18
+ second_issue.save_draft(current_user)
19
+
20
+ restored_issues = YeshouaCrm::ActsAsDraftable::Draft.restore_all
21
+ assert_equal [first_issue.subject, second_issue.subject], restored_issues.map(&:subject).sort
22
+ end
23
+
24
+ private
25
+
26
+ def current_user
27
+ users(:sam)
28
+ end
29
+ end
@@ -0,0 +1,185 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class RcrmActsAsDraftableTest < ActiveSupport::TestCase
4
+ def test_rcrm_acts_as_draftable_without_arguments
5
+ assert_nothing_raised do
6
+ Cell.rcrm_acts_as_draftable
7
+ end
8
+ end
9
+
10
+ class SomeClass < ActiveRecord::Base; end
11
+ def test_rcrm_acts_as_draftable_with_parent
12
+ assert_nothing_raised do
13
+ News.rcrm_acts_as_draftable(parent: :author)
14
+ end
15
+ assert User.new.respond_to?(:drafts)
16
+ end
17
+
18
+ def test_rcrm_acts_as_draftable_with_non_existing_parent
19
+ assert_raises do
20
+ Issue.rcrm_acts_as_draftable(parent: :foo)
21
+ end
22
+ end
23
+
24
+ def test_rcrm_acts_as_draftable_with_non_hash_argument
25
+ assert_raises do
26
+ Issue.rcrm_acts_as_draftable('bar')
27
+ end
28
+ end
29
+
30
+ def test_rcrm_acts_as_draftable_with_invalid_hash_key
31
+ assert_raises do
32
+ Issue.rcrm_acts_as_draftable(baz: 'qux')
33
+ end
34
+ end
35
+
36
+ def test_drafts
37
+ issue = Issue.new
38
+ issue.save_draft(current_user)
39
+
40
+ assert_equal 1, Issue.drafts(current_user).count
41
+ assert_equal YeshouaCrm::ActsAsDraftable::Draft, Issue.drafts(current_user).first.class
42
+ end
43
+
44
+ def test_from_draft
45
+ issue = Issue.new(subject: 'subject')
46
+ issue.save_draft(current_user)
47
+
48
+ issue_from_draft = Issue.from_draft(issue.draft_id)
49
+
50
+ assert_equal Issue, issue_from_draft.class
51
+ assert_equal true, issue_from_draft.new_record?
52
+ assert_equal issue.draft_id, issue_from_draft.draft_id
53
+
54
+ assert_equal issue.subject, issue_from_draft.subject
55
+ end
56
+
57
+ def test_draft_deleted_after_save
58
+ issue = Issue.new(subject: 'subject')
59
+ issue.save_draft(current_user)
60
+
61
+ issue_from_draft = Issue.from_draft(issue.draft_id)
62
+
63
+ assert_difference 'Issue.count', 1 do
64
+ assert_difference 'YeshouaCrm::ActsAsDraftable::Draft.count', -1 do
65
+ issue_from_draft.save!
66
+ end
67
+ end
68
+ assert_nil issue_from_draft.draft_id
69
+ end
70
+
71
+ def test_from_draft_with_non_existing_draft_id
72
+ assert_raises do
73
+ Issue.from_draft(999)
74
+ end
75
+ end
76
+
77
+ def test_from_draft_with_wrong_type
78
+ Cell.rcrm_acts_as_draftable
79
+
80
+ cell = Cell.new
81
+ cell.save_draft(current_user)
82
+
83
+ assert_raises do
84
+ Issue.from_draft(cell.draft_id)
85
+ end
86
+ end
87
+
88
+ def test_dump_to_draft
89
+ assert_equal String, Issue.new.dump_to_draft.class
90
+ end
91
+
92
+ def test_load_to_draft
93
+ attributes = {subject: 'subject', description: 'description'}
94
+ draft = Issue.new(attributes).dump_to_draft
95
+
96
+ issue = Issue.new
97
+ issue.load_from_draft(draft)
98
+
99
+ assert_equal attributes[:subject], issue.subject
100
+ assert_equal attributes[:description], issue.description
101
+ end
102
+
103
+ def test_save_draft
104
+ attributes = {subject: 'subject'}
105
+ issue = Issue.new(attributes)
106
+
107
+ assert_difference 'Issue.count', 0 do
108
+ assert_difference 'YeshouaCrm::ActsAsDraftable::Draft.count', 1 do
109
+ result = issue.save_draft(current_user)
110
+ assert_equal true, result
111
+ end
112
+ end
113
+ assert_equal Fixnum, issue.draft_id.class
114
+
115
+
116
+ draft = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id)
117
+ assert_equal 'Issue', draft.target_type
118
+ assert_equal current_user.id, draft.user_id
119
+ assert_equal attributes[:subject], draft.restore.subject
120
+ end
121
+
122
+ def test_save_draft_without_user
123
+ issue = Issue.new
124
+
125
+ assert_difference 'Issue.count', 0 do
126
+ assert_difference 'YeshouaCrm::ActsAsDraftable::Draft.count', 1 do
127
+ result = issue.save_draft
128
+ assert_equal true, result
129
+ end
130
+ end
131
+
132
+ draft = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id)
133
+ assert_nil draft.user_id
134
+ end
135
+
136
+ def test_save_draft_with_associations
137
+ issue = Issue.new(cell: cells(:second_cell))
138
+ issue.save_draft(current_user)
139
+
140
+ draft = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id)
141
+ assert_equal issue.cell.name, draft.restore.cell.name
142
+ end
143
+
144
+ def test_save_draft_updates_existing_draft
145
+ issue = Issue.new
146
+ issue.save_draft(current_user)
147
+
148
+ issue.subject = 'changed subject'
149
+ assert_difference 'Issue.count', 0 do
150
+ assert_difference 'YeshouaCrm::ActsAsDraftable::Draft.count', 0 do
151
+ issue.save_draft(current_user)
152
+ end
153
+ end
154
+
155
+ draft = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id)
156
+ assert_equal issue.subject, draft.restore.subject
157
+ end
158
+
159
+ def test_save_draft_does_not_save_persisted_model
160
+ issue = Issue.new
161
+ issue.save_draft(current_user)
162
+ issue.save!
163
+ assert_equal false, issue.save_draft(current_user)
164
+ end
165
+
166
+ def test_update_draft
167
+ issue = Issue.new(subject: 'subject')
168
+ issue.save_draft(current_user)
169
+
170
+ assert_difference 'Issue.count', 0 do
171
+ assert_difference 'YeshouaCrm::ActsAsDraftable::Draft.count', 0 do
172
+ issue.update_draft(current_user, subject: 'updated_subject')
173
+ end
174
+ end
175
+
176
+ draft = YeshouaCrm::ActsAsDraftable::Draft.find(issue.draft_id)
177
+ assert_equal issue.subject, draft.restore.subject
178
+ end
179
+
180
+ private
181
+
182
+ def current_user
183
+ users(:sam)
184
+ end
185
+ end