flowcommerce_spree 0.0.2 → 0.0.7

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -13
  3. data/SPREE_FLOW.md +6 -28
  4. data/app/controllers/concerns/current_zone_loader_decorator.rb +33 -25
  5. data/app/controllers/flowcommerce_spree/inventory_controller.rb +23 -0
  6. data/app/controllers/flowcommerce_spree/orders_controller.rb +20 -0
  7. data/app/controllers/flowcommerce_spree/webhooks_controller.rb +23 -13
  8. data/app/controllers/users/sessions_controller_decorator.rb +28 -0
  9. data/app/helpers/spree/core/controller_helpers/flow_io_order_helper_decorator.rb +4 -9
  10. data/app/models/spree/address_decorator.rb +19 -0
  11. data/app/models/spree/calculator/flow_io.rb +61 -0
  12. data/app/models/spree/calculator/shipping/flow_io.rb +40 -0
  13. data/app/models/spree/flow_io_credit_card_decorator.rb +21 -0
  14. data/app/models/spree/flow_io_order_decorator.rb +163 -0
  15. data/app/models/spree/flow_io_variant_decorator.rb +4 -2
  16. data/app/models/spree/gateway/flow_io.rb +153 -0
  17. data/app/models/spree/{credit_card_decorator.rb → payment_capture_event_decorator.rb} +1 -1
  18. data/app/models/spree/promotion_handler/coupon_decorator.rb +1 -1
  19. data/app/models/spree/zones/flow_io_product_zone_decorator.rb +8 -0
  20. data/app/models/tracking/setup_decorator.rb +40 -0
  21. data/app/overrides/spree/admin/order_sidebar_summary_flow_link.rb +13 -0
  22. data/app/overrides/spree/admin/products/order_price_flow_message.rb +9 -0
  23. data/app/serializers/api/v2/order_serializer_decorator.rb +20 -0
  24. data/app/services/flowcommerce_spree/import_experience_items.rb +1 -1
  25. data/app/services/flowcommerce_spree/order_sync.rb +81 -173
  26. data/app/services/flowcommerce_spree/order_updater.rb +78 -0
  27. data/app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb +76 -0
  28. data/app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb +66 -0
  29. data/app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb +25 -0
  30. data/app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb +35 -0
  31. data/app/services/flowcommerce_spree/webhooks/local_item_upserted.rb +40 -0
  32. data/app/views/spree/admin/payments/source_views/_flow_io_gateway.html.erb +21 -0
  33. data/config/rails_best_practices.yml +51 -0
  34. data/config/routes.rb +3 -1
  35. data/db/migrate/20201021755957_add_meta_to_spree_tables.rb +6 -4
  36. data/lib/flow/simple_gateway.rb +0 -36
  37. data/lib/flowcommerce_spree.rb +17 -3
  38. data/lib/flowcommerce_spree/engine.rb +33 -3
  39. data/lib/flowcommerce_spree/experience_service.rb +1 -27
  40. data/lib/flowcommerce_spree/logging_http_client.rb +33 -15
  41. data/lib/flowcommerce_spree/session.rb +17 -32
  42. data/lib/flowcommerce_spree/test_support.rb +7 -0
  43. data/lib/flowcommerce_spree/version.rb +1 -1
  44. data/lib/tasks/flowcommerce_spree.rake +4 -1
  45. metadata +88 -21
  46. data/app/mailers/spree/spree_order_mailer_decorator.rb +0 -24
  47. data/app/models/spree/gateway/spree_flow_gateway.rb +0 -116
  48. data/app/models/spree/line_item_decorator.rb +0 -15
  49. data/app/models/spree/order_decorator.rb +0 -179
  50. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -86
  51. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -38
  52. data/config/initializers/flowcommerce_spree.rb +0 -7
  53. data/lib/flow/error.rb +0 -73
  54. data/lib/flow/pay_pal.rb +0 -25
  55. data/lib/flowcommerce_spree/webhook_service.rb +0 -98
  56. data/lib/simple_csv_writer.rb +0 -44
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class CardAuthorizationUpsertedV2
6
+ attr_reader :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data)
10
+ new(data).process
11
+ end
12
+
13
+ def initialize(data)
14
+ @data = data['authorization']&.to_hash
15
+ @data&.[]('method')&.delete('images')
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ errors << { message: 'Authorization param missing' } && (return self) unless @data
21
+
22
+ errors << { message: 'Card param missing' } && (return self) unless (flow_io_card = @data.delete('card'))
23
+
24
+ if (order_number = @data.dig('order', 'number'))
25
+ if (order = Spree::Order.find_by(number: order_number))
26
+ card = upsert_card(flow_io_card, order)
27
+
28
+ order.payments.where(response_code: @data['id'])
29
+ .update_all(source_id: card.id, source_type: 'Spree::CreditCard')
30
+
31
+ return card
32
+ else
33
+ errors << { message: "Order #{order_number} not found" }
34
+ end
35
+ else
36
+ errors << { message: 'Order number param missing' }
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def upsert_card(flow_io_card, order)
45
+ flow_io_card_expiration = flow_io_card.delete('expiration')
46
+
47
+ card = Spree::CreditCard.find_or_initialize_by(month: flow_io_card_expiration['month'].to_s,
48
+ year: flow_io_card_expiration['year'].to_s,
49
+ cc_type: flow_io_card.delete('type'),
50
+ last_digits: flow_io_card.delete('last4'),
51
+ name: flow_io_card.delete('name'),
52
+ user_id: order.user&.id)
53
+ card.flow_data ||= {}
54
+ if card.new_record?
55
+ card.flow_data.merge!(flow_io_card.except('discriminator'))
56
+ card.imported = true
57
+ end
58
+
59
+ card.push_authorization(@data.except('discriminator'))
60
+ card.new_record? ? card.save : card.update_column(:meta, card.meta.to_json)
61
+
62
+ card
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class ExperienceUpsertedV2
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ experience = @data['experience']
21
+ Spree::Zones::Product.find_or_initialize_by(name: experience['key'].titleize).store_flow_io_data(experience)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class FraudStatusChanged
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ order_number = @data.dig('order', 'number')
21
+ errors << { message: 'Order number param missing' } && (return self) unless order_number
22
+
23
+ order = Spree::Order.find_by(number: order_number)
24
+ errors << { message: "Order #{order_number} not found" } && (return self) unless order
25
+
26
+ if @data['status'] == 'declined'
27
+ order.update_columns(fraudulent: true)
28
+ order.cancel!
29
+ end
30
+
31
+ order
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowcommerceSpree
4
+ module Webhooks
5
+ class LocalItemUpserted
6
+ attr_accessor :errors
7
+ alias full_messages errors
8
+
9
+ def self.process(data, opts = {})
10
+ new(data, opts).process
11
+ end
12
+
13
+ def initialize(data, opts = {})
14
+ @data = data
15
+ @opts = opts
16
+ @errors = []
17
+ end
18
+
19
+ def process
20
+ errors << { message: 'Local item param missing' } && (return self) unless (local_item = @data['local_item'])
21
+
22
+ errors << { message: 'SKU param missing' } && (return self) unless (flow_sku = local_item.dig('item', 'number'))
23
+
24
+ if (variant = Spree::Variant.find_by(sku: flow_sku))
25
+ variant.add_flow_io_experience_data(
26
+ local_item.dig('experience', 'key'),
27
+ 'prices' => [local_item.dig('pricing', 'price')], 'status' => local_item['status']
28
+ )
29
+
30
+ variant.update_column(:meta, variant.meta.to_json)
31
+ return variant
32
+ else
33
+ errors << { message: "Variant with sku [#{flow_sku}] not found!" }
34
+ end
35
+
36
+ self
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ <fieldset data-hook="credit_card">
2
+ <legend align="center"><%= Spree.t(:credit_card) %></legend>
3
+
4
+ <div class="row">
5
+ <div class="alpha six columns">
6
+ <dl>
7
+ <dt><%= Spree.t(:name_on_card) %>:</dt>
8
+ <dd><%= payment.source&.name %></dd>
9
+
10
+ <dt><%= Spree.t(:card_type) %>:</dt>
11
+ <dd><%= payment.source&.cc_type %></dd>
12
+
13
+ <dt><%= Spree.t(:card_number) %>:</dt>
14
+ <dd><%= payment.source&.display_number %></dd>
15
+
16
+ <dt><%= Spree.t(:expiration) %>:</dt>
17
+ <dd><%= payment.source&.month %>/<%= payment.source&.year %></dd>
18
+ </dl>
19
+ </div>
20
+ </div>
21
+ </fieldset>
@@ -0,0 +1,51 @@
1
+ AddModelVirtualAttributeCheck: { }
2
+ AlwaysAddDbIndexCheck: { }
3
+ #CheckSaveReturnValueCheck: { }
4
+ #CheckDestroyReturnValueCheck: { }
5
+ DefaultScopeIsEvilCheck: { }
6
+ DryBundlerInCapistranoCheck: { }
7
+ #HashSyntaxCheck: { }
8
+ IsolateSeedDataCheck: { }
9
+ KeepFindersOnTheirOwnModelCheck: { }
10
+ LawOfDemeterCheck: { }
11
+ #LongLineCheck: { max_line_length: 80 }
12
+ MoveCodeIntoControllerCheck: { }
13
+ MoveCodeIntoHelperCheck: { array_count: 3 }
14
+ MoveCodeIntoModelCheck: { use_count: 2 }
15
+ MoveFinderToNamedScopeCheck: { }
16
+ MoveModelLogicIntoModelCheck: { use_count: 4 }
17
+ NeedlessDeepNestingCheck: { nested_count: 2 }
18
+ NotRescueExceptionCheck: { }
19
+ NotUseDefaultRouteCheck: { }
20
+ NotUseTimeAgoInWordsCheck: { }
21
+ OveruseRouteCustomizationsCheck: { customize_count: 3 }
22
+ ProtectMassAssignmentCheck: { }
23
+ RemoveEmptyHelpersCheck: { }
24
+ #RemoveTabCheck: { }
25
+ RemoveTrailingWhitespaceCheck: { }
26
+ RemoveUnusedMethodsInControllersCheck: { except_methods: [] }
27
+ RemoveUnusedMethodsInHelpersCheck: { except_methods: [] }
28
+ RemoveUnusedMethodsInModelsCheck: { except_methods:
29
+ [
30
+ 'Spree::Calculator::FlowIo#compute_shipment', # Used by Spree::Calculator
31
+ 'Spree::Calculator::FlowIo#compute_line_item', # Used by Spree::Calculator
32
+ 'Spree::Calculator::FlowIo#description', # Used by Spree
33
+ 'Spree::Shipping::FlowIo#compute_package', # Used by Spree
34
+ 'Spree::Shipping::FlowIo#default_charge', # Used by Spree
35
+ 'Spree::Shipping::FlowIo#threshold', # Used by Spree
36
+ 'Spree::Shipping::FlowIo#description', # Used by Spree
37
+ ] }
38
+ ReplaceComplexCreationWithFactoryMethodCheck: { attribute_assignment_count: 2 }
39
+ ReplaceInstanceVariableWithLocalVariableCheck: { }
40
+ RestrictAutoGeneratedRoutesCheck: { }
41
+ SimplifyRenderInControllersCheck: { }
42
+ SimplifyRenderInViewsCheck: { }
43
+ #UseBeforeFilterCheck: { customize_count: 2 }
44
+ UseModelAssociationCheck: { }
45
+ UseMultipartAlternativeAsContentTypeOfEmailCheck: { }
46
+ #UseParenthesesInMethodDefCheck: { }
47
+ UseObserverCheck: { }
48
+ UseQueryAttributeCheck: { }
49
+ UseSayWithTimeInMigrationsCheck: { }
50
+ UseScopeAccessCheck: { }
51
+ UseTurboSprocketsRails3Check: { }
data/config/routes.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  FlowcommerceSpree::Engine.routes.draw do
4
- post '/event-target', to: 'webhooks#handle_flow_web_hook_event'
4
+ post '/event-target', to: 'webhooks#handle_flow_io_event'
5
+ get '/order-completed', to: 'orders#order_completed'
6
+ post '/online-stock-availability', to: 'inventory#online_stock_availability'
5
7
  end
