uri_service 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ module UriService
2
+
3
+ VERSION = '0.1.0'
4
+
5
+ def self.version
6
+ VERSION
7
+ end
8
+
9
+ end
@@ -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: