opera-mobile-store-sdk 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/opera-mobile-store-sdk.rb +69 -0
- data/lib/opera-mobile-store-sdk/version.rb +5 -0
- data/lib/opera/mobile_store/author.rb +31 -0
- data/lib/opera/mobile_store/build.rb +88 -0
- data/lib/opera/mobile_store/build_file.rb +27 -0
- data/lib/opera/mobile_store/category.rb +31 -0
- data/lib/opera/mobile_store/developer.rb +70 -0
- data/lib/opera/mobile_store/payment_info.rb +105 -0
- data/lib/opera/mobile_store/product.rb +157 -0
- data/lib/opera/mobile_store/product_image.rb +42 -0
- data/lib/opera/mobile_store/product_localization.rb +35 -0
- data/lib/opera/mobile_store_sdk/api_accessible.rb +44 -0
- data/lib/opera/mobile_store_sdk/api_object_list.rb +174 -0
- data/lib/opera/mobile_store_sdk/config.rb +71 -0
- data/lib/opera/mobile_store_sdk/errors.rb +10 -0
- data/lib/opera/mobile_store_sdk/faraday_middleware.rb +4 -0
- data/lib/opera/mobile_store_sdk/faraday_middleware/authentication.rb +76 -0
- data/lib/opera/mobile_store_sdk/faraday_middleware/required_response_format.rb +14 -0
- data/lib/opera/mobile_store_sdk/faraday_middleware/response_parser.rb +45 -0
- data/lib/opera/mobile_store_sdk/faraday_middleware/sdk_benchmark.rb +47 -0
- data/lib/opera/mobile_store_sdk/identity_mapable.rb +35 -0
- data/opera-mobile-store-sdk.gemspec +35 -0
- metadata +174 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
module Opera
|
2
|
+
module MobileStore
|
3
|
+
class PaymentInfo
|
4
|
+
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
def self.build_from_nokogiri_node(node)
|
8
|
+
if node.present?
|
9
|
+
type = node.xpath("string(@type)").strip.downcase
|
10
|
+
|
11
|
+
case type
|
12
|
+
when "check" then Check.build_from_nokogiri_node node
|
13
|
+
when "wired" then Wired.build_from_nokogiri_node node
|
14
|
+
when "paypal" then PayPal.build_from_nokogiri_node node
|
15
|
+
when "none" then nil
|
16
|
+
else raise "WTF?"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def type
|
22
|
+
self.class.name.demodulize.downcase
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
attr_inspect = attributes.inject [] do |attributes, keyval|
|
27
|
+
key, val = keyval
|
28
|
+
attributes + ["#{key}: \"#{val}\""]
|
29
|
+
end.join(", ")
|
30
|
+
|
31
|
+
"<#{self.class.name} #{attr_inspect}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
class Check < PaymentInfo
|
35
|
+
attr_accessor :name, :address
|
36
|
+
|
37
|
+
def self.build_from_nokogiri_node(node)
|
38
|
+
self.new(
|
39
|
+
name: node.xpath("string(payment_check_name)"),
|
40
|
+
address: node.xpath("string(payment_check_address)")
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def attributes
|
45
|
+
[:name, :address].inject({}) do |hash, method|
|
46
|
+
value = self.public_send method
|
47
|
+
hash[method] = value unless value.nil?
|
48
|
+
hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Wired < PaymentInfo
|
54
|
+
attr_accessor :bank_account,
|
55
|
+
:bank_name,
|
56
|
+
:bank_address,
|
57
|
+
:bank_swiftbic,
|
58
|
+
:bank_iban,
|
59
|
+
:bank_routing_number,
|
60
|
+
:intermediary_bank_name,
|
61
|
+
:intermediary_bank_address,
|
62
|
+
:intermediary_bank_swiftbic,
|
63
|
+
:intermediary_bank_iban
|
64
|
+
|
65
|
+
def self.build_from_nokogiri_node(node)
|
66
|
+
data = [
|
67
|
+
:bank_account, :bank_name, :bank_address, :bank_swiftbic, :bank_iban,
|
68
|
+
:bank_routing_number, :intermediary_bank_name, :intermediary_bank_address,
|
69
|
+
:intermediary_bank_swiftbic, :intermediary_bank_iban
|
70
|
+
].inject({}) do |hash, attribute_name|
|
71
|
+
value = node.xpath("string(payment_wired_#{attribute_name})").strip
|
72
|
+
hash[attribute_name] = value if value.present?
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
|
76
|
+
self.new data
|
77
|
+
end
|
78
|
+
|
79
|
+
def attributes
|
80
|
+
[
|
81
|
+
:bank_account, :bank_name, :bank_address, :bank_swiftbic, :bank_iban,
|
82
|
+
:bank_routing_number, :intermediary_bank_name, :intermediary_bank_address,
|
83
|
+
:intermediary_bank_swiftbic, :intermediary_bank_iban
|
84
|
+
].inject({}) do |hash, method|
|
85
|
+
value = self.public_send method
|
86
|
+
hash[method] = value unless value.nil?
|
87
|
+
hash
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class PayPal < PaymentInfo
|
93
|
+
attr_accessor :account
|
94
|
+
def self.build_from_nokogiri_node(node)
|
95
|
+
self.new(account: node.xpath("string(account)").strip)
|
96
|
+
end
|
97
|
+
|
98
|
+
def attributes
|
99
|
+
{ account: account }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require "active_model"
|
2
|
+
|
3
|
+
module Opera
|
4
|
+
module MobileStore
|
5
|
+
class Product
|
6
|
+
|
7
|
+
include ActiveModel::Model
|
8
|
+
|
9
|
+
include Opera::MobileStoreSDK::APIAccessible
|
10
|
+
|
11
|
+
# All attributes are Read-Only...
|
12
|
+
attr_accessor :id, :code, :category_id, :cp_product_id,
|
13
|
+
:app_type,
|
14
|
+
:released_at, # Release date
|
15
|
+
:download_count, # Download count at Opera Mobile Store
|
16
|
+
:author_id,
|
17
|
+
:support_url,
|
18
|
+
:version,
|
19
|
+
:requirements,
|
20
|
+
:price,
|
21
|
+
:adult_content,
|
22
|
+
:rating,
|
23
|
+
:currency,
|
24
|
+
:product_type,
|
25
|
+
:weight,
|
26
|
+
:updated_at,
|
27
|
+
:added_at,
|
28
|
+
:keywords,
|
29
|
+
:rating,
|
30
|
+
:images,
|
31
|
+
:eula,
|
32
|
+
:subsites,
|
33
|
+
:builds,
|
34
|
+
:i18n
|
35
|
+
|
36
|
+
def category
|
37
|
+
Category.find category_id if category_id.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def author
|
41
|
+
Author.find author_id if author_id.present?
|
42
|
+
end
|
43
|
+
|
44
|
+
def title(key = "en")
|
45
|
+
required_data = i18n.detect do |x|
|
46
|
+
x.language_iso_code == key.to_s
|
47
|
+
end.title
|
48
|
+
required_data.present? ? required_data : title('en')
|
49
|
+
end
|
50
|
+
|
51
|
+
def short_description(key = "en")
|
52
|
+
required_data = i18n.detect do |x|
|
53
|
+
x.language_iso_code == key.to_s
|
54
|
+
end.short_description
|
55
|
+
required_data.present? ? required_data : short_description('en')
|
56
|
+
end
|
57
|
+
|
58
|
+
def long_description(key = "en")
|
59
|
+
required_data = i18n.detect do |x|
|
60
|
+
x.language_iso_code == key.to_s
|
61
|
+
end.long_description
|
62
|
+
required_data.present? ? required_data : long_description('en')
|
63
|
+
end
|
64
|
+
|
65
|
+
def available_language_codes
|
66
|
+
i18n.map(&:language_iso_code)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.build_from_nokogiri_node(node)
|
70
|
+
|
71
|
+
category_id = node.xpath("number(category/@id)").to_i
|
72
|
+
author_id = node.xpath("number(author/@id)").to_i
|
73
|
+
|
74
|
+
# Register the category unless it's already registered in the ID Map:
|
75
|
+
Category.register(
|
76
|
+
id: category_id,
|
77
|
+
code: node.xpath("string(category/@code)"),
|
78
|
+
name: node.xpath("string(category)")
|
79
|
+
) unless Category.registered? category_id
|
80
|
+
|
81
|
+
# Register the author unless it's already registered in the ID Map:
|
82
|
+
unless Author.registered? author_id
|
83
|
+
author_attributes = {
|
84
|
+
id: author_id,
|
85
|
+
name: node.xpath("string(author)"),
|
86
|
+
email: node.xpath("string(author_email)")
|
87
|
+
}
|
88
|
+
author_attributes.delete(:email) unless author_attributes[:email].present?
|
89
|
+
|
90
|
+
Author.register author_attributes
|
91
|
+
end
|
92
|
+
|
93
|
+
data = {
|
94
|
+
id: node.xpath("number(@id)").to_i,
|
95
|
+
code: node.xpath("string(@code)"),
|
96
|
+
cp_product_id: node.xpath("number(cp_product_id)").to_i,
|
97
|
+
|
98
|
+
category_id: category_id,
|
99
|
+
author_id: author_id,
|
100
|
+
app_type: node.xpath("string(apptype)"),
|
101
|
+
released_at: Time.parse(node.xpath "string(release_date)"),
|
102
|
+
download_count: node.xpath("number(downloads_count)").to_i,
|
103
|
+
support_url: node.xpath("string(support_url)"),
|
104
|
+
version: node.xpath("string(version)"),
|
105
|
+
# TODO: process requirements node
|
106
|
+
|
107
|
+
# Product localization in English:
|
108
|
+
i18n: [
|
109
|
+
ProductLocalization.new(
|
110
|
+
language_code: "en",
|
111
|
+
language_iso_code: "en",
|
112
|
+
language_name: "English",
|
113
|
+
title: node.xpath("string(product_name)").strip,
|
114
|
+
short_description: node.xpath("string(short_description)").strip,
|
115
|
+
long_description: node.xpath("string(long_description)").strip
|
116
|
+
)
|
117
|
+
],
|
118
|
+
|
119
|
+
price: node.xpath("number(price)"),
|
120
|
+
currency: node.xpath("string(currency)"),
|
121
|
+
product_type: node.xpath("string(type)"),
|
122
|
+
weight: node.xpath("number(weight)").to_i,
|
123
|
+
updated_at: Time.parse(node.xpath "string(update_date)"),
|
124
|
+
added_at: Time.parse(node.xpath "string(add_date)"),
|
125
|
+
|
126
|
+
keywords: node.xpath("keywords/keyword").map do |x|
|
127
|
+
value = x.text.strip
|
128
|
+
value.present? ? Opera::MobileStoreSDK.html_entities.decode(value) : nil
|
129
|
+
end.compact,
|
130
|
+
|
131
|
+
images: node.xpath("images/*").map do |i|
|
132
|
+
ProductImage.build_from_nokogiri_node i
|
133
|
+
end,
|
134
|
+
|
135
|
+
builds: node.xpath("builds/build").map do |b|
|
136
|
+
Build.build_from_nokogiri_node b
|
137
|
+
end,
|
138
|
+
|
139
|
+
}
|
140
|
+
|
141
|
+
node.xpath("translates/language").each do |language_node|
|
142
|
+
data[:i18n] << ProductLocalization.new(
|
143
|
+
language_code: language_node.xpath("string(@code)").strip,
|
144
|
+
language_iso_code: language_node.xpath("string(@iso)").strip,
|
145
|
+
language_name: language_node.xpath("string(@language)").strip,
|
146
|
+
title: node.xpath("string(title)").strip,
|
147
|
+
short_description: node.xpath("string(short_description)").strip,
|
148
|
+
long_description: node.xpath("string(description)").strip
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
self.new data
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Opera
|
2
|
+
module MobileStore
|
3
|
+
class ProductImage
|
4
|
+
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
# All attributes are Read-Only...
|
8
|
+
attr_accessor :type, :width, :height, :url
|
9
|
+
|
10
|
+
def self.build_from_nokogiri_node(node)
|
11
|
+
|
12
|
+
data = {
|
13
|
+
type: node.name,
|
14
|
+
url: node.text.strip
|
15
|
+
}
|
16
|
+
|
17
|
+
# Extract width + height data:
|
18
|
+
width = node.xpath("string(@width)")
|
19
|
+
height = node.xpath("string(@height)")
|
20
|
+
data[:width] = width.to_i if width.present?
|
21
|
+
data[:height] = width.to_i if height.present?
|
22
|
+
|
23
|
+
self.new data
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
info = { type: type }
|
28
|
+
info[:width] = width if width.present?
|
29
|
+
info[:height] = height if height.present?
|
30
|
+
info[:url] = url
|
31
|
+
|
32
|
+
info = info.inject [] do |attributes, keyval|
|
33
|
+
key, val = keyval
|
34
|
+
attributes + ["#{key}:#{val}"]
|
35
|
+
end
|
36
|
+
|
37
|
+
"<Opera::MobileStore::ProductImage " + info.join(', ') + ">"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Opera
|
2
|
+
module MobileStore
|
3
|
+
class ProductLocalization
|
4
|
+
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
# All attributes are Read-Only...
|
8
|
+
attr_accessor :language_code,
|
9
|
+
:language_iso_code,
|
10
|
+
:language_name,
|
11
|
+
:title,
|
12
|
+
:short_description,
|
13
|
+
:long_description
|
14
|
+
|
15
|
+
def attributes
|
16
|
+
[:language_code, :language_iso_code, :language_name,
|
17
|
+
:title, :short_description, :long_description].inject({}) do |hash, method|
|
18
|
+
value = self.public_send method
|
19
|
+
hash[method] = value unless value.nil?
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
attr_inspect = attributes.inject [] do |attributes, keyval|
|
26
|
+
key, val = keyval
|
27
|
+
attributes + ["#{key}:#{val}"]
|
28
|
+
end.join(", ")
|
29
|
+
|
30
|
+
"<#{self.class.name} #{attr_inspect}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module Opera
|
4
|
+
module MobileStoreSDK
|
5
|
+
module APIAccessible
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
def where(given_options = {})
|
16
|
+
APIObjectList.new(name).where given_options
|
17
|
+
end
|
18
|
+
|
19
|
+
def per(given_item_count)
|
20
|
+
APIObjectList.new(name).per given_item_count
|
21
|
+
end
|
22
|
+
|
23
|
+
def page(given_page)
|
24
|
+
APIObjectList.new(name).page given_page
|
25
|
+
end
|
26
|
+
|
27
|
+
def all
|
28
|
+
APIObjectList.new(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def includes(*included_fields)
|
32
|
+
params = included_fields.map(&:to_s).map(&:downcase)
|
33
|
+
.select { |x| %w(original_images billing eula adult subsites compatibility profit rating).include? x }
|
34
|
+
.map { |x| x == "billing" ? "show_billing" : x }
|
35
|
+
.inject({}) { |hash, param_name| hash[param_name] = "1"; hash }
|
36
|
+
|
37
|
+
APIObjectList.new name, params
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "opera/mobile_store_sdk/faraday_middleware"
|
3
|
+
|
4
|
+
module Opera
|
5
|
+
module MobileStoreSDK
|
6
|
+
|
7
|
+
# Our version of Array... which will request each page upon iteration...
|
8
|
+
# see http://stackoverflow.com/questions/1571349/can-the-array-be-reinvented-in-ruby
|
9
|
+
class APIObjectList
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
def initialize(klass, options = {})
|
16
|
+
@klass = klass
|
17
|
+
@options = options.with_indifferent_access
|
18
|
+
end
|
19
|
+
|
20
|
+
def length
|
21
|
+
response.body.count
|
22
|
+
end
|
23
|
+
|
24
|
+
def found_rows
|
25
|
+
response.env[:found_rows]
|
26
|
+
end
|
27
|
+
alias_method :total_count, :found_rows
|
28
|
+
|
29
|
+
def sdk_tms
|
30
|
+
{
|
31
|
+
api_calling: response.env[:opera_api_calling_tms],
|
32
|
+
api_response_parsing: response.env[:opera_api_response_parsing_tms],
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_call_duration
|
37
|
+
sdk_tms[:api_calling].real
|
38
|
+
end
|
39
|
+
|
40
|
+
def api_response_parsing_duration
|
41
|
+
sdk_tms[:api_response_parsing].real
|
42
|
+
end
|
43
|
+
|
44
|
+
def api_response_datetime
|
45
|
+
response.env[:opera_api_response_datetime]
|
46
|
+
end
|
47
|
+
|
48
|
+
def timestamp
|
49
|
+
response.env[:timestamp]
|
50
|
+
end
|
51
|
+
|
52
|
+
def updated_at
|
53
|
+
Time.at(page.timestamp).to_datetime
|
54
|
+
end
|
55
|
+
|
56
|
+
def [](n)
|
57
|
+
response.body[n]
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
0.upto(length - 1) { |idx| yield self[idx] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_a
|
65
|
+
response.body
|
66
|
+
end
|
67
|
+
|
68
|
+
# Chainable Methods ------------------------------------------------------
|
69
|
+
def where(given_options = {})
|
70
|
+
|
71
|
+
given_options = given_options.with_indifferent_access
|
72
|
+
|
73
|
+
########################################################################
|
74
|
+
# Validate (& filter out conflicting) options:
|
75
|
+
|
76
|
+
# Product list API can use added_after/updated_after parameters:
|
77
|
+
|
78
|
+
if given_options.include? :updated_after
|
79
|
+
if given_options[:updated_after].is_a? String
|
80
|
+
begin
|
81
|
+
given_options[:updated_after] = DateTime.parse(given_options[:updated_after])
|
82
|
+
rescue ArgumentError => e
|
83
|
+
raise Opera::MobileStoreSDK::Errors::APIParamsError,
|
84
|
+
"Parameter 'updated_after' is not a date (#{given_options[:updated_after]})"
|
85
|
+
end
|
86
|
+
elsif !given_options[:updated_after].respond_to? :to_date
|
87
|
+
raise Opera::MobileStoreSDK::Errors::APIParamsError,
|
88
|
+
"Parameter 'updated_after' is not a date (#{given_options[:updated_after]})"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if given_options.key? :added_after
|
93
|
+
if given_options[:added_after].is_a? String
|
94
|
+
begin
|
95
|
+
given_options[:added_after] = DateTime.parse(given_options[:added_after])
|
96
|
+
rescue ArgumentError => e
|
97
|
+
raise Opera::MobileStoreSDK::Errors::APIParamsError,
|
98
|
+
"Parameter 'added_after' is not a date (#{given_options[:added_after]})"
|
99
|
+
end
|
100
|
+
elsif !given_options[:added_after].respond_to? :to_date
|
101
|
+
raise Opera::MobileStoreSDK::Errors::APIParamsError,
|
102
|
+
"Parameter 'added_after' is not a date (#{given_options[:added_after]})"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Delete conflicting parameters:
|
106
|
+
given_options.delete :updated_after
|
107
|
+
end
|
108
|
+
########################################################################
|
109
|
+
|
110
|
+
self.class.new @klass, @options.merge(given_options)
|
111
|
+
end
|
112
|
+
|
113
|
+
def page(given_page)
|
114
|
+
where @options.merge(page: given_page)
|
115
|
+
end
|
116
|
+
|
117
|
+
def offset(given_offset)
|
118
|
+
where @options.merge(offset: given_offset)
|
119
|
+
end
|
120
|
+
|
121
|
+
def per(given_item_count)
|
122
|
+
where @options.merge(items: given_item_count)
|
123
|
+
end
|
124
|
+
|
125
|
+
def includes(included_fields = [])
|
126
|
+
params = included_fields.map(&:to_s).map(&:downcase)
|
127
|
+
.select { |x| %w(original_images billing eula adult subsites compatibility profit rating).include? x }
|
128
|
+
.map { |x| x == "billing" ? "show_billing" : x }
|
129
|
+
.inject({}) { |hash, param_name| hash[param_name] = "1"; hash }
|
130
|
+
|
131
|
+
self.class.new @klass, @options.merge(params)
|
132
|
+
end
|
133
|
+
# ------------------------------------------------------------------------
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
def response
|
138
|
+
|
139
|
+
converted_options = @options.inject({}) do |hsh, keyval|
|
140
|
+
key, val = keyval
|
141
|
+
|
142
|
+
hsh[key.to_s] = case key.to_sym
|
143
|
+
when :added_after, :updated_after then
|
144
|
+
# byebug
|
145
|
+
val.to_date.to_s
|
146
|
+
else val.to_s
|
147
|
+
end
|
148
|
+
|
149
|
+
hsh
|
150
|
+
end
|
151
|
+
|
152
|
+
# puts "Options: #{@options.inspect}"
|
153
|
+
# byebug
|
154
|
+
|
155
|
+
@_response ||= Opera::MobileStoreSDK.connection.get do |request|
|
156
|
+
request.url MobileStoreSDK.config.api_path, {
|
157
|
+
action: action_param
|
158
|
+
}.merge(converted_options)
|
159
|
+
|
160
|
+
request.options.timeout = MobileStoreSDK.config.api_call_timeout
|
161
|
+
request.options.open_timeout = MobileStoreSDK.config.api_call_open_timeout
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def action_param
|
166
|
+
@_action_param ||= case @klass
|
167
|
+
when "Opera::MobileStore::Developer" then "full_developers"
|
168
|
+
else @klass.demodulize.underscore.pluralize
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|