@@ -5,13 +5,15 @@ class AddMetaToSpreeTables < ActiveRecord::Migration
5
5
  add_column :spree_orders, :meta, :jsonb, default: '{}' unless column_exists?(:spree_orders, :meta)
6
6
  add_column :spree_promotions, :meta, :jsonb, default: '{}' unless column_exists?(:spree_promotions, :meta)
7
7
  add_column :spree_credit_cards, :meta, :jsonb, default: '{}' unless column_exists?(:spree_credit_cards, :meta)
8
+ add_column :spree_payment_capture_events, :meta, :jsonb, default: '{}' unless column_exists?(:spree_payment_capture_events, :meta)
8
9
  end
9
10
 
10
11
  def down
11
- remove_column :spree_products, :meta if column_exists?(:spree_products, :meta)
12
- remove_column :spree_variants, :meta if column_exists?(:spree_variants, :meta)
13
- remove_column :spree_orders, :meta if column_exists?(:spree_orders, :meta)
14
- remove_column :spree_promotions, :meta if column_exists?(:spree_promotions, :meta)
12
+ remove_column :spree_payment_capture_events, :meta if column_exists?(:spree_payment_capture_events, :meta)
15
13
  remove_column :spree_credit_cards, :meta if column_exists?(:spree_credit_cards, :meta)
