opera-mobile-store-sdk 0.1.1 → 0.1.2

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.
@@ -0,0 +1,13 @@
1
+ require 'active_support/concern'
2
+ module Opera::MobileStore
3
+
4
+ module InspectableAttributes
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ def inspect
9
+ "<#{self.class.name} #{attributes.inspect}>"
10
+ end
11
+
12
+ end
13
+ end
@@ -1,105 +1,99 @@
1
- module Opera
2
- module MobileStore
3
- class PaymentInfo
1
+ require "active_model"
4
2
 
5
- include ActiveModel::Model
3
+ module Opera::MobileStore
4
+ class PaymentInfo
6
5
 
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
6
+ include ActiveModel::Model
7
+ include ActiveModel::Serialization
20
8
 
21
- def type
22
- self.class.name.demodulize.downcase
23
- end
9
+ include Opera::MobileStore::InspectableAttributes
10
+
11
+ def type
12
+ self.class.name.demodulize.downcase
13
+ end
24
14
 
25
- def inspect
26
- attr_inspect = attributes.inject [] do |attributes, keyval|
27
- key, val = keyval
28
- attributes + ["#{key}: \"#{val}\""]
29
- end.join(", ")
15
+ def self.build_from_nokogiri_node(node)
16
+ if node.present?
17
+ type = node.xpath("string(@type)").strip.downcase
30
18
 
31
- "<#{self.class.name} #{attr_inspect}>"
19
+ case type
20
+ when "check" then Check.build_from_nokogiri_node node
21
+ when "wired" then Wired.build_from_nokogiri_node node
22
+ when "paypal" then PayPal.build_from_nokogiri_node node
23
+ when "none" then nil
24
+ else raise "WTF?"
25
+ end
32
26
  end
27
+ end
33
28
 
34
- class Check < PaymentInfo
35
- attr_accessor :name, :address
29
+ class Check < PaymentInfo
30
+ attr_accessor :name, :address
36
31
 
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
32
+ def self.build_from_nokogiri_node(node)
33
+ self.new(
34
+ name: node.xpath("string(payment_check_name)"),
35
+ address: node.xpath("string(payment_check_address)")
36
+ )
37
+ end
43
38
 
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
39
+ def attributes
40
+ [:name, :address].inject({}) do |hash, method|
41
+ value = self.public_send method
42
+ hash[method] = value unless value.nil?
43
+ hash
50
44
  end
51
45
  end
46
+ end
52
47
 
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
48
+ class Wired < PaymentInfo
49
+ attr_accessor :bank_account,
50
+ :bank_name,
51
+ :bank_address,
52
+ :bank_swiftbic,
53
+ :bank_iban,
54
+ :bank_routing_number,
55
+ :intermediary_bank_name,
56
+ :intermediary_bank_address,
57
+ :intermediary_bank_swiftbic,
58
+ :intermediary_bank_iban
78
59
 
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
60
+ def self.build_from_nokogiri_node(node)
61
+ data = [
62
+ :bank_account, :bank_name, :bank_address, :bank_swiftbic, :bank_iban,
63
+ :bank_routing_number, :intermediary_bank_name, :intermediary_bank_address,
64
+ :intermediary_bank_swiftbic, :intermediary_bank_iban
65
+ ].inject({}) do |hash, attribute_name|
66
+ value = node.xpath("string(payment_wired_#{attribute_name})").strip
67
+ hash[attribute_name] = value if value.present?
68
+ hash
89
69
  end
70
+
71
+ self.new data
90
72
  end
91
73
 
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)
74
+ def attributes
75
+ [
76
+ :bank_account, :bank_name, :bank_address, :bank_swiftbic, :bank_iban,
77
+ :bank_routing_number, :intermediary_bank_name, :intermediary_bank_address,
78
+ :intermediary_bank_swiftbic, :intermediary_bank_iban
79
+ ].inject({}) do |hash, method|
80
+ value = self.public_send method
81
+ hash[method] = value unless value.nil?
82
+ hash
96
83
  end
84
+ end
85
+ end
97
86
 
98
- def attributes
99
- { account: account }
100
- end
87
+ class PayPal < PaymentInfo
88
+ attr_accessor :account
89
+ def self.build_from_nokogiri_node(node)
90
+ self.new(account: node.xpath("string(account)").strip)
101
91
  end
102
92
 
93
+ def attributes
94
+ { account: account }
95
+ end
103
96
  end
97
+
104
98
  end
105
99
  end
@@ -1,212 +1,207 @@
1
1
  require "active_model"
2
2
 
3
- module Opera
4
- module MobileStore
5
- class Product
3
+ module Opera::MobileStore
4
+ class Product
6
5
 
7
- FIELDS = [
8
- :id, :code, :category_id, :cp_product_id, :app_type, :author_id,
9
- :support_url, :version, :requirements, :price, :adult_content, :rating,
10
- :currency, :product_type, :weight, :updated_at, :added_at, :keywords,
11
- :rating, :images, :eula, :subsites, :builds, :i18n,
12
- :released_at, # Release Date
13
- :download_count # Download count at Opera Mobile Store
14
- ].freeze
6
+ FIELDS = [
7
+ :id, :code, :category_id, :cp_product_id, :app_type, :author_id,
8
+ :support_url, :version, :requirements, :price, :adult_content, :rating,
9
+ :currency, :product_type, :weight, :updated_at, :added_at, :keywords,
10
+ :rating, :images, :eula, :subsites, :builds, :i18n,
11
+ :released_at, # Release Date
12
+ :download_count # Download count at Opera Mobile Store
13
+ ].freeze
15
14
 
16
- include ActiveModel::Model
17
- include ActiveModel::Serialization
15
+ include ActiveModel::Model
16
+ include ActiveModel::Serialization
18
17
 
19
- include Opera::MobileStoreSDK::APIAccessible
18
+ include Opera::MobileStore::InspectableAttributes
20
19
 
21
- # All attributes are Read-Only...
22
- attr_accessor *FIELDS
20
+ include Opera::MobileStoreSDK::APIAccessible
23
21
 
24
- def category
25
- Category.find category_id if category_id.present?
26
- end
27
-
28
- def category=(given_category)
29
- @category_id = given_category.id
30
- end
22
+ # All attributes are Read-Only...
23
+ attr_accessor *FIELDS
31
24
 
32
- def author
33
- Author.find author_id if author_id.present?
34
- end
25
+ def category
26
+ Category.find category_id if category_id.present?
27
+ end
35
28
 
36
- def author=(given_author)
37
- @author_id = given_author.id
38
- end
29
+ def category=(given_category)
30
+ @category_id = given_category.id
31
+ end
39
32
 
40
- def title(key = "en")
41
- required_data = i18n.detect do |x|
42
- x.language_code == key.to_s
43
- end.title
44
- required_data.present? ? required_data : title('en')
45
- end
33
+ def author
34
+ Developer.find author_id if author_id.present?
35
+ end
46
36
 
47
- def short_description(key = "en")
48
- required_data = i18n.detect do |x|
49
- x.language_code == key.to_s
50
- end.short_description
51
- required_data.present? ? required_data : short_description('en')
52
- end
37
+ def author=(given_author)
38
+ @author_id = given_author.id
39
+ end
53
40
 
54
- def long_description(key = "en")
55
- required_data = i18n.detect do |x|
56
- x.language_code == key.to_s
57
- end.long_description
58
- required_data.present? ? required_data : long_description('en')
59
- end
41
+ def title(key = "en")
42
+ required_data = i18n.detect do |x|
43
+ x.language_code == key.to_s
44
+ end.title
45
+ required_data.present? ? required_data : title('en')
46
+ end
60
47
 
61
- def available_language_codes
62
- i18n.map(&:language_code)
63
- end
48
+ def short_description(key = "en")
49
+ required_data = i18n.detect do |x|
50
+ x.language_code == key.to_s
51
+ end.short_description
52
+ required_data.present? ? required_data : short_description('en')
53
+ end
64
54
 
65
- def attributes
66
- FIELDS.inject({}) do |hsh, field_name|
67
- field_value = self.public_send field_name
68
- hsh[field_name.to_s] = field_value if field_value.present?
69
- hsh
70
- end
71
- end
55
+ def long_description(key = "en")
56
+ required_data = i18n.detect do |x|
57
+ x.language_code == key.to_s
58
+ end.long_description
59
+ required_data.present? ? required_data : long_description('en')
60
+ end
72
61
 
73
- # Override of serializable_hash:
74
- #
75
- # In the case of the category and author of a product, since we can't
76
- # query the OMS API for a single entity directly, we'll replace the entity
77
- # id field (category_id, author_id) with the serializable hash of the
78
- # actual object (category, author) as part of the serializable hash:
79
- def serializable_hash(options = nil)
80
- attributes.inject({}) do |shsh, keyval|
81
- field_name, field_value = keyval
82
-
83
- case field_name
84
- when 'category_id', 'author_id'
85
- field_name = field_name[0..-4]
86
- field_value = self.send(field_name).serializable_hash
87
- when 'i18n', 'images', 'builds' # Array of special objects
88
- field_value = field_value.map(&:serializable_hash)
89
- end
62
+ def available_language_codes
63
+ i18n.map(&:language_code)
64
+ end
90
65
 
91
- shsh[field_name] = field_value
92
- shsh
93
- end
66
+ def attributes
67
+ FIELDS.inject({}) do |hsh, field_name|
68
+ field_value = self.public_send field_name
69
+ hsh[field_name.to_s] = field_value if field_value.present?
70
+ hsh
94
71
  end
72
+ end
95
73
 
96
- def self.deserialize(serializable_hash)
97
- attributes_hash = serializable_hash.inject({}) do |hsh, keyval|
98
- field_name, field_value = keyval
99
-
100
- case field_name
101
- when 'category'
102
- field_value = Category.deserialize field_value
103
- when 'author'
104
- field_value = Author.deserialize field_value
105
- when 'i18n'
106
- field_value = field_value.map do |item_serializable_hash|
107
- ProductLocalization.deserialize item_serializable_hash
108
- end
109
- when 'images'
110
- field_value = field_value.map do |item_serializable_hash|
111
- ProductImage.deserialize item_serializable_hash
112
- end
113
- when 'builds' # Array of special objects
114
- field_value = field_value.map do |item_serializable_hash|
115
- Build.deserialize item_serializable_hash
116
- end
117
- end
118
-
119
- hsh[field_name] = field_value
120
- hsh
74
+ # Override of serializable_hash:
75
+ #
76
+ # In the case of the category and author of a product, since we can't
77
+ # query the OMS API for a single entity directly, we'll replace the entity
78
+ # id field (category_id, author_id) with the serializable hash of the
79
+ # actual object (category, author) as part of the serializable hash:
80
+ def serializable_hash(options = nil)
81
+ attributes.inject({}) do |shsh, keyval|
82
+ field_name, field_value = keyval
83
+
84
+ case field_name
85
+ when 'category_id', 'author_id'
86
+ field_name = field_name[0..-4]
87
+ field_value = self.send(field_name).serializable_hash
88
+ when 'i18n', 'images', 'builds' # Array of special objects
89
+ field_value = field_value.map(&:serializable_hash)
121
90
  end
122
91
 
123
- self.new attributes_hash
92
+ shsh[field_name] = field_value
93
+ shsh
124
94
  end
95
+ end
125
96
 
126
- # TODO: Move this implementation to the SDK namespace
127
- def self.build_from_nokogiri_node(node)
128
-
129
- category_id = node.xpath("number(category/@id)").to_i
130
- author_id = node.xpath("number(author/@id)").to_i
131
-
132
- # Initialize the category unless it's already in it's Identity Map:
133
- Category.new(
134
- id: category_id,
135
- code: node.xpath("string(category/@code)"),
136
- name: node.xpath("string(category)")
137
- ) unless Category.registered? category_id
138
-
139
- # Initialize the author unless it's already in it's Identity Map:
140
- unless Author.registered? author_id
141
- author_attributes = {
142
- id: author_id,
143
- name: node.xpath("string(author)"),
144
- email: node.xpath("string(author_email)")
145
- }
146
- author_attributes.delete(:email) unless author_attributes[:email].present?
147
-
148
- Author.new author_attributes
97
+ def self.deserialize(serializable_hash)
98
+ attributes_hash = serializable_hash.inject({}) do |hsh, keyval|
99
+ field_name, field_value = keyval
100
+
101
+ case field_name
102
+ when 'category'
103
+ field_value = Category.deserialize field_value
104
+ when 'author'
105
+ field_value = Author.deserialize field_value
106
+ when 'i18n'
107
+ field_value = field_value.map do |item_serializable_hash|
108
+ ProductLocalization.deserialize item_serializable_hash
109
+ end
110
+ when 'images'
111
+ field_value = field_value.map do |item_serializable_hash|
112
+ ProductImage.deserialize item_serializable_hash
113
+ end
114
+ when 'builds' # Array of special objects
115
+ field_value = field_value.map do |item_serializable_hash|
116
+ Build.deserialize item_serializable_hash
117
+ end
149
118
  end
150
119
 
151
- data = {
152
- id: node.xpath("number(@id)").to_i,
153
- code: node.xpath("string(@code)"),
154
- cp_product_id: node.xpath("number(cp_product_id)").to_i,
155
-
156
- category_id: category_id,
157
- author_id: author_id,
158
- app_type: node.xpath("string(apptype)"),
159
- released_at: DateTime.parse(node.xpath "string(release_date)"),
160
- download_count: node.xpath("number(downloads_count)").to_i,
161
- support_url: node.xpath("string(support_url)"),
162
- version: node.xpath("string(version)"),
163
- # TODO: process requirements node
164
-
165
- # Product localization in English:
166
- i18n: [
167
- ProductLocalization.new({
168
- language_code: "en",
169
- title: node.xpath("string(product_name)").strip,
170
- short_description: node.xpath("string(short_description)").strip,
171
- long_description: node.xpath("string(long_description)").strip
172
- }.select { |key, val| val.present? })
173
- ],
174
-
175
- price: node.xpath("number(price)"),
176
- currency: node.xpath("string(currency)"),
177
- product_type: node.xpath("string(type)"),
178
- weight: node.xpath("number(weight)").to_i,
179
- updated_at: DateTime.parse(node.xpath "string(update_date)"),
180
- added_at: DateTime.parse(node.xpath "string(add_date)"),
181
-
182
- keywords: node.xpath("keywords/keyword").map do |x|
183
- value = x.text.strip
184
- value.present? ? Opera::MobileStoreSDK.html_entities.decode(value) : nil
185
- end.compact,
186
-
187
- images: node.xpath("images/*").map do |i|
188
- ProductImage.build_from_nokogiri_node i
189
- end,
190
-
191
- builds: node.xpath("builds/build").map do |b|
192
- Build.build_from_nokogiri_node b
193
- end
120
+ hsh[field_name] = field_value
121
+ hsh
122
+ end
194
123
 
195
- }
124
+ self.new attributes_hash
125
+ end
196
126
 
