uri_service 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +161 -0
- data/lib/uri_service/client.rb +327 -0
- data/lib/uri_service/railtie.rb +14 -0
- data/lib/uri_service/version.rb +9 -0
- data/lib/uri_service.rb +53 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: db76f18be933843e1f6ebbfe4030ad43f4023213
|
4
|
+
data.tar.gz: a2e90e89004ac7f7fc48e8f721ae5e50776a0bfd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4f19344458d769786b63b62f3bd990a00c20a9b0d77b541486da24d59eb8d765785da7328a9906b9f5d6374b3cb31d46b5dad9a0bb23b3dd1cf308efb7ea13b3
|
7
|
+
data.tar.gz: 8bac0c9b2704fdab55893b4560e0b48367060030ba7714db499050cb23113c49dd8ad0641a8e78c261e006809c46ab61d3b84696a912ef09bb4a84d33e100226
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
#URI Service
|
2
|
+
|
3
|
+
A database-backed and Solr-cached lookup/creation service for URIs. Works with or without Rails.
|
4
|
+
|
5
|
+
### Usage:
|
6
|
+
|
7
|
+
**Install uri_service:**
|
8
|
+
|
9
|
+
```bash
|
10
|
+
gem install uri_service
|
11
|
+
```
|
12
|
+
|
13
|
+
**Initialize and use the main client instance:**
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
UriService::init({
|
17
|
+
'local_uri_base' => 'http://id.example.com/term/',
|
18
|
+
'solr' => {
|
19
|
+
'url' => 'http://localhost:8983/solr/uri_service_test',
|
20
|
+
'pool_size' => 5,
|
21
|
+
'pool_timeout' => 5000
|
22
|
+
}
|
23
|
+
'database' => {
|
24
|
+
'adapter' => sqlite,
|
25
|
+
'database' => db/test.sqlite3,
|
26
|
+
'max_connections' => 5
|
27
|
+
'pool_timeout' => 5000
|
28
|
+
}
|
29
|
+
})
|
30
|
+
|
31
|
+
UriService.client.do_stuff(...)
|
32
|
+
```
|
33
|
+
|
34
|
+
**Or create your own separate instance:**
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
client = UriService::Client.new({
|
38
|
+
'local_uri_base' => 'http://id.example.com/term/',
|
39
|
+
'solr' => {
|
40
|
+
'url' => 'http://localhost:8983/solr/uri_service_test',
|
41
|
+
'pool_size' => 5,
|
42
|
+
'pool_timeout' => 5000
|
43
|
+
}
|
44
|
+
'database' => {
|
45
|
+
'adapter' => sqlite,
|
46
|
+
'database' => db/test.sqlite3,
|
47
|
+
'max_connections' => 5
|
48
|
+
'pool_timeout' => 5000
|
49
|
+
}
|
50
|
+
})
|
51
|
+
|
52
|
+
client.do_stuff(...)
|
53
|
+
```
|
54
|
+
|
55
|
+
**Note: Each instance of UriService::Client creates a solr connection pool and a database connection pool, so it's better to share a single instance rather than create many separate instances.**
|
56
|
+
|
57
|
+
### In Rails:
|
58
|
+
|
59
|
+
When including the uri_service gem in Rails, create a file at **config/uri_service.yml** file that includes configurations for each environment. These settings will be picked up automatically and passed to UriService::init() for the current environment.
|
60
|
+
|
61
|
+
Note that the database that you specify here does not have to be the same database that your other Rails models point to (in your Rails database.yml file). If multiple apps share the same URI Service data, you may want to have a separate, shared database.
|
62
|
+
|
63
|
+
**uri_service.yml:**
|
64
|
+
|
65
|
+
```yaml
|
66
|
+
development:
|
67
|
+
local_uri_base: 'http://id.example.com/term/'
|
68
|
+
solr:
|
69
|
+
url: 'http://localhost:8983/solr/uri_service_development'
|
70
|
+
pool_size: 5
|
71
|
+
pool_timeout: 5000
|
72
|
+
database:
|
73
|
+
adapter: sqlite
|
74
|
+
database: db/uri_service_development.sqlite3
|
75
|
+
max_connections: 5
|
76
|
+
pool_timeout: 5000
|
77
|
+
|
78
|
+
test:
|
79
|
+
local_uri_base: 'http://id.example.com/term/'
|
80
|
+
solr:
|
81
|
+
url: 'http://localhost:8983/solr/uri_service_test'
|
82
|
+
pool_size: 5
|
83
|
+
pool_timeout: 5000
|
84
|
+
database:
|
85
|
+
adapter: sqlite
|
86
|
+
database: db/uri_service_test.sqlite3
|
87
|
+
max_connections: 5
|
88
|
+
pool_timeout: 5000
|
89
|
+
|
90
|
+
production:
|
91
|
+
local_uri_base: 'http://id.example.com/term/'
|
92
|
+
solr:
|
93
|
+
url: 'http://localhost:9983/solr/uri_service_production'
|
94
|
+
pool_size: 5
|
95
|
+
pool_timeout: 5000
|
96
|
+
database:
|
97
|
+
adapter: mysql2
|
98
|
+
database: dbname
|
99
|
+
max_connections: 10
|
100
|
+
pool_timeout: 5000
|
101
|
+
timeout: 5000
|
102
|
+
host: uri_service_prod.example.com
|
103
|
+
port: 3306
|
104
|
+
username: the_username
|
105
|
+
password: the_password
|
106
|
+
```
|
107
|
+
|
108
|
+
### Using different database systems (sqlite, MySQL, PostgreSQL):
|
109
|
+
|
110
|
+
Parameters for the database config are passed directly to a new instance of a Sequel library connection, so any options offered by Sequel can be used here. (Just be sure to set the right adapter). See: http://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html
|
111
|
+
|
112
|
+
### Creating Vocabularies and Terms:
|
113
|
+
|
114
|
+
Create a vocabulary:
|
115
|
+
```ruby
|
116
|
+
# Creates a vocabulary with string key 'names' and display label 'Names'
|
117
|
+
|
118
|
+
UriService.client.create_vocabulary('names', 'Names')
|
119
|
+
```
|
120
|
+
|
121
|
+
Create a term in a vocabulary:
|
122
|
+
```ruby
|
123
|
+
# Creates a term in the 'names' vocabulary, using the given value, uri and a couple of custom key-value pairs
|
124
|
+
|
125
|
+
UriService.client.create_term('names', 'Lincoln, Abraham, 1809-1865', 'http://id.loc.gov/authorities/names/n79006779', {'is_awesome' => true, 'best_president' => true, 'hat_type' => 'Stove Pipe'})
|
126
|
+
```
|
127
|
+
|
128
|
+
Create a LOCAL term in a vocabulary (when you don't have a URI for your term):
|
129
|
+
```ruby
|
130
|
+
# Creates a new LOCAL term in the 'names' vocabulary. New URI is automatically generated.
|
131
|
+
|
132
|
+
UriService.client.create_local_term('names', 'Baby, Newborn', {'is_baby' => true})
|
133
|
+
```
|
134
|
+
|
135
|
+
Find a term in a vocabulary (after you've already added the term to the vocabulary):
|
136
|
+
```ruby
|
137
|
+
UriService.client.find_terms_by_query('names', 'batman')
|
138
|
+
# =>
|
139
|
+
# [
|
140
|
+
# {
|
141
|
+
# 'uri' => 'http://id.loc.gov/authorities/names/n91059657',
|
142
|
+
# 'value' => 'Batman, John, 1800-1839',
|
143
|
+
# 'vocabulary_string_key' => 'names',
|
144
|
+
# 'is_local' => false
|
145
|
+
# }
|
146
|
+
# {
|
147
|
+
# 'uri' => 'http://id.loc.gov/authorities/names/n82259885',
|
148
|
+
# 'value' => 'Batman, Stephen, -1584',
|
149
|
+
# 'vocabulary_string_key' => 'names',
|
150
|
+
# 'is_local' => false
|
151
|
+
# },
|
152
|
+
# ]
|
153
|
+
```
|
154
|
+
|
155
|
+
### Running Integration Tests (for developers):
|
156
|
+
|
157
|
+
Integration tests are great and we should run them. Here's how:
|
158
|
+
|
159
|
+
```sh
|
160
|
+
bundle exec rake uri_service:ci
|
161
|
+
```
|
@@ -0,0 +1,327 @@
|
|
1
|
+
class UriService::Client
|
2
|
+
|
3
|
+
attr_reader :db, :rsolr_pool, :local_uri_base
|
4
|
+
|
5
|
+
ALPHANUMERIC_UNDERSCORE_KEY_REGEX = /\A[a-z]+[a-z0-9_]*\z/
|
6
|
+
VALID_URI_REGEX = /\A#{URI::regexp(['http', 'https'])}\z/
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
raise UriService::InvalidOptsError, "Must supply opts['local_uri_base'] to initialize method." if opts['local_uri_base'].nil?
|
10
|
+
raise UriService::InvalidOptsError, "Must supply opts['database'] to initialize method." if opts['database'].nil?
|
11
|
+
raise UriService::InvalidOptsError, "Must supply opts['solr'] to initialize method." if opts['solr'].nil?
|
12
|
+
|
13
|
+
# Set local_uri_base
|
14
|
+
@local_uri_base = opts['local_uri_base']
|
15
|
+
|
16
|
+
# Create DB connection pool
|
17
|
+
@db = Sequel.connect(opts['database'])
|
18
|
+
|
19
|
+
# Create Solr connection pool
|
20
|
+
@rsolr_pool = ConnectionPool.new( size: opts['solr']['pool_size'], timeout: (opts['solr']['pool_timeout'].to_f/1000.to_f) ) { RSolr.connect(:url => opts['solr']['url']) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def disconnect!
|
24
|
+
unless @db.nil?
|
25
|
+
db_reference = @db
|
26
|
+
@db = nil
|
27
|
+
db_reference.disconnect
|
28
|
+
end
|
29
|
+
|
30
|
+
unless @rsolr_pool.nil?
|
31
|
+
rsolr_pool_reference = @rsolr_pool
|
32
|
+
@rsolr_pool = nil
|
33
|
+
rsolr_pool_reference.shutdown{|rsolr|} # connection_pool gem docs say that shutting down is
|
34
|
+
# optional and pool would be garbage collected anyway,
|
35
|
+
# but this doesn't hurt.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def connected?
|
40
|
+
return false if @db.nil? || @rsolr_pool.nil?
|
41
|
+
|
42
|
+
begin
|
43
|
+
self.test_connection
|
44
|
+
return true
|
45
|
+
rescue Sequel::DatabaseConnectionError, Errno::ECONNREFUSED
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_connection
|
51
|
+
@db.test_connection # Raises Sequel::DatabaseConnectionError if connection didn't work
|
52
|
+
@rsolr_pool.with do |rsolr|
|
53
|
+
rsolr.get('admin/ping') # Raises Errno::ECONNREFUSED if connection didn't work
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def required_tables_exist?
|
58
|
+
return (UriService.required_tables - @db.tables).length == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_required_tables
|
62
|
+
current_tables = @db.tables
|
63
|
+
|
64
|
+
unless current_tables.include?(UriService::VOCABULARIES)
|
65
|
+
@db.create_table UriService::VOCABULARIES do |t|
|
66
|
+
primary_key :id
|
67
|
+
String :string_key, size: 255, index: true, unique: true
|
68
|
+
String :display_label, size: 255
|
69
|
+
end
|
70
|
+
else
|
71
|
+
puts 'Skipped creation of table ' + UriService::VOCABULARIES.to_s + ' because it already exists.'
|
72
|
+
end
|
73
|
+
|
74
|
+
unless current_tables.include?(UriService::TERMS)
|
75
|
+
@db.create_table UriService::TERMS do |t|
|
76
|
+
primary_key :id
|
77
|
+
String :vocabulary_string_key, size: 255, index: true
|
78
|
+
String :uri, text: true # This needs to be a text field because utf8 strings cannot be our desired 2000 characters long in MySQL. uri_hash will be used to verify uniqueness.
|
79
|
+
String :uri_hash, fixed: true, size: 64, unique: true
|
80
|
+
String :value, text: true
|
81
|
+
TrueClass :is_local, default: false
|
82
|
+
String :additional_fields, text: true
|
83
|
+
end
|
84
|
+
else
|
85
|
+
puts 'Skipped creation of table ' + UriService::TERMS.to_s + ' because it already exists.'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##################
|
90
|
+
# Create methods #
|
91
|
+
##################
|
92
|
+
|
93
|
+
def create_vocabulary(string_key, display_label)
|
94
|
+
if string_key.to_s == 'all'
|
95
|
+
raise UriService::InvalidVocabularyStringKeyError, 'The value "all" is a reserved word and cannot be used as the string_key value for a vocabulary.'
|
96
|
+
end
|
97
|
+
unless string_key =~ ALPHANUMERIC_UNDERSCORE_KEY_REGEX
|
98
|
+
raise UriService::InvalidVocabularyStringKeyError, "Invalid key (can only include lower case letters, numbers or underscores, but cannot start with an underscore): " + string_key
|
99
|
+
end
|
100
|
+
|
101
|
+
@db.transaction do
|
102
|
+
begin
|
103
|
+
@db[UriService::VOCABULARIES].insert(string_key: string_key, display_label: display_label)
|
104
|
+
rescue Sequel::UniqueConstraintViolation
|
105
|
+
raise UriService::ExistingVocabularyStringKeyError, "A vocabulary already exists with string key: " + string_key
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
# Creates a new term.
|
112
|
+
def create_term(vocabulary_string_key, value, term_uri, additional_fields={})
|
113
|
+
self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, false)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Creates a new local term, auto-generating a URI
|
117
|
+
def create_local_term(vocabulary_string_key, value, additional_fields={})
|
118
|
+
|
119
|
+
# Create a new URI for this local term, using the @local_uri_base
|
120
|
+
term_uri = URI(@local_uri_base)
|
121
|
+
term_uri.path = '/' + File.join(vocabulary_string_key, SecureRandom.uuid) # Generate random UUID for local URI
|
122
|
+
term_uri = term_uri.to_s
|
123
|
+
|
124
|
+
# Getting a duplicate UUID from SecureRandom.uuid is EXTREMELY unlikely, but we'll account for it just in case (by making a few more attempts).
|
125
|
+
5.times {
|
126
|
+
begin
|
127
|
+
self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, true)
|
128
|
+
break
|
129
|
+
rescue UriService::ExistingUriError
|
130
|
+
if defined?(Rails)
|
131
|
+
Rails.logger.error "UriService generated a duplicate random UUID (via SecureRandom.uuid) and will now attempt to create another. This type of problem is EXTREMELY rare."
|
132
|
+
end
|
133
|
+
end
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, is_local)
|
138
|
+
|
139
|
+
#Ensure that vocabulary with vocabulary_string_key exists
|
140
|
+
if self.find_vocabulary(vocabulary_string_key).nil?
|
141
|
+
raise UriService::NonExistentVocabularyError, "There is no vocabulary with string key: " + vocabulary_string_key
|
142
|
+
end
|
143
|
+
unless term_uri =~ VALID_URI_REGEX
|
144
|
+
raise UriService::InvalidUriError, "Invalid URI supplied: #{term_uri}, with result #{(VALID_URI_REGEX.match(term_uri)).to_s}"
|
145
|
+
end
|
146
|
+
validate_additional_fields(additional_fields) # This method call raises an error if an invalid additional_field key is supplied
|
147
|
+
|
148
|
+
@db.transaction do
|
149
|
+
begin
|
150
|
+
@db[UriService::TERMS].insert(
|
151
|
+
is_local: is_local,
|
152
|
+
uri: term_uri,
|
153
|
+
uri_hash: Digest::SHA256.hexdigest(term_uri),
|
154
|
+
value: value,
|
155
|
+
vocabulary_string_key: vocabulary_string_key,
|
156
|
+
additional_fields: JSON.generate(additional_fields)
|
157
|
+
)
|
158
|
+
|
159
|
+
self.send_term_to_solr(@db[UriService::TERMS].where(uri: term_uri).first)
|
160
|
+
|
161
|
+
rescue Sequel::UniqueConstraintViolation
|
162
|
+
raise UriService::ExistingUriError, "A term already exists with uri: " + term_uri + " (conflict found via uri_hash check)"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
def term_db_row_to_solr_doc(term_row_data_from_db)
|
169
|
+
doc = {}
|
170
|
+
doc['uri'] = term_row_data_from_db[:uri]
|
171
|
+
doc['value'] = term_row_data_from_db[:value]
|
172
|
+
doc['is_local_bsi'] = term_row_data_from_db[:is_local]
|
173
|
+
doc['vocabulary_string_key_ssi'] = term_row_data_from_db[:vocabulary_string_key]
|
174
|
+
JSON.parse(term_row_data_from_db[:additional_fields]).each do |key, val|
|
175
|
+
doc[key + '_ssi'] = val
|
176
|
+
end
|
177
|
+
|
178
|
+
return doc
|
179
|
+
end
|
180
|
+
|
181
|
+
# Index the DB row term data into solr
|
182
|
+
def send_term_to_solr(term_row_data_from_db, commit=true)
|
183
|
+
|
184
|
+
@rsolr_pool.with do |rsolr|
|
185
|
+
rsolr.add(term_db_row_to_solr_doc(term_row_data_from_db))
|
186
|
+
rsolr.commit if commit
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Validates additional_fields and verifies that no reserved words are supplied
|
191
|
+
def validate_additional_fields(additional_fields)
|
192
|
+
reserved_keys = ['is_local', 'uri', 'value', 'vocabulary_string_key']
|
193
|
+
additional_fields.each do |key, value|
|
194
|
+
if reserved_keys.include?(key.to_s)
|
195
|
+
raise UriService::InvalidAdditionalFieldKeyError, "Cannot supply the key \"#{key.to_s}\" as an additional field because it is a reserved key."
|
196
|
+
end
|
197
|
+
unless key =~ ALPHANUMERIC_UNDERSCORE_KEY_REGEX
|
198
|
+
raise UriService::InvalidAdditionalFieldKeyError, "Invalid key (can only include lower case letters, numbers or underscores, but cannot start with an underscore): " + key
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
################
|
204
|
+
# Find methods #
|
205
|
+
################
|
206
|
+
|
207
|
+
def find_vocabulary(vocabulary_string_key)
|
208
|
+
@db[UriService::VOCABULARIES].where(string_key: vocabulary_string_key).first
|
209
|
+
end
|
210
|
+
|
211
|
+
def find_term_by_uri(uri)
|
212
|
+
UriService.client.rsolr_pool.with do |rsolr|
|
213
|
+
response = rsolr.get('select', params: { :q => '*:*', :fq => 'uri:' + UriService.solr_escape(uri) })
|
214
|
+
if response['response']['numFound'] == 1
|
215
|
+
return term_solr_doc_to_term_hash(response['response']['docs'].first)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
return nil
|
219
|
+
end
|
220
|
+
|
221
|
+
def term_solr_doc_to_term_hash(term_solr_doc)
|
222
|
+
term_hash = {}
|
223
|
+
term_solr_doc.each do |key, value|
|
224
|
+
next if ['_version_', 'timestamp', 'score'].include?(key) # Skip certain automatically added fields that we don't care about
|
225
|
+
term_hash[key.gsub(/_[^_]+$/, '')] = value # Remove trailing '_si', '_bi', etc. if present
|
226
|
+
end
|
227
|
+
return term_hash
|
228
|
+
end
|
229
|
+
|
230
|
+
def find_terms_by_query(vocabulary_string_key, value_query)
|
231
|
+
|
232
|
+
return [] if value_query.empty?
|
233
|
+
|
234
|
+
terms_to_return = []
|
235
|
+
UriService.client.rsolr_pool.with do |rsolr|
|
236
|
+
|
237
|
+
solr_params = {
|
238
|
+
:q => UriService.solr_escape(value_query),
|
239
|
+
:fq => 'vocabulary_string_key_ssi:' + UriService.solr_escape(vocabulary_string_key)
|
240
|
+
}
|
241
|
+
|
242
|
+
response = rsolr.get('suggest', params: solr_params)
|
243
|
+
if response['response']['numFound'] > 0
|
244
|
+
response['response']['docs'].each do |doc|
|
245
|
+
terms_to_return << term_solr_doc_to_term_hash(doc)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
return terms_to_return
|
250
|
+
end
|
251
|
+
|
252
|
+
##################
|
253
|
+
# Delete methods #
|
254
|
+
##################
|
255
|
+
|
256
|
+
def delete_vocabulary(vocabulary_string_key)
|
257
|
+
@db[UriService::VOCABULARIES].where(string_key: vocabulary_string_key).delete
|
258
|
+
end
|
259
|
+
|
260
|
+
def delete_term(term_uri, commit=true)
|
261
|
+
@db.transaction do
|
262
|
+
@db[UriService::TERMS].where(uri: term_uri).delete
|
263
|
+
@rsolr_pool.with do |rsolr|
|
264
|
+
rsolr.delete_by_query('uri:' + UriService.solr_escape(term_uri))
|
265
|
+
rsolr.commit if commit
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
##################
|
271
|
+
# Update methods #
|
272
|
+
##################
|
273
|
+
|
274
|
+
def update_vocabulary(string_key, new_display_label)
|
275
|
+
dataset = @db[UriService::VOCABULARIES].where(string_key: string_key)
|
276
|
+
raise UriService::NonExistentVocabularyError, "No vocabulary found with string_key: " + string_key if dataset.count == 0
|
277
|
+
|
278
|
+
@db.transaction do
|
279
|
+
dataset.update(display_label: new_display_label)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def update_term_value(term_uri, value)
|
284
|
+
self.update_term_impl(term_uri, value, {}, true)
|
285
|
+
end
|
286
|
+
|
287
|
+
def update_term_additional_fields(term_uri, additional_fields, merge=false)
|
288
|
+
self.update_term_impl(term_uri, nil, additional_fields, merge)
|
289
|
+
end
|
290
|
+
|
291
|
+
def update_term_impl(term_uri, value, additional_fields, merge_additional_fields)
|
292
|
+
|
293
|
+
dataset = @db[UriService::TERMS].where(uri: term_uri)
|
294
|
+
raise UriService::NonExistentUriError, "No term found with uri: " + term_uri if dataset.count == 0
|
295
|
+
validate_additional_fields(additional_fields)
|
296
|
+
|
297
|
+
term = dataset.first
|
298
|
+
term_additional_fields = term['additional_fields'].nil? ? {} : JSON.parse(term['additional_fields'])
|
299
|
+
|
300
|
+
if merge_additional_fields
|
301
|
+
term_additional_fields.merge!(additional_fields)
|
302
|
+
term_additional_fields.delete_if { |k, v| v.nil? } # Delete nil values. This is a way to clear data in additional_fields.
|
303
|
+
else
|
304
|
+
term_additional_fields = additional_fields
|
305
|
+
end
|
306
|
+
|
307
|
+
new_data = {}
|
308
|
+
new_data[:value] = value unless value.nil?
|
309
|
+
new_data[:additional_fields] = JSON.generate(term_additional_fields)
|
310
|
+
|
311
|
+
@db.transaction do
|
312
|
+
dataset.update(new_data)
|
313
|
+
self.send_term_to_solr(@db[UriService::TERMS].where(uri: term_uri).first)
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
class UriService::InvalidAdditionalFieldKeyError < StandardError;end
|
321
|
+
class UriService::InvalidVocabularyStringKeyError < StandardError;end
|
322
|
+
class UriService::InvalidOptsError < StandardError;end
|
323
|
+
class UriService::InvalidUriError < StandardError;end
|
324
|
+
class UriService::ExistingUriError < StandardError;end
|
325
|
+
class UriService::ExistingVocabularyStringKeyError < StandardError;end
|
326
|
+
class UriService::NonExistentUriError < StandardError;end
|
327
|
+
class UriService::NonExistentVocabularyError < StandardError;end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class UriService::Railtie < Rails::Railtie
|
2
|
+
initializer "uri_service_railtie.configure_rails_initialization" do
|
3
|
+
UriService::init(YAML.load_file("#{Rails.root.to_s}/config/uri_service.yml")[Rails.env])
|
4
|
+
UriService.client.test_connection
|
5
|
+
end
|
6
|
+
|
7
|
+
rake_tasks do
|
8
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../tasks/**/*.rake')).each do |rakefile|
|
9
|
+
load rakefile unless rakefile.end_with?('ci.rake') # The ci rakefile has require statements that
|
10
|
+
# are only development dependencies, so that
|
11
|
+
# file shouldn't be pulled into a Rails app.
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/uri_service.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'connection_pool'
|
3
|
+
require 'rsolr'
|
4
|
+
require 'sequel'
|
5
|
+
require 'uri'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
module UriService
|
9
|
+
|
10
|
+
# Constants
|
11
|
+
VOCABULARY = :vocabulary
|
12
|
+
VOCABULARIES = :vocabularies
|
13
|
+
TERM = :term
|
14
|
+
TERMS = :terms
|
15
|
+
|
16
|
+
# Initialize the main instance of UriService::Client
|
17
|
+
# opts format: { 'local_uri_base' => 'http://id.example.com/term/', 'solr' => {...solr config...}, 'database' => {...database config...} }
|
18
|
+
def self.init(opts)
|
19
|
+
if @client && @client.connected?
|
20
|
+
@client.disconnect!
|
21
|
+
end
|
22
|
+
|
23
|
+
@client = UriService::Client.new(opts)
|
24
|
+
@client.test_connection
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.client
|
28
|
+
return @client
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.version
|
32
|
+
return UriService::VERSION
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.required_tables
|
36
|
+
return [UriService::VOCABULARIES, UriService::TERMS]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Wrapper around escape method for different versions of RSolr
|
40
|
+
def self.solr_escape(str)
|
41
|
+
if RSolr.respond_to?(:solr_escape)
|
42
|
+
return RSolr.solr_escape(str) # Newer method
|
43
|
+
else
|
44
|
+
return RSolr.escape(str) # Fall back to older method
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
require "uri_service/version"
|
51
|
+
require "uri_service/client"
|
52
|
+
|
53
|
+
require 'uri_service/railtie' if defined?(Rails)
|
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uri_service
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric O'Hanlon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rsolr
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sequel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.26.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.26.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: connection_pool
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rdf
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.1'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: jettywrapper
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sqlite3
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: mysql2
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.3.18
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.3.18
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: solr_wrapper
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: A service for registering local URIs and performing both local and remote
|
168
|
+
URI lookups.
|
169
|
+
email: elo2112@columbia.edu
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- README.md
|
175
|
+
- lib/uri_service.rb
|
176
|
+
- lib/uri_service/client.rb
|
177
|
+
- lib/uri_service/railtie.rb
|
178
|
+
- lib/uri_service/version.rb
|
179
|
+
homepage: https://github.com/cul/uri_service
|
180
|
+
licenses:
|
181
|
+
- MIT
|
182
|
+
metadata: {}
|
183
|
+
post_install_message:
|
184
|
+
rdoc_options: []
|
185
|
+
require_paths:
|
186
|
+
- lib
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - ">="
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
requirements: []
|
198
|
+
rubyforge_project:
|
199
|
+
rubygems_version: 2.4.3
|
200
|
+
signing_key:
|
201
|
+
specification_version: 4
|
202
|
+
summary: A service for registering local URIs and performing both local and remote
|
203
|
+
URI lookups.
|
204
|
+
test_files: []
|
205
|
+
has_rdoc:
|