14
+ remove_column :spree_promotions, :meta if column_exists?(:spree_promotions, :meta)
15
+ remove_column :spree_orders, :meta if column_exists?(:spree_orders, :meta)
16
+ remove_column :spree_variants, :meta if column_exists?(:spree_variants, :meta)
17
+ remove_column :spree_products, :meta if column_exists?(:spree_products, :meta)
16
18
  end
17
19
  end
@@ -34,42 +34,6 @@ module Flow
34
34
  error_response(e)
35
35
  end
36
36
 
37
- # capture authorised funds
38
- def cc_capture
39
- # GET /:organization/authorizations, order_number: abc
40
- data = @order.flow_data['authorization']
41
-
42
- raise ArgumentError, 'No Authorization data, please authorize first' unless data
43
-
44
- capture_form = ::Io::Flow::V0::Models::CaptureForm.new(data)
45
- response = FlowcommerceSpree.client.captures.post(FlowcommerceSpree::ORGANIZATION, capture_form)
46
-
47
- return ActiveMerchant::Billing::Response.new false, 'error', response: response unless response.id
48
-
49
- @order.update_column :flow_data, @order.flow_data.merge('capture': response.to_hash)
50
- @order.flow_finalize!
51
-
52
- ActiveMerchant::Billing::Response.new true, 'success', response: response
53
- rescue StandardError => e
54
- error_response(e)
55
- end
56
-
57
- def cc_refund
58
- raise ArgumentError, 'capture info is not available' unless @order.flow_data['capture']
59
-
60
- # we allways have capture ID, so we use it
61
- refund_data = { capture_id: @order.flow_data['capture']['id'] }
62
- refund_form = ::Io::Flow::V0::Models::RefundForm.new(refund_data)
63
- response = FlowcommerceSpree.client.refunds.post(FlowcommerceSpree::ORGANIZATION, refund_form)
64
-
65
- return ActiveMerchant::Billing::Response.new false, 'error', response: response unless response.id
66
-
67
- @order.update_column :flow_data, @order.flow_data.merge('refund': response.to_hash)
68
- ActiveMerchant::Billing::Response.new true, 'success', response: response
69
- rescue StandardError => e
70
- error_response(e)
71
- end
72
-
73
37
  private
