opera-mobile-store-sdk 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5ef23d96109139c5a09b57d6fb5e31bcc35baf6d
4
- data.tar.gz: ee58fda10f20bc9895b7e9eea0d30209e4267ab6
3
+ metadata.gz: 74b3499c0b6013c04854c3bf42825ffa4193c701
4
+ data.tar.gz: 6698b91cbf6ad11d45cea44ac0461579d00e3c3e
5
5
  SHA512:
6
- metadata.gz: 983993cc0dd055f20366267a5b2ba093e8ae78e89bc7599f3f749a25e7eeca4a25006f33c88a35d71ea1cdcfa8b1caa0f000317a5d75cb536ace0e9a8b73e27e
7
- data.tar.gz: e627c31ccb5af62e9fe5a29217d379fc558d1176e3ad3e02bf4e2ebe374180ebab26bb660a9d01d5f3e5a465494d68112ae505d2e00b9ef5d8d3907f0a2e72af
6
+ metadata.gz: 70b6f179a33e1f413b1dc95d0a3aca5337f0877ed5221c1d0ac364c4e0b32a80694e25dab4b9b9f3214330bba1139ff41f3536c89db35bf69f06d20c5d51ad62
7
+ data.tar.gz: 81f2d3db5b3890fcae6f4f698fd5b0cccd820963e60f7e1eeb093ee18a798d7a9a51a2a16b685d20b852da82af3e620e840dc3a74244ef39cfbc85bf5a77f5a9
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  .ruby-version
11
+ /opera-mobile-store-sdk-*.gem
@@ -1,5 +1,5 @@
1
1
  module Opera
2
2
  module MobileStoreSDK
3
- VERSION = "0.1.0" #
3
+ VERSION = "0.1.1" #
4
4
  end
5
5
  end
@@ -3,6 +3,7 @@ module Opera
3
3
  class Author
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  include Opera::MobileStoreSDK::IdentityMapable
8
9
 
@@ -10,22 +11,19 @@ module Opera
10
11
  attr_accessor :id, :name, :email
11
12
 
12
13
  def attributes
13
- [:id, :name, :email].inject({}) do |hash, method|
14
- value = self.public_send method
15
- hash[method] = value unless value.nil?
14
+ [:id, :name, :email].inject({}) do |hash, field_name|
15
+ value = self.public_send field_name
16
+ hash[field_name.to_s] = value unless value.nil?
16
17
  hash
17
18
  end
18
19
  end
19
20
 
20
21
  def inspect
21
- attr_inspect = attributes.inject [] do |attributes, keyval|
22
- key, val = keyval
23
- attributes + ["#{key}:#{val}"]
24
- end.join(", ")
25
-
26
- "<#{self.class.name} #{attr_inspect}>"
22
+ "<#{self.class.name} #{attributes.inspect}>"
27
23
  end
28
24
 
25
+ self.singleton_class.send :alias_method, :deserialize, :new
26
+
29
27
  end
30
28
  end
31
29
  end
@@ -3,6 +3,7 @@ module Opera
3
3
  class Build
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  # include Opera::MobileStoreSDK::IdentityMapable
8
9
 
@@ -32,6 +33,34 @@ module Opera
32
33
  @billing ||= false
33
34
  end
34
35
 
36
+ def attributes
37
+ [
38
+ :id, :name, :platform, :type, :installer_type, :package_name,
39
+ :version_name, :version_code, :build_update_date, :files, :billing,
40
+ :languages
41
+ ].inject({}) do |hash, field_name|
42
+ field_value = self.public_send field_name
43
+ hash[field_name.to_s] = field_value if field_value.present?
44
+ hash
45
+ end
46
+ end
47
+
48
+ # Override of serializable_hash:
49
+ #
50
+ # This override will prevent dumping special objects to the hash:
51
+ def serializable_hash(options = nil)
52
+ attributes.inject({}) do |shsh, keyval|
53
+ field_name, field_value = keyval
54
+
55
+ if field_name == 'files' # Array of special objects
56
+ field_value = field_value.map(&:serializable_hash)
57
+ end
58
+
59
+ shsh[field_name] = field_value
60
+ shsh
61
+ end
62
+ end
63
+
35
64
  def self.build_from_nokogiri_node(node)
