reality 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/CHANGELOG.md +20 -0
- data/README.md +104 -407
- data/bin/reality +7 -10
- data/config/demo.yml +1 -0
- data/lib/reality.rb +11 -6
- data/lib/reality/command_line.rb +124 -0
- data/lib/reality/config.rb +10 -3
- data/lib/reality/definitions/helpers.rb +1 -0
- data/lib/reality/definitions/wikidata.rb +10 -1
- data/lib/reality/definitions/wikipedia_character.rb +1 -0
- data/lib/reality/definitions/wikipedia_city.rb +1 -0
- data/lib/reality/definitions/wikipedia_continent.rb +1 -0
- data/lib/reality/definitions/wikipedia_country.rb +1 -0
- data/lib/reality/definitions/wikipedia_musical_artist.rb +1 -0
- data/lib/reality/definitions/wikipedia_person.rb +1 -0
- data/lib/reality/entity.rb +239 -51
- data/lib/reality/entity/coercion.rb +20 -0
- data/lib/reality/entity/wikidata_predicates.rb +1 -0
- data/lib/reality/entity/wikipedia_type.rb +1 -0
- data/lib/reality/extras/geonames.rb +5 -3
- data/lib/reality/extras/quandl.rb +57 -0
- data/lib/reality/geo.rb +67 -6
- data/lib/reality/list.rb +76 -6
- data/lib/reality/measure.rb +17 -9
- data/lib/reality/methods.rb +7 -3
- data/lib/reality/names.rb +46 -0
- data/lib/reality/refinements.rb +1 -0
- data/lib/reality/tz_offset.rb +57 -4
- data/lib/reality/util/formatters.rb +1 -0
- data/lib/reality/util/parsers.rb +1 -0
- data/lib/reality/version.rb +1 -1
- data/lib/reality/wikidata.rb +25 -177
- data/lib/reality/wikidata/query.rb +106 -0
- data/reality.gemspec +5 -7
- metadata +20 -56
data/lib/reality/methods.rb
CHANGED
@@ -2,12 +2,16 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Reality
|
4
4
|
module Methods
|
5
|
-
def Entity(name
|
6
|
-
Entity.load(name
|
5
|
+
def Entity(name)
|
6
|
+
Entity.load(name)
|
7
7
|
end
|
8
8
|
|
9
9
|
def List(*names)
|
10
|
-
|
10
|
+
List.new(*names)
|
11
|
+
end
|
12
|
+
|
13
|
+
def Measure(*arg)
|
14
|
+
Measure.new(*arg)
|
11
15
|
end
|
12
16
|
|
13
17
|
extend Forwardable
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Reality
|
2
|
+
# This optional and higly experimental module allows treat ALL objects
|
3
|
+
# available with Reality, as Ruby constants (via redefined `const_missing`).
|
4
|
+
# This practice may seem questionable, so use it wisely!
|
5
|
+
#
|
6
|
+
# You can just use this module on its own:
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# Reality::Names::Argentina
|
10
|
+
# # => #<Reality::Entity(Argentina):country>
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# ...Or just include it elsewhere:
|
14
|
+
#
|
15
|
+
# ```ruby
|
16
|
+
# include Reality::Names
|
17
|
+
#
|
18
|
+
# Argentina
|
19
|
+
# # => #<Reality::Entity(Argentina):country>
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
# Multi-word entities can also be called:
|
23
|
+
#
|
24
|
+
# ```ruby
|
25
|
+
# BuenosAires
|
26
|
+
# # => #<Reality::Entity(Buenos Aires):city>
|
27
|
+
# ```
|
28
|
+
#
|
29
|
+
# Though, more complicated entity names (with punctuations) can't be
|
30
|
+
# accessed this way.
|
31
|
+
#
|
32
|
+
module Names
|
33
|
+
def Names.const_missing(symbol)
|
34
|
+
name = symbol.to_s.
|
35
|
+
gsub('_', ' ').
|
36
|
+
gsub(/([a-z])([A-Z])/, '\1 \2')
|
37
|
+
Reality::Entity(name) or super
|
38
|
+
end
|
39
|
+
|
40
|
+
def Names.included(other)
|
41
|
+
other.define_singleton_method(:const_missing){|name|
|
42
|
+
Reality::Names.const_missing(name)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/reality/refinements.rb
CHANGED
data/lib/reality/tz_offset.rb
CHANGED
@@ -1,11 +1,46 @@
|
|
1
1
|
module Reality
|
2
|
+
# Simple class representing timezone offset (in minutes). Knows nothing
|
3
|
+
# about timezone name, DST or other complications, but useful when ONLY
|
4
|
+
# offset is known for some {Entity}.
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# # As entity property:
|
10
|
+
# Reality::Entity('Beijing').tz_offset
|
11
|
+
# # => #<Reality::TZOffset(UTC+08:00)>
|
12
|
+
#
|
13
|
+
# # On itself:
|
14
|
+
# o = Reality::TZOffset.parse('UTC+3')
|
15
|
+
# # => #<Reality::TZOffset(UTC+03:00)>
|
16
|
+
#
|
17
|
+
# o.now
|
18
|
+
# # => 2016-04-16 19:01:40 +0300
|
19
|
+
# o.local(2016, 4, 1, 20, 30)
|
20
|
+
# # => 2016-04-01 20:30:00 +0300
|
21
|
+
# o.convert(Time.now)
|
22
|
+
# # => 2016-04-16 19:02:22 +0300
|
23
|
+
# ```
|
24
|
+
#
|
2
25
|
class TZOffset
|
3
26
|
using Refinements
|
4
|
-
|
27
|
+
|
28
|
+
# Number of minutes in offset.
|
29
|
+
#
|
30
|
+
# @return [Fixnum]
|
5
31
|
attr_reader :minutes
|
6
32
|
|
33
|
+
# @private
|
7
34
|
MINUSES = /[−—–]/
|
8
35
|
|
36
|
+
# Parses TZOffset from string. Understands several options like:
|
37
|
+
#
|
38
|
+
# * `GMT` (not all TZ names, only those Ruby itself knows about);
|
39
|
+
# * `UTC+3` (or `GMT+3`);
|
40
|
+
# * `+03:30`;
|
41
|
+
# * ..and several combinations.
|
42
|
+
#
|
43
|
+
# @return [TZOffset]
|
9
44
|
def self.parse(text)
|
10
45
|
text = text.gsub(MINUSES, '-')
|
11
46
|
|
@@ -13,24 +48,32 @@ module Reality
|
|
13
48
|
when /^[A-Z]{3}$/
|
14
49
|
Time.zone_offset(text)
|
15
50
|
when /^(?:UTC|GMT)?([+-]\d{1,2}:?\d{2})$/
|
16
|
-
|
51
|
+
offset = $1
|
52
|
+
Time.zone_offset(offset.sub(/^([+-])(\d):/, '\10\2:'))
|
17
53
|
when /^(?:UTC|GMT)?([+-]\d{1,2})/
|
18
54
|
$1.to_i * 3600
|
19
55
|
end.derp{|sec| sec && new(sec / 60)}
|
20
56
|
end
|
21
|
-
|
57
|
+
|
58
|
+
# Constructs offset from number of minutes. In most cases, you don't
|
59
|
+
# want to use it, but rather {TZOffset.parse}.
|
60
|
+
#
|
61
|
+
# @param minutes [Fixnum] Number of minutes in offset.
|
22
62
|
def initialize(minutes)
|
23
63
|
@minutes = minutes
|
24
64
|
end
|
25
65
|
|
66
|
+
# @return [String]
|
26
67
|
def inspect
|
27
68
|
'#<%s(UTC%+03i:%02i)>' % [self.class.name, *minutes.divmod(60)]
|
28
69
|
end
|
29
70
|
|
71
|
+
# @return [String]
|
30
72
|
def to_s
|
31
73
|
'%+03i:%02i' % minutes.divmod(60)
|
32
74
|
end
|
33
75
|
|
76
|
+
# @return [Boolean]
|
34
77
|
def <=>(other)
|
35
78
|
other.is_a?(TZOffset) or fail ArgumentError, "Can't compare TZOffset with #{other.class}"
|
36
79
|
minutes <=> other.minutes
|
@@ -38,18 +81,28 @@ module Reality
|
|
38
81
|
|
39
82
|
include Comparable
|
40
83
|
|
84
|
+
# Like Ruby's `Time.now`, but in desired offset.
|
85
|
+
#
|
86
|
+
# @return [Time] Current time in that offset.
|
41
87
|
def now
|
42
88
|
convert(Time.now)
|
43
89
|
end
|
44
90
|
|
91
|
+
# Like Ruby's `Time.local`, but in desired offset.
|
92
|
+
#
|
93
|
+
# @return [Time] Constructed time in that offset.
|
45
94
|
def local(*values)
|
46
95
|
values << 0 until values.count == 6
|
47
96
|
Time.new(*values, to_s)
|
48
97
|
end
|
49
98
|
|
50
|
-
#
|
99
|
+
# Converts `tm` into correct offset.
|
100
|
+
#
|
101
|
+
# @param tm [Time] Time object to convert (with any offset);
|
102
|
+
# @return [Time] Converted object.
|
51
103
|
def convert(tm)
|
52
104
|
pattern = tm.utc + minutes * 60
|
105
|
+
# FIXME: usec are lost
|
53
106
|
Time.new(
|
54
107
|
pattern.year,
|
55
108
|
pattern.month,
|
data/lib/reality/util/parsers.rb
CHANGED
data/lib/reality/version.rb
CHANGED
data/lib/reality/wikidata.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require_relative 'wikidata/query'
|
2
|
+
|
1
3
|
module Reality
|
2
4
|
using Reality::Refinements
|
3
|
-
|
5
|
+
|
6
|
+
# @private
|
4
7
|
module Wikidata
|
5
8
|
class Link
|
6
9
|
attr_reader :id, :label
|
@@ -18,198 +21,38 @@ module Reality
|
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
|
-
# FIXME: I should be burn in hell for this mess. But it works. Somehow.
|
22
24
|
class Entity
|
23
|
-
PREFIX = %Q{
|
24
|
-
PREFIX wikibase: <http://wikiba.se/ontology#>
|
25
|
-
PREFIX wd: <http://www.wikidata.org/entity/>
|
26
|
-
PREFIX wdt: <http://www.wikidata.org/prop/direct/>
|
27
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
28
|
-
PREFIX p: <http://www.wikidata.org/prop/>
|
29
|
-
PREFIX v: <http://www.wikidata.org/prop/statement/>
|
30
|
-
PREFIX schema: <http://schema.org/>
|
31
|
-
}
|
32
|
-
|
33
|
-
SINGLE_QUERY = %Q{
|
34
|
-
#{PREFIX}
|
35
|
-
|
36
|
-
SELECT ?id ?p ?o ?oLabel WHERE {
|
37
|
-
<https://en.wikipedia.org/wiki/%{title}> schema:about ?id .
|
38
|
-
{
|
39
|
-
?id ?p ?o .
|
40
|
-
FILTER(STRSTARTS(STR(?p), "http://www.wikidata.org/prop/direct/"))
|
41
|
-
} union {
|
42
|
-
?id ?p ?o .
|
43
|
-
filter(langMatches(lang(?o), "EN")).
|
44
|
-
filter(?p = rdfs:label)
|
45
|
-
}
|
46
|
-
SERVICE wikibase:label {
|
47
|
-
bd:serviceParam wikibase:language "en" .
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|
52
|
-
ID_QUERY = %Q{
|
53
|
-
#{PREFIX}
|
54
|
-
|
55
|
-
SELECT ?id ?p ?o ?oLabel WHERE {
|
56
|
-
bind(wd:%{id} as ?id)
|
57
|
-
{
|
58
|
-
?id ?p ?o .
|
59
|
-
FILTER(
|
60
|
-
STRSTARTS(STR(?p), "http://www.wikidata.org/prop/direct/") ||
|
61
|
-
(?p = rdfs:label && langMatches(lang(?o), "EN"))
|
62
|
-
)
|
63
|
-
} union {
|
64
|
-
bind(schema:about as ?p) .
|
65
|
-
?o schema:about ?id .
|
66
|
-
filter(strstarts(str(?o), "https://en.wikipedia.org/wiki/"))
|
67
|
-
}
|
68
|
-
SERVICE wikibase:label {
|
69
|
-
bd:serviceParam wikibase:language "en" .
|
70
|
-
}
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
MULTIPLE_QUERY = %Q{
|
75
|
-
#{PREFIX}
|
76
|
-
|
77
|
-
SELECT ?id ?p ?o ?oLabel WHERE {
|
78
|
-
%{selectors} .
|
79
|
-
{
|
80
|
-
?id ?p ?o .
|
81
|
-
FILTER(
|
82
|
-
STRSTARTS(STR(?p), "http://www.wikidata.org/prop/direct/") ||
|
83
|
-
(?p = rdfs:label && langMatches(lang(?o), "EN"))
|
84
|
-
)
|
85
|
-
} union {
|
86
|
-
bind(schema:about as ?p) .
|
87
|
-
?o schema:about ?id .
|
88
|
-
filter(strstarts(str(?o), "https://en.wikipedia.org/wiki/"))
|
89
|
-
}
|
90
|
-
SERVICE wikibase:label {
|
91
|
-
bd:serviceParam wikibase:language "en" .
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
MULTIPLE_IDS_QUERY = %Q{
|
96
|
-
#{PREFIX}
|
97
|
-
|
98
|
-
SELECT ?id ?p ?o ?oLabel WHERE {
|
99
|
-
%{selectors} .
|
100
|
-
{
|
101
|
-
?id ?p ?o .
|
102
|
-
FILTER(
|
103
|
-
STRSTARTS(STR(?p), "http://www.wikidata.org/prop/direct/") ||
|
104
|
-
(?p = rdfs:label && langMatches(lang(?o), "EN"))
|
105
|
-
)
|
106
|
-
} union {
|
107
|
-
bind(schema:about as ?p) .
|
108
|
-
?o schema:about ?id .
|
109
|
-
filter(strstarts(str(?o), "https://en.wikipedia.org/wiki/"))
|
110
|
-
}
|
111
|
-
SERVICE wikibase:label {
|
112
|
-
bd:serviceParam wikibase:language "en" .
|
113
|
-
}
|
114
|
-
}
|
115
|
-
}
|
116
|
-
SELECTOR = %Q{
|
117
|
-
{
|
118
|
-
<https://en.wikipedia.org/wiki/%{title}> schema:about ?id
|
119
|
-
}
|
120
|
-
}
|
121
|
-
IDSELECTOR = %Q{
|
122
|
-
{
|
123
|
-
BIND(wd:%{id} as ?id)
|
124
|
-
}
|
125
|
-
}
|
126
|
-
|
127
|
-
UNSAFE = Regexp.union(URI::UNSAFE, /[,()']/)
|
128
|
-
|
129
25
|
class << self
|
130
|
-
def
|
131
|
-
|
132
|
-
f.adapter Faraday.default_adapter
|
133
|
-
}
|
26
|
+
def by_wikititle(*titles)
|
27
|
+
Query.by_wikititle(*titles)
|
134
28
|
end
|
135
29
|
|
136
|
-
def
|
137
|
-
|
138
|
-
faraday.get('', query: SINGLE_QUERY % {title: title}, format: :json).
|
139
|
-
derp{|res| from_sparql(res.body, subject: 'id', predicate: 'p', object: 'o', object_label: 'oLabel')}
|
30
|
+
def by_id(*ids)
|
31
|
+
Query.by_id(*ids)
|
140
32
|
end
|
141
33
|
|
142
|
-
def
|
143
|
-
|
144
|
-
derp{|res| from_sparql(res.body, subject: 'id', predicate: 'p', object: 'o', object_label: 'oLabel')}.
|
145
|
-
first
|
34
|
+
def by_label(*labels)
|
35
|
+
Query.by_label(*labels)
|
146
36
|
end
|
147
37
|
|
148
|
-
|
149
|
-
|
150
|
-
MAX_SLICE = 20
|
151
|
-
|
152
|
-
def fetch_list(*titles)
|
153
|
-
titles.each_slice(MAX_SLICE).map{|titles_chunk|
|
154
|
-
fetch_small_list(*titles_chunk)
|
155
|
-
}.inject(:merge)
|
38
|
+
def one_by_wikititle(title)
|
39
|
+
by_wikititle(title).values.first
|
156
40
|
end
|
157
41
|
|
158
|
-
def
|
159
|
-
|
160
|
-
fetch_small_idlist(*ids_chunk)
|
161
|
-
}.inject(:merge)
|
42
|
+
def one_by_id(id)
|
43
|
+
by_id(id).values.first
|
162
44
|
end
|
163
45
|
|
164
|
-
def
|
165
|
-
|
166
|
-
map{|t| SELECTOR % {title: URI.escape(t, UNSAFE)}}.
|
167
|
-
join(' UNION ').
|
168
|
-
derp{|selectors| MULTIPLE_QUERY % {selectors: selectors}}.
|
169
|
-
derp{|query|
|
170
|
-
faraday.get('', query: query, format: :json)
|
171
|
-
}.
|
172
|
-
derp{|res|
|
173
|
-
from_sparql(
|
174
|
-
res.body,
|
175
|
-
subject: 'id',
|
176
|
-
predicate: 'p',
|
177
|
-
object: 'o',
|
178
|
-
object_label: 'oLabel')
|
179
|
-
}.
|
180
|
-
map{|e|
|
181
|
-
[e.en_wikipage, e]
|
182
|
-
}.to_h
|
46
|
+
def one_by_label(label)
|
47
|
+
by_label(label).values.first
|
183
48
|
end
|
184
49
|
|
185
|
-
|
186
|
-
def fetch_small_idlist(*ids)
|
187
|
-
ids.
|
188
|
-
map{|i| IDSELECTOR % {id: i}}.
|
189
|
-
join(' UNION ').
|
190
|
-
derp{|selectors| MULTIPLE_IDS_QUERY % {selectors: selectors}}.
|
191
|
-
derp{|query|
|
192
|
-
faraday.get('', query: query, format: :json)
|
193
|
-
}.
|
194
|
-
derp{|res|
|
195
|
-
from_sparql(
|
196
|
-
res.body,
|
197
|
-
subject: 'id',
|
198
|
-
predicate: 'p',
|
199
|
-
object: 'o',
|
200
|
-
object_label: 'oLabel')
|
201
|
-
}.
|
202
|
-
map{|e|
|
203
|
-
[e.id, e]
|
204
|
-
}.to_h
|
205
|
-
end
|
206
|
-
|
207
|
-
def from_sparql(sparql_json, subject: 'subject', predicate: 'predicate', object: 'object', object_label: 'object_label')
|
50
|
+
def from_sparql(sparql_json)
|
208
51
|
JSON.parse(sparql_json)['results']['bindings'].map{|row|
|
209
52
|
[
|
210
|
-
row[
|
211
|
-
row[
|
212
|
-
row[
|
53
|
+
row['s']['value'].sub('http://www.wikidata.org/entity/', ''),
|
54
|
+
row['p']['value'].sub('http://www.wikidata.org/prop/direct/', ''),
|
55
|
+
row['o'].merge('label' => row['oLabel']['value'])
|
213
56
|
]
|
214
57
|
}.group_by(&:first).
|
215
58
|
map{|id, rows|
|
@@ -223,6 +66,7 @@ module Reality
|
|
223
66
|
to_h
|
224
67
|
end
|
225
68
|
|
69
|
+
# FIXME: move all parse_* to util/parsers or wikidata/parsers
|
226
70
|
def parse_value(hash)
|
227
71
|
case hash['type']
|
228
72
|
when 'literal'
|
@@ -278,6 +122,10 @@ module Reality
|
|
278
122
|
@predicates[pred]
|
279
123
|
end
|
280
124
|
|
125
|
+
def id_i
|
126
|
+
id.sub('Q', '').to_i
|
127
|
+
end
|
128
|
+
|
281
129
|
def label
|
282
130
|
self['http://www.w3.org/2000/01/rdf-schema#label'].first
|
283
131
|
end
|