wikidatum 0.1.0 → 0.2.0

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
  SHA256:
3
- metadata.gz: d5aa9e8eb9fccc24e9c423dcba185ef7550ad465426f90c57128968e06e99d51
4
- data.tar.gz: 19fc905557859d97d1e604155d08d56d416eb332d6f285246c5354bdc1f024a9
3
+ metadata.gz: c8f1ec02cd83bf3ea3e432868a8283474f810bfd55740b5759c820d880f95e31
4
+ data.tar.gz: a5af62f917570a29ceceb6e420377154775d9820dec8abb02eb84e037f0bab89
5
5
  SHA512:
6
- metadata.gz: 6ee9f49ee6689d953f308a9e2f4deaad71226080f70bf6fe3d7f375ac86bbd887e3494d50eed06b96308e6965165c86596fc7dae6abbdf2367e5f4302e49d73a
7
- data.tar.gz: 4147886d03d2f17e2743d0b9309a22e278cbe2e0febb95acc77dbdebeb30319058b1cb33f2b142d0f1f0c84858fa3d59b40039492ff393b89fa19f04e516f54b
6
+ metadata.gz: a6c46eacdb7d7455ee5e96d3cf7c0970bd85ffc3abd1dc09c856b0f92062beaede5d451c9587ab72afc14d3b3c3a4e800c9685b435fbb4074b7d5e0b77cda463
7
+ data.tar.gz: bf4810fc7384b5b5cecf2db956758a1125a1664b62b8f7275d77347c7e0b78d64fbd76d011375914162c7a3a687bb4b679fd2ace7e8addcda2ad84775b88503f
data/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.2.0 - 2022-08-13
10
+ ### Added
11
+
12
+ - Add ability to serialize most of an item response from the Beta REST API into a usable `Wikidatum::Item` instance.
13
+ - This enables basic functionality like `client.item(id: 'Q123')` and `client.item(id: 'Q123').statements(properties: ['P123'])`.
14
+ - Add `Client#add_statement` method for creating new statements on an item.
15
+ - Add `Client#delete_statement` method for deleting statements from an item.
16
+ - Add support for reading and writing all statement types: `novalue`, `somevalue`, `string`, `time`, `quantity`, `globecoordinate`, `monolingualtext`, and `wikibase-entityid`.
17
+
18
+ ### Internal
19
+
20
+ - Add YARD docs and auto-deploy them with GitHub Pages.
21
+ - Add comprehensive unit tests for the gem.
22
+
9
23
  ## 0.1.0 - 2022-06-20
10
24
  ### Added
11
25
 
data/Gemfile CHANGED
@@ -7,5 +7,7 @@ gemspec
7
7
 
8
8
  gem 'rake', '~> 13.0'
9
9
  gem 'minitest', '~> 5.16'
10
+ gem 'simplecov', '~> 0.21', require: false
10
11
  gem 'rubocop', '~> 1.30'
11
12
  gem 'yard', '~> 0.9'
