flowcommerce_spree 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/README.md +35 -6
- data/app/controllers/flowcommerce_spree/webhooks_controller.rb +16 -18
- data/app/models/spree/app_configuration_decorator.rb +7 -0
- data/app/models/spree/calculator/shipping/flow_io.rb +5 -2
- data/app/models/spree/flow_io_order_decorator.rb +11 -58
- data/app/models/spree/flow_io_variant_decorator.rb +2 -0
- data/app/overrides/spree/admin/order_sidebar_summary_flow_link.rb +13 -0
- data/app/overrides/spree/admin/products/order_price_flow_message.rb +9 -0
- data/app/services/flowcommerce_spree/order_sync.rb +38 -81
- data/app/services/flowcommerce_spree/webhooks/capture_upserted_v2.rb +76 -0
- data/app/services/flowcommerce_spree/webhooks/card_authorization_upserted_v2.rb +66 -0
- data/app/services/flowcommerce_spree/webhooks/experience_upserted_v2.rb +25 -0
- data/app/services/flowcommerce_spree/webhooks/fraud_status_changed.rb +35 -0
- data/app/services/flowcommerce_spree/webhooks/local_item_upserted.rb +40 -0
- data/config/routes.rb +1 -1
- data/lib/flowcommerce_spree.rb +3 -1
- data/lib/flowcommerce_spree/engine.rb +5 -0
- data/lib/flowcommerce_spree/experience_service.rb +1 -27
- data/lib/flowcommerce_spree/version.rb +1 -1
- data/lib/tasks/flowcommerce_spree.rake +4 -1
- metadata +74 -17
- data/app/mailers/spree/spree_order_mailer_decorator.rb +0 -24
- data/app/views/spree/order_mailer/confirm_email.html.erb +0 -86
- data/app/views/spree/order_mailer/confirm_email.text.erb +0 -38
- data/lib/flow/error.rb +0 -73
- data/lib/flow/pay_pal.rb +0 -25
- data/lib/flowcommerce_spree/webhook_service.rb +0 -154
- data/lib/simple_csv_writer.rb +0 -44
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Spree
|
4
|
-
OrderMailer.class_eval do
|
5
|
-
# default from: ApplicationMailer::DEFAULT_FROM
|
6
|
-
|
7
|
-
def refund_complete_email(web_hook_event)
|
8
|
-
auth_id = web_hook_event.dig('refund', 'authorization', 'key')
|
9
|
-
|
10
|
-
raise Flow::Error, 'authorization key not found in WebHookEvent [refund_capture_upserted_v2]' unless auth_id
|
11
|
-
|
12
|
-
authorization = FlowcommerceSpree.client.authorizations.get_by_key FlowcommerceSpree::ORGANIZATION, auth_id
|
13
|
-
|
14
|
-
refund_requested = web_hook_event['refund']['requested']
|
15
|
-
@mail_to = authorization.customer.email
|
16
|
-
@full_name = "#{authorization.customer.name.first} #{authorization.customer.name.last}"
|
17
|
-
@amount = "#{refund_requested['amount']} #{refund_requested['currency']}"
|
18
|
-
@number = authorization.order.number
|
19
|
-
@order = Spree::Order.find_by number: @number
|
20
|
-
|
21
|
-
mail(to: @mail_to, subject: "We refunded your order for ammount #{@amount}")
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
<%
|
2
|
-
# render text email in console. parts[1] for html body
|
3
|
-
# puts Spree::OrderMailer.confirm_email(Spree::Order.last).body.parts[0].body
|
4
|
-
|
5
|
-
@prices = @order.flow_cart_breakdown
|
6
|
-
@total_price = @prices.pop
|
7
|
-
%>
|
8
|
-
|
9
|
-
<style>
|
10
|
-
table.order td { padding: 4px; border-top: 1px solid #bbb; }
|
11
|
-
</style>
|
12
|
-
|
13
|
-
<h6>Dear <%= @order.bill_address.firstname %></h6>
|
14
|
-
|
15
|
-
<br>
|
16
|
-
<br>
|
17
|
-
|
18
|
-
<p><%= Spree.t('order_mailer.confirm_email.instructions') %></p>
|
19
|
-
<p><%= Spree.t('order_mailer.confirm_email.order_summary') %></p>
|
20
|
-
|
21
|
-
<table class="order">
|
22
|
-
<tr>
|
23
|
-
<th>Product</th>
|
24
|
-
<th width="100" align="right">Price</th>
|
25
|
-
<th width="80" align="center">Quantity</th>
|
26
|
-
<th width="100" align="right">Total</th>
|
27
|
-
</tr>
|
28
|
-
<% @order.line_items.each do |line_item| %>
|
29
|
-
<tr>
|
30
|
-
<td><%= line_item.variant.product.name %></td>
|
31
|
-
<td align="right"><%= @order.flow_line_item_price(line_item) %></td>
|
32
|
-
<td align="center"><%= line_item.quantity %></td>
|
33
|
-
<td align="right"><%= @order.flow_line_item_price(line_item, :with_quantity) %></td>
|
34
|
-
</tr>
|
35
|
-
<% end %>
|
36
|
-
</table>
|
37
|
-
|
38
|
-
<br>
|
39
|
-
|
40
|
-
<p><b>Total</b></p>
|
41
|
-
|
42
|
-
<table class="order">
|
43
|
-
|
44
|
-
<% @prices.each do |price| %>
|
45
|
-
<tr><td width="120"><%= price.name.capitalize %></td><td align="right"><%= price.label %></td></tr>
|
46
|
-
<% end %>
|
47
|
-
|
48
|
-
<tr>
|
49
|
-
<td><%= Spree.t(:total) %></td>
|
50
|
-
<td align="right"><b><%= @total_price.label %></b></td>
|
51
|
-
</tr>
|
52
|
-
<tr>
|
53
|
-
<td>Payment method</td>
|
54
|
-
<td align="right"><%= @order.flow_payment_method == 'paypal' ? 'PayPal' : 'Credit Card' %></td>
|
55
|
-
</tr>
|
56
|
-
</table>
|
57
|
-
|
58
|
-
<br>
|
59
|
-
|
60
|
-
<% ['ship', 'bill'].each do |name|
|
61
|
-
address = @order.send('%s_address' % name)
|
62
|
-
%>
|
63
|
-
<p><b><%= name.capitalize %>ing address</b></p>
|
64
|
-
|
65
|
-
<table class="order">
|
66
|
-
<tr>
|
67
|
-
<td>Full name</td>
|
68
|
-
<td><%= address.firstname %> <%= address.lastname %></td>
|
69
|
-
</tr>
|
70
|
-
<tr>
|
71
|
-
<td>Address</td>
|
72
|
-
<td><%= address.address1 %></td>
|
73
|
-
</tr>
|
74
|
-
<tr>
|
75
|
-
<td>City</td>
|
76
|
-
<td><%= address.city %></td>
|
77
|
-
</tr>
|
78
|
-
<tr>
|
79
|
-
<td>Country</td>
|
80
|
-
<td><%= address.country.name rescue '-' %>, <%= address.state.name rescue '-' %></td>
|
81
|
-
</tr>
|
82
|
-
</table>
|
83
|
-
<br />
|
84
|
-
<% end %>
|
85
|
-
|
86
|
-
<p><%= Spree.t('order_mailer.confirm_email.thanks') %></p>
|
@@ -1,38 +0,0 @@
|
|
1
|
-
<%= Spree.t('order_mailer.confirm_email.dear_customer') %>
|
2
|
-
|
3
|
-
<%= Spree.t('order_mailer.confirm_email.instructions') %>
|
4
|
-
|
5
|
-
============================================================
|
6
|
-
<%= Spree.t('order_mailer.confirm_email.order_summary') %>
|
7
|
-
============================================================
|
8
|
-
<% @order.line_items.each do |item| %>
|
9
|
-
<%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= @order.flow_line_item_price(line_item, :with_quantity) %>
|
10
|
-
<% end %>
|
11
|
-
============================================================
|
12
|
-
<%= Spree.t('order_mailer.confirm_email.subtotal', :subtotal => @order.display_item_total) %>
|
13
|
-
<% if @order.line_item_adjustments.exists? %>
|
14
|
-
<% if @order.all_adjustments.promotion.eligible.exists? %>
|
15
|
-
<% @order.all_adjustments.promotion.eligible.group_by(&:label).each do |label, adjustments| %>
|
16
|
-
<%= Spree.t(:promotion) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
|
17
|
-
<% end %>
|
18
|
-
<% end %>
|
19
|
-
<% end %>
|
20
|
-
|
21
|
-
<% @order.shipments.group_by { |s| s.selected_shipping_rate.try(:name) }.each do |name, shipments| %>
|
22
|
-
<%= Spree.t(:shipping) %>: <%= name %> <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: @order.currency) %>
|
23
|
-
<% end %>
|
24
|
-
|
25
|
-
<% if @order.all_adjustments.eligible.tax.exists? %>
|
26
|
-
<% @order.all_adjustments.eligible.tax.group_by(&:label).each do |label, adjustments| %>
|
27
|
-
<%= Spree.t(:tax) %>: <%= label %> <%= Spree::Money.new(adjustments.sum(&:amount), currency: @order.currency) %>
|
28
|
-
<% end %>
|
29
|
-
<% end %>
|
30
|
-
|
31
|
-
<% @order.adjustments.eligible.each do |adjustment| %>
|
32
|
-
<% next if (adjustment.source_type == 'Spree::TaxRate') and (adjustment.amount == 0) %>
|
33
|
-
<%= adjustment.label %> <%= adjustment.display_amount %>
|
34
|
-
<% end %>
|
35
|
-
============================================================
|
36
|
-
<%= Spree.t('order_mailer.confirm_email.total', :total => @order.display_total) %>
|
37
|
-
|
38
|
-
<%= Spree.t('order_mailer.confirm_email.thanks') %>
|
data/lib/flow/error.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Flow (2017)
|
4
|
-
# api error logger and formater
|
5
|
-
|
6
|
-
require 'digest/sha1'
|
7
|
-
|
8
|
-
class Flow::Error < StandardError
|
9
|
-
# logs error to file for easy discovery and fix
|
10
|
-
def self.log(exception, request)
|
11
|
-
history = exception.backtrace.reject { |el| el.index('/gems/') }.map { |el| el.sub(Rails.root.to_s, '') }.join($/)
|
12
|
-
|
13
|
-
msg = "#{exception.class} in #{request.url}"
|
14
|
-
data = [msg, exception.message, history].join("\n\n")
|
15
|
-
key = Digest::SHA1.hexdigest(exception.backtrace.first.split(' ').first)
|
16
|
-
|
17
|
-
folder = Rails.root.join('log/exceptions').to_s
|
18
|
-
Dir.mkdir(folder) unless Dir.exist?(folder)
|
19
|
-
|
20
|
-
folder += "/#{exception.class.to_s.tableize.gsub('/', '-')}"
|
21
|
-
Dir.mkdir(folder) unless Dir.exist?(folder)
|
22
|
-
|
23
|
-
"#{folder}/#{key}.txt".tap do |path|
|
24
|
-
File.write(path, data)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.format_message(exception)
|
29
|
-
# format Flow errors in a special way
|
30
|
-
# Io::Flow::V0::HttpClient::ServerError - 422 Unprocessable Entity:
|
31
|
-
# {"code":"invalid_number","messages":["Card number is not valid"]}
|
32
|
-
# hash['code'] = 'invalid_number'
|
33
|
-
# hash['message'] = 'Card number is not valid'
|
34
|
-
# hash['title'] = '422 Unprocessable Entity'
|
35
|
-
# hash['klass'] = 'Io::Flow::V0::HttpClient::ServerError'
|
36
|
-
if exception.class == Io::Flow::V0::HttpClient::ServerError
|
37
|
-
parts = exception.message.split(': ', 2)
|
38
|
-
hash = Oj.load(parts[1])
|
39
|
-
|
40
|
-
hash[:message] = hash['messages'].join(', ')
|
41
|
-
hash[:title] = parts[0]
|
42
|
-
hash[:klass] = exception.class
|
43
|
-
hash[:code] = hash['code']
|
44
|
-
else
|
45
|
-
msg = exception.message.is_a?(Array) ? exception.message.join(' - ') : exception.message
|
46
|
-
|
47
|
-
hash = {}
|
48
|
-
hash[:message] = msg
|
49
|
-
hash[:title] = '-'
|
50
|
-
hash[:klass] = exception.class
|
51
|
-
hash[:code] = '-'
|
52
|
-
end
|
53
|
-
|
54
|
-
hash
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.format_order_message(order)
|
58
|
-
message = if order['messages']
|
59
|
-
msg = order['messages'].join(', ')
|
60
|
-
msg += " (#{Spree::Variant.where(id: order['numbers']).map(&:name).join(', ')})" if order['numbers']
|
61
|
-
msg
|
62
|
-
else
|
63
|
-
'Order not properly localized (sync issue)'
|
64
|
-
end
|
65
|
-
|
66
|
-
# sub_info = 'Flow.io'
|
67
|
-
# sub_info += ' - %s' % flow_experience.key[0, 15] if flow_experience
|
68
|
-
|
69
|
-
# '%s (%s)' % [message, sub_info]
|
70
|
-
|
71
|
-
message
|
72
|
-
end
|
73
|
-
end
|
data/lib/flow/pay_pal.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Flow.io (2017)
|
4
|
-
# communicates with flow api to synchronize Spree order with PayPal
|
5
|
-
|
6
|
-
module Flow::PayPal
|
7
|
-
extend self
|
8
|
-
|
9
|
-
def get_id(order)
|
10
|
-
raise 'PayPal only supported while using flow' unless order.flow_order
|
11
|
-
|
12
|
-
# get PayPal ID using Flow api
|
13
|
-
body = {
|
14
|
-
# discriminator: 'merchant_of_record_payment_form',
|
15
|
-
method: 'paypal',
|
16
|
-
order_number: order.number,
|
17
|
-
amount: order.flow_order.total.amount,
|
18
|
-
currency: order.flow_order.total.currency
|
19
|
-
}
|
20
|
-
|
21
|
-
# FlowcommerceSpree::Api.run :post, '/:organization/payments', {}, body
|
22
|
-
form = ::Io::Flow::V0::Models::MerchantOfRecordPaymentForm.new body
|
23
|
-
FlowcommerceSpree.client.payments.post FlowcommerceSpree::ORGANIZATION, form
|
24
|
-
end
|
25
|
-
end
|
@@ -1,154 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FlowcommerceSpree
|
4
|
-
# responds to webhook events from flow.io
|
5
|
-
class WebhookService
|
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
|
-
hook_method = @data['discriminator']
|
21
|
-
# If hook processing method registered an error, a self.object of WebhookService with this error will be
|
22
|
-
# returned, else an ActiveRecord object will be returned
|
23
|
-
return __send__(hook_method) if respond_to?(hook_method, true)
|
24
|
-
|
25
|
-
errors << { message: "No hook for #{hook_method}" }
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def capture_upserted_v2
|
32
|
-
errors << { message: 'Capture param missing' } && (return self) unless (capture = @data['capture']&.to_hash)
|
33
|
-
|
34
|
-
order_number = capture.dig('authorization', 'order', 'number')
|
35
|
-
if (order = Spree::Order.find_by(number: order_number))
|
36
|
-
order.flow_data['captures'] ||= []
|
37
|
-
order_captures = order.flow_data['captures']
|
38
|
-
order_captures.delete_if { |c| c['id'] == capture['id'] }
|
39
|
-
order_captures << capture
|
40
|
-
order.update_column(:meta, order.meta.to_json)
|
41
|
-
map_payment_captures_to_spree(order) if order.flow_io_payments.present?
|
42
|
-
order
|
43
|
-
else
|
44
|
-
errors << { message: "Order #{order_number} not found" }
|
45
|
-
self
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def card_authorization_upserted_v2
|
50
|
-
card_auth = @data['authorization']&.to_hash
|
51
|
-
errors << { message: 'Authorization param missing' } && (return self) unless card_auth
|
52
|
-
|
53
|
-
errors << { message: 'Card param missing' } && (return self) unless (flow_io_card = card_auth.delete('card'))
|
54
|
-
|
55
|
-
if (order_number = card_auth.dig('order', 'number'))
|
56
|
-
if (order = Spree::Order.find_by(number: order_number))
|
57
|
-
flow_io_card_expiration = flow_io_card.delete('expiration')
|
58
|
-
|
59
|
-
card = Spree::CreditCard.find_or_initialize_by(month: flow_io_card_expiration['month'].to_s,
|
60
|
-
year: flow_io_card_expiration['year'].to_s,
|
61
|
-
cc_type: flow_io_card.delete('type'),
|
62
|
-
last_digits: flow_io_card.delete('last4'),
|
63
|
-
name: flow_io_card.delete('name'),
|
64
|
-
user_id: order.user&.id)
|
65
|
-
card.flow_data ||= {}
|
66
|
-
card.flow_data.merge!(flow_io_card.except('discriminator')) if card.new_record?
|
67
|
-
card_auth['method'].delete('images')
|
68
|
-
card.push_authorization(card_auth.except('discriminator'))
|
69
|
-
if card.new_record?
|
70
|
-
card.imported = true
|
71
|
-
card.save!
|
72
|
-
else
|
73
|
-
card.update_column(:meta, card.meta.to_json)
|
74
|
-
end
|
75
|
-
|
76
|
-
order.payments.where(response_code: card_auth['id'])
|
77
|
-
.update_all(source_id: card.id, source_type: 'Spree::CreditCard')
|
78
|
-
|
79
|
-
return card
|
80
|
-
else
|
81
|
-
errors << { message: "Order #{order_number} not found" }
|
82
|
-
end
|
83
|
-
else
|
84
|
-
errors << { message: 'Order number param missing' }
|
85
|
-
end
|
86
|
-
|
87
|
-
self
|
88
|
-
end
|
89
|
-
|
90
|
-
def experience_upserted_v2
|
91
|
-
experience = @data['experience']
|
92
|
-
Spree::Zones::Product.find_or_initialize_by(name: experience['key'].titleize).store_flow_io_data(experience)
|
93
|
-
end
|
94
|
-
|
95
|
-
def fraud_status_changed
|
96
|
-
order_number = @data.dig('order', 'number')
|
97
|
-
errors << { message: 'Order number param missing' } && (return self) unless order_number
|
98
|
-
|
99
|
-
order = Spree::Order.find_by(number: order_number)
|
100
|
-
errors << { message: "Order #{order_number} not found" } && (return self) unless order
|
101
|
-
|
102
|
-
if @data['status'] == 'declined'
|
103
|
-
order.update_columns(fraudulent: true)
|
104
|
-
order.cancel!
|
105
|
-
end
|
106
|
-
|
107
|
-
order
|
108
|
-
end
|
109
|
-
|
110
|
-
def local_item_upserted
|
111
|
-
errors << { message: 'Local item param missing' } && (return self) unless (local_item = @data['local_item'])
|
112
|
-
|
113
|
-
errors << { message: 'SKU param missing' } && (return self) unless (flow_sku = local_item.dig('item', 'number'))
|
114
|
-
|
115
|
-
if (variant = Spree::Variant.find_by(sku: flow_sku))
|
116
|
-
variant.add_flow_io_experience_data(
|
117
|
-
local_item.dig('experience', 'key'),
|
118
|
-
'prices' => [local_item.dig('pricing', 'price')], 'status' => local_item['status']
|
119
|
-
)
|
120
|
-
|
121
|
-
variant.update_column(:meta, variant.meta.to_json)
|
122
|
-
return variant
|
123
|
-
else
|
124
|
-
errors << { message: "Variant with sku [#{flow_sku}] not found!" }
|
125
|
-
end
|
126
|
-
|
127
|
-
self
|
128
|
-
end
|
129
|
-
|
130
|
-
def map_payment_captures_to_spree(order)
|
131
|
-
payments = order.flow_data&.dig('order', 'payments')
|
132
|
-
order.flow_data['captures']&.each do |c|
|
133
|
-
next unless c['status'] == 'succeeded'
|
134
|
-
|
135
|
-
auth = c.dig('authorization', 'id')
|
136
|
-
next unless payments&.find { |p| p['reference'] == auth }
|
137
|
-
|
138
|
-
next unless (payment = Spree::Payment.find_by(response_code: auth))
|
139
|
-
|
140
|
-
next if Spree::PaymentCaptureEvent.where("meta -> 'flow_data' ->> 'id' = ?", c['id']).exists?
|
141
|
-
|
142
|
-
payment.capture_events.create!(amount: c['amount'], meta: { 'flow_data' => { 'id' => c['id'] } })
|
143
|
-
return if payment.completed? || payment.capture_events.sum(:amount) < payment.amount
|
144
|
-
|
145
|
-
payment.complete
|
146
|
-
end
|
147
|
-
|
148
|
-
return if order.completed?
|
149
|
-
return unless order.flow_io_captures_sum >= order.flow_io_total_amount && order.flow_io_balance_amount <= 0
|
150
|
-
|
151
|
-
FlowcommerceSpree::OrderUpdater.new(order: order).finalize_order
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
data/lib/simple_csv_writer.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# simple class to build scv files
|
4
|
-
|
5
|
-
# csv = CsvWriter.new
|
6
|
-
# csv.add a: 1, b: 'a', c: '"a'
|
7
|
-
# csv.add a: ',', b: 'foo, bar'
|
8
|
-
# csv.to_s
|
9
|
-
|
10
|
-
class SimpleCsvWriter
|
11
|
-
def initialize(delimiter: nil)
|
12
|
-
@data = []
|
13
|
-
@delimiter = delimiter || "\t"
|
14
|
-
end
|
15
|
-
|
16
|
-
# add hash or list
|
17
|
-
def add(data)
|
18
|
-
list = if data.class == Hash
|
19
|
-
@keys ||= data.keys
|
20
|
-
@keys.map { |key| data[key] }
|
21
|
-
else
|
22
|
-
data
|
23
|
-
end
|
24
|
-
|
25
|
-
@data.push list.map { |el| fmt(el) }.join(@delimiter)
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_s
|
29
|
-
if @keys
|
30
|
-
@keys.map(&:to_s).join(@delimiter) + "\n" +
|
31
|
-
@data.join($RS)
|
32
|
-
else
|
33
|
-
@data.join($RS)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def fmt(item)
|
40
|
-
item = item.to_s.gsub($RS, '\\n').gsub('"', '""')
|
41
|
-
|
42
|
-
item.include?(@delimiter) || item.include?('\\') ? "\"#{item}\"" : item
|
43
|
-
end
|
44
|
-
end
|