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