13
+ gem 'webmock', '~> 3.17', require: false
data/Gemfile.lock CHANGED
@@ -1,16 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- wikidatum (0.1.0)
4
+ wikidatum (0.2.0)
5
+ faraday (~> 2.4)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ addressable (2.8.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
9
12
  ast (2.4.2)
13
+ crack (0.4.5)
14
+ rexml
15
+ docile (1.4.0)
16
+ faraday (2.4.0)
17
+ faraday-net_http (~> 2.0)
18
+ ruby2_keywords (>= 0.0.4)
19
+ faraday-net_http (2.1.0)
20
+ hashdiff (1.0.1)
10
21
  minitest (5.16.0)
11
22
  parallel (1.22.1)
12
23
  parser (3.1.2.0)
13
24
  ast (~> 2.4.1)
25
+ public_suffix (4.0.7)
14
26
  rainbow (3.1.1)
15
27
  rake (13.0.6)
16
28
  regexp_parser (2.5.0)
@@ -27,18 +39,33 @@ GEM
27
39
  rubocop-ast (1.18.0)
28
40
  parser (>= 3.1.1.0)
29
41
  ruby-progressbar (1.11.0)
42
+ ruby2_keywords (0.0.5)
43
+ simplecov (0.21.2)
44
+ docile (~> 1.1)
45
+ simplecov-html (~> 0.11)
46
+ simplecov_json_formatter (~> 0.1)
47
+ simplecov-html (0.12.3)
48
+ simplecov_json_formatter (0.1.4)
30
49
  unicode-display_width (2.1.0)
50
+ webmock (3.17.1)
51
+ addressable (>= 2.8.0)
52
+ crack (>= 0.3.2)
53
+ hashdiff (>= 0.4.0, < 2.0.0)
31
54
  webrick (1.7.0)
32
55
  yard (0.9.28)
33
56
  webrick (~> 1.7.0)
34
57
 
35
58
  PLATFORMS
59
+ arm64-darwin-21
36
60
  x86_64-darwin-21
61
+ x86_64-linux
37
62
 
38
63
  DEPENDENCIES
39
64
  minitest (~> 5.16)
40
65
  rake (~> 13.0)
41
66
  rubocop (~> 1.30)
67
+ simplecov (~> 0.21)
68
+ webmock (~> 3.17)
42
69
  wikidatum!
43
70
  yard (~> 0.9)
44
71
 
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Wikidatum
2
2
 
3
- This gem will support making requests with the [new Wikidata/Wikibase REST API](https://doc.wikimedia.org/Wikibase/master/js/rest-api/).
3
+ This gem supports making requests to the [new Wikidata/Wikibase REST API](https://doc.wikimedia.org/Wikibase/master/js/rest-api/).
4
4
 
5
- **Currently it is in very early development and is not ready for actual usage**.
5
+ **The gem is currently in very early development and is not ready for production usage**.
6
6
 
7
7
  ## Installation
8
8
 
@@ -22,7 +22,42 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ You can view the YARD docs on GitHub Pages [here](https://connorshea.github.io/wikidatum/index.html).
26
+
27
+ Currently, the gem is able to hit a few GET endpoints, and currently has no way to provide authentication and perform POST/PUT/DELETE requests. The additional features will be added later.
28
+
29
+ ```ruby
30
+ require 'wikidatum'
31
+
32
+ wikidatum_client = Wikidatum::Client.new(
33
+ user_agent: 'REPLACE ME WITH THE NAME OF YOUR BOT!',
34
+ # Currently only the beta site has the API available, you'll
35
+ # likely want to use wikidata.org once it's stable.
36
+ wikibase_url: 'https://wikidata.beta.wmflabs.org',
37
+ bot: true
38
+ )
39
+
40
+ # Get an item from the Wikibase instance.
41
+ item = wikidatum_client.item(id: 'Q2') #=> Wikidatum::Item
42
+
43
+ # Get the statements from the item.
44
+ item.statements #=> Array<Wikidatum::Statement>
45
+
46
+ # Get the statments for property P123 on the item.
47
+ item.statements(properties: ['P123']) #=> Array<Wikidatum::Statement>
48
+
49
+ # Get all the labels for the item.
50
+ item.labels #=> Array<Wikidatum::Term>
51
+
52
+ # Get the English label for the item.
53
+ item.label(lang: :en) #=> Wikidatum::Term
54
+
55
+ # Get the actual value for the label.
56
+ item.label(lang: :en).value #=> "Earth"
57
+
58
+ # Get the values for all English aliases on this item.
59
+ item.aliases(langs: [:en]).map(&:value) #=> ["Planet Earth", "Pale Blue Dot"]
60
+ ```
26
61
 
27
62
  ## Development
28
63
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require "rake/testtask"
6
6
  Rake::TestTask.new(:test) do |t|
7
7
  t.libs << "test"
8
8
  t.libs << "lib"
9
- t.test_files = FileList["test/**/test_*.rb"]
9
+ t.test_files = FileList["test/**/*_spec.rb"]
10
10
  end
11
11
 
12
12
  require "rubocop/rake_task"
@@ -1,22 +1,370 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday'
4
+ require 'faraday/net_http'
5
+
3
6
  class Wikidatum::Client
4
- # @return [String] The root URL of the Wikibase instance we want to interact
5
- # with. If not provided, will default to Wikidata.
7
+ ITEM_REGEX = /^Q?\d+$/.freeze
8
+ STATEMENT_REGEX = /^Q?\d+\$[\w-]+$/.freeze
9
+
10
+ # @return [String] the root URL of the Wikibase instance we want to interact
11
+ # with. If not provided, will default to Wikidata.
6
12
  attr_reader :wikibase_url
7
13
 
8
- # @return [Boolean] Whether this client instance should identify itself
9
- # as a bot when making requests.
14
+ # @return [Boolean] whether this client instance should identify itself
15
+ # as a bot when making requests.
10
16
  attr_reader :bot
11
17
 
18
+ # @return [String] the UserAgent header to send with all requests to the
19
+ # Wikibase API.
20
+ attr_reader :user_agent
21
+
22
+ # Create a new Wikidatum::Client to interact with the Wikibase REST API.
23
+ #
24
+ # @example
25
+ # wikidatum_client = Wikidatum::Client.new(
26
+ # user_agent: 'REPLACE ME WITH THE NAME OF YOUR BOT!',
27
+ # wikibase_url: 'https://www.wikidata.org',
28
+ # bot: true
29
+ # )
30
+ #
31
+ # @param user_agent [String] The UserAgent header to send with all requests
32
+ # to the Wikibase API.
12
33
  # @param wikibase_url [String] The root URL of the Wikibase instance we want
13
- # to interact with. If not provided, will
14
- # default to Wikidata.
34
+ # to interact with. If not provided, will default to
35
+ # `https://www.wikidata.org`. Do not include a `/` at the end of the URL.
15
36
  # @param bot [Boolean] Whether requests sent by this client instance should
16
- # be registered as bot requests.
37
+ # be registered as bot requests. Defaults to `true`.
17
38
  # @return [Wikidatum::Client]
18
- def initialize(wikibase_url: 'https://www.wikidata.org', bot: true)
39
+ def initialize(user_agent:, wikibase_url: 'https://www.wikidata.org', bot: true)
40
+ raise ArgumentError, "Wikibase URL must not end with a `/`, got #{wikibase_url.inspect}." if wikibase_url.end_with?('/')
41
+
42
+ # TODO: Add the Ruby gem version to the UserAgent automatically, and
43
+ # restrict the ability for end-users to actually set the UserAgent?
44
+ @user_agent = user_agent
19
45
  @wikibase_url = wikibase_url
20
46
  @bot = bot
47
+
48
+ Faraday.default_adapter = :net_http
49
+ end
50
+
51
+ # Get an item from the Wikibase API based on its QID.
52
+ #
53
+ # @example
54
+ # wikidatum_client.item(id: 'Q123')
55
+ # wikidatum_client.item(id: 123)
56
+ # wikidatum_client.item(id: '123')
57
+ #
58
+ # @param id [String, Integer] Either a string or integer representation of
59
+ # the item's QID, e.g. `"Q123"`, `"123"`, or `123`.
60
+ # @return [Wikidatum::Item]
61
+ def item(id:)
62
+ raise ArgumentError, "#{id.inspect} is an invalid Wikibase QID. Must be an integer, a string representation of an integer, or in the format 'Q123'." unless id.is_a?(Integer) || id.match?(ITEM_REGEX)
63
+
64
+ id = coerce_item_id(id)
65
+
66
+ response = get_request("/entities/items/#{id}")
67
+
68
+ puts JSON.pretty_generate(response) if ENV['DEBUG']
69
+
70
+ Wikidatum::Item.marshal_load(response)
71
+ end
72
+
73
+ # Get a statement from the Wikibase API based on its ID.
74
+ #
75
+ # @example
76
+ # wikidatum_client.statement(id: 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac')
77
+ #
78
+ # @param id [String] A string representation of the statement's ID.
79
+ # @return [Wikidatum::Statement]
80
+ def statement(id:)
81
+ raise ArgumentError, "#{id.inspect} is an invalid Wikibase Statement ID. Must be a string in the format 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac'." unless id.match?(STATEMENT_REGEX)
82
+
83
+ response = get_request("/statements/#{id}")
84
+
85
+ puts JSON.pretty_generate(response) if ENV['DEBUG']
86
+
87
+ Wikidatum::Statement.marshal_load(response)
88
+ end
89
+
90
+ # Add a statement to an item.
91
+ #
92
+ # NOTE: Adding references/qualifiers with `add_statement` is untested and
93
+ # effectively unsupported for now.
94
+ #
95
+ # @example Add a string statement.
96
+ # wikidatum_client.add_statement(
97
+ # id: 'Q123',
98
+ # property: 'P23',
99
+ # datavalue: Wikidatum::DataValueType::WikibaseString.new(string: 'Foo'),
100
+ # comment: 'Adding something or another.'
101
+ # )
102
+ #
103
+ # @example Add a 'no value' statement.
104
+ # wikidatum_client.add_statement(
105
+ # id: 'Q123',
106
+ # property: 'P124',
107
+ # datavalue: Wikidatum::DataValueType::NoValue.new(
108
+ # type: :no_value,
109
+ # value: nil
110
+ # )
111
+ # )
112
+ #
113
+ # @example Add an 'unknown value' statement.
114
+ # wikidatum_client.add_statement(
115
+ # id: 'Q123',
116
+ # property: 'P124',
117
+ # datavalue: Wikidatum::DataValueType::SomeValue.new(
118
+ # type: :some_value,
119
+ # value: nil
120
+ # )
121
+ # )
122
+ #
123
+ # @example Add a globe coordinate statement.
124
+ # wikidatum_client.add_statement(
125
+ # id: 'Q123',
126
+ # property: 'P124',
127
+ # datavalue: Wikidatum::DataValueType::GlobeCoordinate.new(
128
+ # latitude: 52.51666,
129
+ # longitude: 13.3833,
130
+ # precision: 0.01666,
131
+ # globe: 'https://wikidata.org/entity/Q2'
132
+ # )
133
+ # )
134
+ #
135
+ # @example Add a monolingual text statement.
136
+ # wikidatum_client.add_statement(
137
+ # id: 'Q123',
138
+ # property: 'P124',
139
+ # datavalue: Wikidatum::DataValueType::MonolingualText.new(
140
+ # language: 'en',
141
+ # text: 'Foobar'
142
+ # )
143
+ # )
144
+ #
145
+ # @example Add a quantity statement.
146
+ # wikidatum_client.add_statement(
147
+ # id: 'Q123',
148
+ # property: 'P124',
149
+ # datavalue: Wikidatum::DataValueType::Quantity.new(
150
+ # amount: '+12',
151
+ # upper_bound: nil,
152
+ # lower_bound: nil,
153
+ # unit: 'https://wikidata.org/entity/Q1234'
154
+ # )
155
+ # )
156
+ #
157
+ # @example Add a time statement.
158
+ # wikidatum_client.add_statement(
159
+ # id: 'Q123',
160
+ # property: 'P124',
161
+ # datavalue: Wikidatum::DataValueType::Time.new(
162
+ # time: '+2022-08-12T00:00:00Z',
163
+ # time_zone: 0,
164
+ # precision: 11,
165
+ # calendar_model: 'https://wikidata.org/entity/Q1234'
166
+ # )
167
+ # )
168
+ #
169
+ # @example Add a Wikibase item statement.
170
+ # wikidatum_client.add_statement(
171
+ # id: 'Q123',
172
+ # property: 'P124',
173
+ # datavalue: Wikidatum::DataValueType::WikibaseEntityId.new(
174
+ # entity_type: 'item',
175
+ # numeric_id: 1234,
176
+ # id: 'Q1234'
177
+ # )
178
+ # )
179
+ #
180
+ # @param id [String] the ID of the item on which the statement will be added.
181
+ # @param property [String] property ID in the format 'P123'.
182
+ # @param datavalue [Wikidatum::DataValueType::GlobeCoordinate, Wikidatum::DataValueType::MonolingualText, Wikidatum::DataValueType::Quantity, Wikidatum::DataValueType::WikibaseString, Wikidatum::DataValueType::Time, Wikidatum::DataValueType::WikibaseEntityId, Wikidatum::DataValueType::NoValue, Wikidatum::DataValueType::SomeValue] the datavalue of the statement being created.
183
+ # @param datatype [String, nil] if nil, it'll determine the type based on what was passed for the statement argument. This may differ from the type of the Statement's datavalue (for example with the 'url' type).
184
+ # @param qualifiers [Hash<String, Array<Wikidatum::Snak>>]
185
+ # @param references [Array<Wikidatum::Reference>]
186
+ # @param rank [String]
187
+ # @param tags [Array<String>]
188
+ # @param comment [String, nil]
189
+ # @return [Boolean] True if the request succeeded.
190
+ def add_statement(id:, property:, datavalue:, datatype: nil, qualifiers: {}, references: [], rank: 'normal', tags: [], comment: nil)
191
+ raise ArgumentError, "#{id.inspect} is an invalid Wikibase QID. Must be an integer, a string representation of an integer, or in the format 'Q123'." unless id.is_a?(Integer) || id.match?(ITEM_REGEX)
192
+
193
+ id = coerce_item_id(id)
194
+
195
+ # Unless datatype is set explicitly by the caller, just assume we can pull the
196
+ # default from the datavalue class.
197
+ datatype ||= datavalue.wikibase_datatype
198
+
199
+ case datavalue.class.to_s
200
+ when 'Wikidatum::DataValueType::NoValue'
201
+ statement_hash = {
202
+ mainsnak: {
203
+ snaktype: 'novalue',
204
+ property: property,
205
+ datatype: datatype
206
+ }
207
+ }
208
+ when 'Wikidatum::DataValueType::SomeValue'
209
+ statement_hash = {
210
+ mainsnak: {
211
+ snaktype: 'somevalue',
212
+ property: property,
213
+ datatype: datatype
214
+ }
215
+ }
216
+ when 'Wikidatum::DataValueType::GlobeCoordinate', 'Wikidatum::DataValueType::MonolingualText', 'Wikidatum::DataValueType::Quantity', 'Wikidatum::DataValueType::WikibaseString', 'Wikidatum::DataValueType::Time', 'Wikidatum::DataValueType::WikibaseEntityId'
217
+ statement_hash = {
218
+ mainsnak: {
219
+ snaktype: 'value',
220
+ property: property,
221
+ datatype: datatype,
222
+ datavalue: {
223
+ type: datavalue.wikibase_type,
224
+ value: datavalue.marshal_dump
225
+ }
226
+ }
227
+ }
228
+ else
229
+ raise ArgumentError, "Expected an instance of one of Wikidatum::DataValueType's subclasses for datavalue, but got #{datavalue.inspect}."
230
+ end
231
+
232
+ body = { statement: statement_hash.merge({ qualifiers: qualifiers, references: references, rank: rank, type: "statement" }) }
233
+
234
+ response = post_request("/entities/items/#{id}/statements", body, tags: tags, comment: comment)
235
+
236
+ puts JSON.pretty_generate(response) if ENV['DEBUG']
237
+
238
+ response.success?
239
+ end
240
+
241
+ # Delete a statement from an item.
242
+ #
243
+ # @example
244
+ # wikidatum_client.delete_statement(
245
+ # id: 'Q123$4543523c-1d1d-1111-1e1e-11b11111b1f1',
246
+ # comment: "Deleting this statement because it's bad."
247
+ # )
248
+ #
249
+ # @param id [String] the ID of the statemnt being deleted.
250
+ # @param tags [Array<String>]
251
+ # @param comment [String, nil]
252
+ # @return [Boolean] True if the request succeeded.
253
+ def delete_statement(id:, tags: [], comment: nil)
254
+ raise ArgumentError, "#{id.inspect} is an invalid Wikibase Statement ID. Must be a string in the format 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac'." unless id.match?(STATEMENT_REGEX)
255
+
256
+ response = delete_request("/statements/#{id}", tags: tags, comment: comment)
257
+
258
+ puts JSON.pretty_generate(response) if ENV['DEBUG']
259
+
260
+ response.success?
261
+ end
262
+
263
+ private
264
+
265
+ # For now this just returns the `@wikibase_url`, but in the future the API
266
+ # routes will presumably be nested further, so this is just future-proofing
267
+ # to allow that to be easily changed later.
268
+ #
269
+ # @return [String] URL for the Wikibase API endpoint.
270
+ def api_url
271
+ @api_url ||= "#{@wikibase_url}/w/rest.php/wikibase/v0"
272
+ end
273
+
274
+ # Default headers to be sent with every request.
275
+ #
276
+ # @return [Hash] A hash of some headers that should be used when sending a request.
277
+ def universal_headers
278
+ @universal_headers ||= {
279
+ 'User-Agent' => @user_agent,
280
+ 'Content-Type' => 'application/json'
281
+ }
282
+ end
283
+
284
+ # Make a GET request to a given Wikibase endpoint.
285
+ #
286
+ # @param path [String] The relative path for the API endpoint.
287
+ # @param params [Hash] Query parameters to send with the request, if any.
288
+ # @return [Hash] JSON response, parsed into a hash.
289
+ def get_request(path, params = nil)
290
+ url = "#{api_url}#{path}"
291
+
292
+ response = Faraday.get(url, params, universal_headers)
293
+
294
+ # Error handling if it doesn't return a 200
295
+ unless response.success?
296
+ puts 'Something went wrong with this request!'
297
+ puts "Status Code: #{response.status}"
298
+ puts response.body.inspect
299
+ end
300
+
301
+ JSON.parse(response.body)
302
+ end
303
+
304
+ # Make a POST request to a given Wikibase endpoint.
305
+ #
306
+ # @param path [String] The relative path for the API endpoint.
307
+ # @param body [Hash] The body to post to the endpoint.
308
+ # @param tags [Array<String>] The tags to apply to the edit being made by this request, for PUT/POST/DELETE requests.
309
+ # @param comment [String] The edit description, for PUT/POST/DELETE requests.
310
+ # @return [Hash] JSON response, parsed into a hash.
311
+ def post_request(path, body = {}, tags: nil, comment: nil)
312
+ url = "#{api_url}#{path}"
313
+
314
+ body[:bot] = @bot
315
+ body[:tags] = tags unless tags.empty?
316
+ body[:comment] = comment unless comment.nil?
317
+
318
+ response = Faraday.post(url) do |req|
319
+ req.body = JSON.generate(body)
320
+ req.headers = universal_headers
321
+ end
322
+
323
+ puts response.body.inspect if ENV['DEBUG']
324
+
325
+ response
326
+ end
327
+
328
+ # Make a DELETE request to a given Wikibase endpoint.
329
+ #
330
+ # @param path [String] The relative path for the API endpoint.
331
+ # @param tags [Array<String>] The tags to apply to the edit being made by this request, for PUT/POST/DELETE requests.
332
+ # @param comment [String] The edit description, for PUT/POST/DELETE requests.
333
+ # @return [Hash] JSON response, parsed into a hash.
334
+ def delete_request(path, tags: [], comment: nil)
335
+ url = "#{api_url}#{path}"
336
+
337
+ body = {}
338
+ body[:bot] = @bot
339
+ body[:tags] = tags unless tags.empty?
340
+ body[:comment] = comment unless comment.nil?
341
+
342
+ response = Faraday.delete(url) do |req|
343
+ req.body = JSON.generate(body)
344
+ req.headers = universal_headers
345
+ end
346
+
347
+ puts response.body.inspect if ENV['DEBUG']
348
+
349
+ # Error handling if it doesn't return a 200
350
+ unless response.success?
351
+ puts 'Something went wrong with this request!'
352
+ puts "Status Code: #{response.status}"
353
+ puts response.body.inspect
354
+ end
355
+
356
+ response
357
+ end
358
+
359
+ # Coerce an Item ID in the formats 'Q123', '123' or 123 into a consistent
360
+ # 'Q123' format. We need to have the ID in the format 'Q123' for the API
361
+ # request, which is why coercion is necessary.
362
+ #
363
+ # @param id [String, Integer]
364
+ # @return [String]
365
+ def coerce_item_id(id)
366
+ return id if id.to_s.start_with?('Q')
367
+
368
+ "Q#{id}"
21
369
  end
22
370
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # For more information on the possible types that can be returned by
4
+ # datavalues, see the official documentation:
5
+ # https://doc.wikimedia.org/Wikibase/master/php/md_docs_topics_json.html#json_datavalues
6
+ module Wikidatum::DataValueType
7
+ class Base
8
+ # Represents the type for this datavalue instance.
9
+ #
10
+ # Possible values for the `type` attribute are:
11
+ #
12
+ # - `:no_value`: No value
13
+ # - `:some_value`: Unknown value
14
+ # - `:globe_coordinate`: {DataValueType::GlobeCoordinate}
15
+ # - `:monolingual_text`: {DataValueType::MonolingualText}
16
+ # - `:quantity`: {DataValueType::Quantity}
17
+ # - `:string`: {DataValueType::WikibaseString}
18
+ # - `:time`: {DataValueType::Time}
19
+ # - `:wikibase_entity_id`: {DataValueType::WikibaseEntityId}
20
+ #
21
+ # @return [Symbol]
22
+ attr_reader :type
23
+
24
+ # The value of the datavalue object in the response.
25
+ #
26
+ # If the `type` is `novalue` or `somevalue`, this returns `nil`.
27
+ #
28
+ # @return [DataValueType::GlobeCoordinate, DataValueType::MonolingualText, DataValueType::Quantity, DataValueType::WikibaseString, DataValueType::Time, DataValueType::WikibaseEntityId, nil]
29
+ attr_reader :value
30
+
31
+ # @param type [Symbol]
32
+ # @param value [DataValueType::GlobeCoordinate, DataValueType::MonolingualText, DataValueType::Quantity, DataValueType::WikibaseString, DataValueType::Time, DataValueType::WikibaseEntityId, nil] nil if type is no_value or some_value
33
+ # @return [void]
34
+ def initialize(type:, value:)
35
+ @type = type
36
+ @value = value
37
+ end
38
+
39
+ # @return [Hash]
40
+ def to_h
41
+ {
42
+ type: @type,
43
+ value: @value&.to_h
44
+ }
45
+ end
46
+
47
+ # @!visibility private
48
+ #
49
+ # @param data_value_type [String] The value of `type` for the given Snak's datavalue.
50
+ # @param data_value_json [Hash] The `value` part of datavalue.
51
+ # @return [Wikidatum::DataValueType::Base] An instance of Base.
52
+ def self.marshal_load(data_value_type, data_value_json)
53
+ unless Wikidatum::DataValueType::DATA_VALUE_TYPES.keys.include?(data_value_type.to_sym)
54
+ puts 'WARNING: Unsupported datavalue type.'
55
+ return nil
56
+ end
57
+
58
+ Object.const_get(Wikidatum::DataValueType::DATA_VALUE_TYPES[data_value_type.to_sym]).marshal_load(data_value_json)
59
+ end
60
+ end
61
+ end