36
65
 
37
66
  version_code = node.xpath("string(version_code)")
@@ -66,22 +95,34 @@ module Opera
66
95
  :id, :name, :platform, :type, :installer_type, :package_name,
67
96
  :version_name, :version_code, :build_update_date, :files, :billing,
68
97
  :languages
69
- ].inject({}) do |hash, method|
70
- value = self.public_send method
71
- hash[method] = value unless value.nil?
98
+ ].inject({}) do |hash, field_name|
99
+ field_value = self.public_send field_name
100
+ hash[field_name.to_s] = field_value if field_value.present?
72
101
  hash
73
102
  end
74
103
  end
75
104
 
76
105
  def inspect
77
- attr_inspect = attributes.inject [] do |attributes, keyval|
78
- key, val = keyval
79
- attributes + ["#{key}:#{val}"]
80
- end.join(", ")
81
-
82
- "<#{self.class.name} #{attr_inspect}>"
106
+ "<#{self.class.name} #{attributes.inspect}>"
83
107
  end
84
108
 
109
+ def self.deserialize(serializable_hash)
110
+ attributes_hash = serializable_hash.inject({}) do |hsh, keyval|
111
+ field_name, field_value = keyval
112
+
113
+ case field_name
114
+ when 'files'
115
+ field_value = field_value.map do |item_serializable_hash|
116
+ BuildFile.deserialize item_serializable_hash
117
+ end
118
+ end
119
+
120
+ hsh[field_name] = field_value
121
+ hsh
122
+ end
123
+
124
+ self.new attributes_hash
125
+ end
85
126
 
86
127
  end
87
128
  end
@@ -3,10 +3,19 @@ module Opera
3
3
  class BuildFile
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  # All attributes are Read-Only...
8
9
  attr_accessor :size, :url
9
10
 
11
+ def attributes
12
+ [:size, :url].inject({}) do |hash, field_name|
13
+ field_value = self.public_send field_name
14
+ hash[field_name.to_s] = field_value if field_value.present?
15
+ hash
16
+ end
17
+ end
18
+
10
19
  def self.build_from_nokogiri_node(node)
11
20
 
12
21
  data = { url: node.text.strip }
@@ -19,9 +28,11 @@ module Opera
19
28
  end
20
29
 
21
30
  def inspect
22
- "<#{self.class.name} size:#{size}, url:#{url}>"
31
+ "<#{self.class.name} #{attributes.inspect}>"
23
32
  end
24
33
 
34
+ self.singleton_class.send :alias_method, :deserialize, :new
35
+
25
36
  end
26
37
  end
27
38
  end
@@ -3,6 +3,7 @@ module Opera
3
3
  class Category
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  include Opera::MobileStoreSDK::IdentityMapable
8
9
 
@@ -10,22 +11,19 @@ module Opera
10
11
  attr_accessor :id, :code, :name
11
12
 
12
13
  def attributes
13
- [:id, :code, :name].inject({}) do |hash, method|
14
- value = self.public_send method
15
- hash[method] = value unless value.nil?
14
+ [:id, :code, :name].inject({}) do |hash, field_name|
15
+ field_value = self.public_send field_name
16
+ hash[field_name.to_s] = field_value unless field_value.nil?
16
17
  hash
17
18
  end
18
19
  end
19
20
 
20
21
  def inspect
21
- attr_inspect = attributes.inject [] do |attributes, keyval|
22
- key, val = keyval
23
- attributes + ["#{key}:#{val}"]
24
- end.join(", ")
25
-
26
- "<#{self.class.name} #{attr_inspect}>"
22
+ "<#{self.class.name} #{attributes.inspect}>"
27
23
  end
28
24
 
25
+ self.singleton_class.send :alias_method, :deserialize, :new
26
+
29
27
  end
30
28
  end
31
29
  end
