opera-mobile-store-sdk 0.1.0 → 0.1.1

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