197
- node.xpath("translates/language").each do |language_node|
198
- data[:i18n] << ProductLocalization.new({
199
- # We'll ignore the "@code" attribute... only Opera Devs knows WTF with it.
200
- language_code: language_node.xpath("string(@iso)").strip,
201
- title: node.xpath("string(title)").strip,
127
+ # TODO: Move this implementation to the SDK namespace
128
+ def self.build_from_nokogiri_node(node)
129
+
130
+ # Initialize the category unless it's already in it's Identity Map:
131
+ category_id = node.xpath("number(category/@id)").to_i
132
+ Category.new(
133
+ id: category_id,
134
+ code: node.xpath("string(category/@code)"),
135
+ name: node.xpath("string(category)")
136
+ ) unless Category.identity_mapped? category_id
137
+
138
+ # Initialize the category unless it's already in it's Identity Map:
139
+ author_id = node.xpath("number(author/@id)").to_i
140
+ Developer.new(
141
+ id: author_id,
142
+ name: node.xpath("string(author)"),
143
+ email: node.xpath("string(author_email)")
144
+ ) unless Developer.identity_mapped? author_id
145
+
146
+ data = {
147
+ id: node.xpath("number(@id)").to_i,
148
+ code: node.xpath("string(@code)"),
149
+ cp_product_id: node.xpath("number(cp_product_id)").to_i,
150
+
151
+ category_id: category_id,
152
+ author_id: author_id,
153
+
154
+ app_type: node.xpath("string(apptype)"),
155
+ released_at: DateTime.parse(node.xpath "string(release_date)"),
156
+ download_count: node.xpath("number(downloads_count)").to_i,
157
+ support_url: node.xpath("string(support_url)"),
158
+ version: node.xpath("string(version)"),
159
+ # TODO: process requirements node
160
+
161
+ # Product localization in English:
162
+ i18n: [
163
+ ProductLocalization.new({
164
+ language_code: "en",
165
+ title: node.xpath("string(product_name)").strip,
202
166
  short_description: node.xpath("string(short_description)").strip,
203
- long_description: node.xpath("string(description)").strip
167
+ long_description: node.xpath("string(long_description)").strip
204
168
  }.select { |key, val| val.present? })
169
+ ],
170
+
171
+ price: node.xpath("number(price)"),
172
+ currency: node.xpath("string(currency)"),
173
+ product_type: node.xpath("string(type)"),
174
+ weight: node.xpath("number(weight)").to_i,
175
+ updated_at: DateTime.parse(node.xpath "string(update_date)"),
176
+ added_at: DateTime.parse(node.xpath "string(add_date)"),
177
+
178
+ keywords: node.xpath("keywords/keyword").map do |x|
179
+ value = x.text.strip
180
+ value.present? ? Opera::MobileStoreSDK.html_entities.decode(value) : nil
181
+ end.compact,
182
+
183
+ images: node.xpath("images/*").map do |i|
184
+ ProductImage.build_from_nokogiri_node i
185
+ end,
186
+
187
+ builds: node.xpath("builds/build").map do |b|
188
+ Build.build_from_nokogiri_node b
205
189
  end
206
190
 
207
- self.new data
191
+ }
192
+
193
+ node.xpath("translates/language").each do |language_node|
194
+ data[:i18n] << ProductLocalization.new({
195
+ # We'll ignore the "@code" attribute... only Opera Devs knows WTF with it.
196
+ language_code: language_node.xpath("string(@iso)").strip,
197
+ title: node.xpath("string(title)").strip,
198
+ short_description: node.xpath("string(short_description)").strip,
199
+ long_description: node.xpath("string(description)").strip
200
+ }.select { |key, val| val.present? })
208
201
  end
209
202
 
203
+ self.new data
210
204
  end
205
+
211
206
  end
212
207
  end