@@ -4,81 +4,139 @@ module Opera
4
4
  module MobileStore
5
5
  class Product
6
6
 
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
15
+
7
16
  include ActiveModel::Model
17
+ include ActiveModel::Serialization
8
18
 
9
19
  include Opera::MobileStoreSDK::APIAccessible
10
20
 
11
21
  # 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
22
+ attr_accessor *FIELDS
35
23
 
36
24
  def category
37
25
  Category.find category_id if category_id.present?
38
26
  end
39
27
 
28
+ def category=(given_category)
29
+ @category_id = given_category.id
30
+ end
31
+
40
32
  def author
41
33
  Author.find author_id if author_id.present?
42
34
  end
43
35
 
36
+ def author=(given_author)
37
+ @author_id = given_author.id
38
+ end
39
+
44
40
  def title(key = "en")
45
41
  required_data = i18n.detect do |x|
46
- x.language_iso_code == key.to_s
42
+ x.language_code == key.to_s
47
43
  end.title
48
44
  required_data.present? ? required_data : title('en')
49
45
  end
50
46
 
51
47
  def short_description(key = "en")
52
48
  required_data = i18n.detect do |x|
53
- x.language_iso_code == key.to_s
49
+ x.language_code == key.to_s
54
50
  end.short_description
55
51
  required_data.present? ? required_data : short_description('en')
56
52
  end
57
53
 
58
54
  def long_description(key = "en")
59
55
  required_data = i18n.detect do |x|
60
- x.language_iso_code == key.to_s
56
+ x.language_code == key.to_s
61
57
  end.long_description
62
58
  required_data.present? ? required_data : long_description('en')
63
59
  end
64
60
 
65
61
  def available_language_codes
66
- i18n.map(&:language_iso_code)
62
+ i18n.map(&:language_code)
63
+ end
64
+
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
67
71
  end
68
72
 
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
90
+
91
+ shsh[field_name] = field_value
92
+ shsh
93
+ end
94
+ end
95
+
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
121
+ end
122
+
123
+ self.new attributes_hash
124
+ end
125
+
126
+ # TODO: Move this implementation to the SDK namespace
69
127
  def self.build_from_nokogiri_node(node)
70
128
 
71
129
  category_id = node.xpath("number(category/@id)").to_i
72
130
  author_id = node.xpath("number(author/@id)").to_i
73
131
 
74
- # Register the category unless it's already registered in the ID Map:
75
- Category.register(
132
+ # Initialize the category unless it's already in it's Identity Map:
133
+ Category.new(
76
134
  id: category_id,
77
135
  code: node.xpath("string(category/@code)"),
78
136
  name: node.xpath("string(category)")
79
137
  ) unless Category.registered? category_id
80
138
 
81
- # Register the author unless it's already registered in the ID Map:
139
+ # Initialize the author unless it's already in it's Identity Map:
82
140
  unless Author.registered? author_id
83
141
  author_attributes = {
84
142
  id: author_id,
@@ -87,7 +145,7 @@ module Opera
87
145
  }
88
146
  author_attributes.delete(:email) unless author_attributes[:email].present?
89
147
 
90
- Author.register author_attributes
148
+ Author.new author_attributes
91
149
  end
92
150
 
93
151
  data = {
@@ -98,7 +156,7 @@ module Opera
98
156
  category_id: category_id,
99
157
  author_id: author_id,
100
158
  app_type: node.xpath("string(apptype)"),
101
- released_at: Time.parse(node.xpath "string(release_date)"),
159
+ released_at: DateTime.parse(node.xpath "string(release_date)"),
102
160
  download_count: node.xpath("number(downloads_count)").to_i,
103
161
  support_url: node.xpath("string(support_url)"),
104
162
  version: node.xpath("string(version)"),
@@ -106,22 +164,20 @@ module Opera
106
164
 
107
165
  # Product localization in English:
108
166
  i18n: [
109
- ProductLocalization.new(
167
+ ProductLocalization.new({
110
168
  language_code: "en",
111
- language_iso_code: "en",
112
- language_name: "English",
113
169
  title: node.xpath("string(product_name)").strip,
114
170
  short_description: node.xpath("string(short_description)").strip,
115
171
  long_description: node.xpath("string(long_description)").strip
116
- )
172
+ }.select { |key, val| val.present? })
117
173
  ],
118
174
 
119
175
  price: node.xpath("number(price)"),
120
176
  currency: node.xpath("string(currency)"),
121
177
  product_type: node.xpath("string(type)"),
122
178
  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)"),
179
+ updated_at: DateTime.parse(node.xpath "string(update_date)"),
180
+ added_at: DateTime.parse(node.xpath "string(add_date)"),
125
181
 
126
182
  keywords: node.xpath("keywords/keyword").map do |x|
127
183
  value = x.text.strip
@@ -134,19 +190,18 @@ module Opera
134
190
 
135
191
  builds: node.xpath("builds/build").map do |b|
136
192
  Build.build_from_nokogiri_node b
137
- end,
193
+ end
138
194
 
139
195
  }