74
38
 
75
39
  # if order is not in flow, we use local Spree settings
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'activerecord-postgres-json'
4
+ require 'active_model_serializers'
3
5
  require 'flowcommerce'
4
6
  require 'flowcommerce_spree/api'
5
7
  require 'flowcommerce_spree/refresher'
6
8
  require 'flowcommerce_spree/engine'
7
9
  require 'flowcommerce_spree/logging_http_client'
8
10
  require 'flowcommerce_spree/logging_http_handler'
9
- require 'flowcommerce_spree/webhook_service'
10
11
  require 'flowcommerce_spree/session'
11
12
  require 'flow/simple_gateway'
13
+ require 'oj'
12
14
 
13
15
  module FlowcommerceSpree
16
+ API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
17
+ ENV['FLOW_TOKEN'] = API_KEY
18
+
14
19
  def self.client(logger: FlowcommerceSpree.logger, **opts)
15
20
  FlowCommerce.instance(http_handler: LoggingHttpHandler.new(logger: logger), **opts)
16
21
  end
@@ -20,11 +25,20 @@ module FlowcommerceSpree
20
25
  end
21
26
 
22
27
  def self.logger
23
- logger = ActiveSupport::Logger.new(STDOUT, 3, 10_485_760)
28
+ logger = ActiveSupport::Logger.new(STDOUT)
29
+
30
+ logger_formatter = proc do |severity, datetime, _progname, msg|
31
+ "\n#{datetime}, #{severity}: #{msg}\n"
32
+ end
33
+
34
+ logger.formatter = logger_formatter
24
35
 
25
36
  # Broadcast the log into the file besides STDOUT, if `log` folder exists
26
37
  if Dir.exist?('log')
27
- logger.extend(ActiveSupport::Logger.broadcast(ActiveSupport::Logger.new('log/flowcommerce_spree.log')))
38
+ file_logger = ActiveSupport::Logger.new('log/flowcommerce_spree.log', 3, 10_485_760)
39
+ file_logger.formatter = logger_formatter
40
+
41
+ logger.extend(ActiveSupport::Logger.broadcast(file_logger))
28
42
  end
29
43
  logger
30
44
  end
@@ -6,22 +6,52 @@ module FlowcommerceSpree
6
6
  isolate_namespace FlowcommerceSpree
7
7
 
8
8
  config.before_initialize do
9
+ FlowcommerceSpree::ORGANIZATION = ENV.fetch('FLOW_ORGANIZATION', 'flow.io')
10
+ FlowcommerceSpree::BASE_COUNTRY = ENV.fetch('FLOW_BASE_COUNTRY', 'USA')
11
+ FlowcommerceSpree::API_KEY = ENV.fetch('FLOW_TOKEN', 'test_key')
12
+ FlowcommerceSpree::FLOW_IO_WEBHOOK_USER = ENV.fetch('FLOW_IO_WEBHOOK_USER', 'test_user')
13
+ FlowcommerceSpree::FLOW_IO_WEBHOOK_PASSWORD = ENV.fetch('FLOW_IO_WEBHOOK_PASSWORD', 'test_password')
14
+
9
15
  FlowcommerceSpree::Config = FlowcommerceSpree::Settings.new
10
16
  end
11
17
 
12
- config.after_initialize do
18
+ config.flowcommerce_spree = ActiveSupport::OrderedOptions.new
19
+
20
+ initializer 'flowcommerce_spree.configuration' do |app|
21
+ # If some Rake tasks will fail in development environment, the cause could be the autoloading.
22
+ # Uncommenting the following 3 lines will enable eager-loading for the flowcommerce_spree Rake tasks.
23
+ # if Rails.env.development?
24
+ # app.config.eager_load = Rake.application.top_level_tasks.any? { |t| t.start_with?('flowcommerce_spree') }
25
+ # end
26
+
27
+ app.config.flowcommerce_spree[:mounted_path] = ENV.fetch('FLOW_MOUNT_PATH', '/flow')
28
+
29
+ app.routes.prepend do
30
+ mount FlowcommerceSpree::Engine => app.config.flowcommerce_spree[:mounted_path]
31
+ end
32
+ end
33
+
34
+ config.after_initialize do |app|
13
35
  # init Flow payments as an option
14
- # app.config.spree.payment_methods << Spree::Gateway::Flow
36
+ app.config.spree.payment_methods << Spree::Gateway::FlowIo
15
37
 
16
- Flow::SimpleGateway.clear_zero_amount_payments = true
38
+ # Flow::SimpleGateway.clear_zero_amount_payments = true
17
39
  end
18
40
 
19
41
  def self.activate
20
42
  Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')).sort.each do |c|
21
43
  Rails.configuration.cache_classes ? require(c) : load(c)
22
44
  end
45
+ Dir.glob(File.join(File.dirname(__FILE__), '../app/overrides/*.rb')).sort.each do |c|
46
+ Rails.configuration.cache_classes ? require(c) : load(c)
47
+ end
23
48
  end
24
49
 
25
50
  config.to_prepare(&method(:activate).to_proc)
51
+
52
+ initializer 'spree.flowcommerce_spree.calculators', after: 'spree.register.calculators' do |_app|
53
+ Rails.application.config.spree.calculators.tax_rates << Spree::Calculator::FlowIo
54
+ Rails.application.config.spree.calculators.shipping_methods << Spree::Calculator::Shipping::FlowIo
55
+ end
26
56
  end
27
57
  end
@@ -22,18 +22,6 @@ module FlowcommerceSpree
22
22
  nil
23
23
  end
24
24
 
25
- def get_by_subcatalog_id(subcatalog_id)
26
- fetch_from_flow.each do |experince|
27
- return experince if experince.subcatalog.id == subcatalog_id
28
- end
29
-
30
- nil
31
- end
32
-
33
- def compact
34
- all.map { |exp| [exp.country, exp.key, exp.name] }
35
- end
36
-
37
25
  def default
38
26
  FlowcommerceSpree::ExperienceService
39
27
  .all.select { |exp| exp.key.downcase == ENV.fetch('FLOW_BASE_COUNTRY').downcase }.first
@@ -42,24 +30,10 @@ module FlowcommerceSpree
42
30
  private
43
31
 
44
32
  def fetch_from_flow
45
- # return cached_experinces if cache_valid?
46
-
47
- experiences = FlowcommerceSpree.client.experiences.get ORGANIZATION
33
+ FlowcommerceSpree.client.experiences.get ORGANIZATION
48
34
 
49
35
  # work with active axperiences only
50
36
  # experiences = experiences.select { |it| it.status.value == 'active' }
51
-
52
- # @cache = [experiences, Time.now]
53
- experiences
54
- end
55
-
56
- def cache_valid?
57
- # cache experinces in worker memory for 1 minute
58
- @cache && @cache[1] > Time.now.ago(1.minute)
59
- end
60
-
61
- def cached_experinces
62
- @cache[0]
63
37
  end
64
38
  end
65
39
  end