140
196
 
141
197
  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,
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,
146
201
  title: node.xpath("string(title)").strip,
147
202
  short_description: node.xpath("string(short_description)").strip,
148
203
  long_description: node.xpath("string(description)").strip
149
- )
204
+ }.select { |key, val| val.present? })
150
205
  end
151
206
 
152
207
  self.new data
@@ -3,10 +3,19 @@ module Opera
3
3
  class ProductImage
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  # All attributes are Read-Only...
8
9
  attr_accessor :type, :width, :height, :url
9
10
 
11
+ def attributes
12
+ [:type, :width, :height, :url].inject({}) do |hash, field_name|
13
+ field_value = self.public_send field_name
14
+ hash[field_name.to_s] = field_value if field_value.present?
15
+ hash
16
+ end
17
+ end
18
+
10
19
  def self.build_from_nokogiri_node(node)
11
20
 
12
21
  data = {
@@ -24,19 +33,11 @@ module Opera
24
33
  end
25
34
 
26
35
  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(', ') + ">"
36
+ "<#{self.class.name} #{attributes.inspect}>"
38
37
  end
39
38
 
39
+ self.singleton_class.send :alias_method, :deserialize, :new
40
+
40
41
  end
41
42
  end
42
43
  end
@@ -3,33 +3,30 @@ module Opera
3
3
  class ProductLocalization
4
4
 
5
5
  include ActiveModel::Model
6
+ include ActiveModel::Serialization
6
7
 
7
8
  # All attributes are Read-Only...
8
9
  attr_accessor :language_code,
9
- :language_iso_code,
10
- :language_name,
11
10
  :title,
12
11
  :short_description,
13
12
  :long_description
14
13
 
15
14
  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?
15
+ [
16
+ :language_code, :title, :short_description, :long_description
17
+ ].inject({}) do |hash, field_name|
18
+ field_value = self.public_send field_name
19
+ hash[field_name.to_s] = field_value if field_value.present?
20
20
  hash
21
21
  end
22
22
  end
23
23
 
24
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}>"
25
+ "<#{self.class.name} #{attributes.inspect}>"
31
26
  end
32
27
 
28
+ self.singleton_class.send :alias_method, :deserialize, :new
29
+
33
30
  end
34
31
  end
35
32
  end
@@ -4,6 +4,14 @@ module Opera
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ # Registers itself in the identity map:
8
+ def initialize(params={})
9
+ super
10
+
11
+ # Register in the identity map:
12
+ self.class.identity_map[self.id] = self
13
+ end
14
+
7
15
  module ClassMethods
8
16
 
9
17
  def identity_map
@@ -22,12 +30,6 @@ module Opera
22
30
  identity_map.key? object_id
23
31
  end
24
32
 
25
- def register(given_attributes)
26
- obj = self.new given_attributes
27
- identity_map[obj.id] = obj
28
- identity_map[obj.id]
29
- end
30
-
31
33
  end
32
34
 
33
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opera-mobile-store-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roberto Quintanilla
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-21 00:00:00.000000000 Z
11
+ date: 2015-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport