govuk-registers-api-client 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 959a3fe6555fd80a48da2138aadaa41877e2c3c406a6688fac9f994805f4500d
4
- data.tar.gz: 1a7499afdcfa437839871d149d133b3d8f3ea428539c009ed1632188373c7d0a
3
+ metadata.gz: ab771bc838779644bbd8763beea0c5a4c1138a298011af39a4ac4121068c3d40
4
+ data.tar.gz: ae34e34c99dc13883a2865393b54aa386fa04cf0174f835bfd60fa1dae582699
5
5
  SHA512:
6
- metadata.gz: 29934a7dd3ebea408e6d2507d9c05d97c005a4d7a1f338266cf9399a2c6749fe261a9e3b68bdf07fc8a2bf13ea6b928f4775bc93fadbbc3dcdad5651c4ecd109
7
- data.tar.gz: b6eaec466707feb72a7a2a78182542d001729d76115c3d252e6cf4c93041d2a2822e9c084a771083cbb877e2956640c1788b8780d90acc03b9cfb2f06b384aa1
6
+ metadata.gz: cbe860777cef29b1ad7ba40fd47dcc3b10319eff06623637ad03deb83fd8e6771dd60c694acd92642c676270b78c9cfd631e4bda8e62d415bc232d858ea998ee
7
+ data.tar.gz: 735d24e0805542891a4cd14a0504b40455ad8e2d77921e267775abefbf07bb4b4d284c78146ee239b608e182e5f5ce569b5b09ec9fb1eb5b697d560c142257fe
@@ -0,0 +1,5 @@
1
+ ### What did you do? (steps to reproduce)
2
+
3
+ ### What happened? (actual results)
4
+
5
+ ### What should have happened? (expected results)
@@ -0,0 +1,7 @@
1
+ ### Context
2
+
3
+
4
+ ### Changes proposed in this pull request
5
+
6
+
7
+ ### Guidance to review
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.5.0
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.0
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.5.2)
5
+ public_suffix (>= 2.0.2, < 4.0)
6
+ crack (0.4.3)
7
+ safe_yaml (~> 1.0.0)
8
+ diff-lcs (1.3)
9
+ domain_name (0.5.20170404)
10
+ unf (>= 0.0.5, < 1.0.0)
11
+ hashdiff (0.3.7)
12
+ http-cookie (1.0.3)
13
+ domain_name (~> 0.5)
14
+ mime-types (3.1)
15
+ mime-types-data (~> 3.2015)
16
+ mime-types-data (3.2016.0521)
17
+ netrc (0.11.0)
18
+ public_suffix (3.0.1)
19
+ rake (12.3.0)
20
+ rest-client (2.0.2)
21
+ http-cookie (>= 1.0.2, < 2.0)
22
+ mime-types (>= 1.16, < 4.0)
23
+ netrc (~> 0.8)
24
+ rspec (3.7.0)
25
+ rspec-core (~> 3.7.0)
26
+ rspec-expectations (~> 3.7.0)
27
+ rspec-mocks (~> 3.7.0)
28
+ rspec-core (3.7.0)
29
+ rspec-support (~> 3.7.0)
30
+ rspec-expectations (3.7.0)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.7.0)
33
+ rspec-mocks (3.7.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.7.0)
36
+ rspec-support (3.7.0)
37
+ safe_yaml (1.0.4)
38
+ unf (0.1.4)
39
+ unf_ext
40
+ unf_ext (0.0.7.4)
41
+ webmock (3.1.1)
42
+ addressable (>= 2.3.6)
43
+ crack (>= 0.3.2)
44
+ hashdiff
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ rake
51
+ rest-client (>= 2.0.1)
52
+ rspec (~> 3.7)
53
+ webmock
54
+
55
+ BUNDLED WITH
56
+ 1.16.0
data/README.md CHANGED
@@ -24,7 +24,7 @@ Registers are authoritative lists of information. The data is owned by [custodia
24
24
 
25
25
  In your Gemfile, add:
26
26
  ```
27
- gem 'govuk-register-api-client'
27
+ gem 'govuk-registers-api-client'
28
28
  ```
29
29
 
30
30
  ## Get started
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = %w(--format documentation --colour)
10
+ end
11
+
12
+ task default: :spec
13
+
14
+ require 'rdoc/task'
15
+ RDoc::Task.new(:rdoc) do |rd|
16
+ rd.main = 'README.md'
17
+ rd.rdoc_files.include('README.md', 'lib/**/*.rb')
18
+ rd.rdoc_dir = 'rdoc'
19
+ end
20
+
21
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: govuk-registers-api-client 1.0.1 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "govuk-registers-api-client".freeze
6
+ s.version = File.read('.version').chomp
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib".freeze]
10
+ s.authors = ["GOV.UK Registers Team (Government Digital Service)".freeze]
11
+ s.date = "2017-11-06"
12
+ s.email = "registers ~@nospam@~ digital.cabinet-office.gov.uk".freeze
13
+ s.extra_rdoc_files = ["README.md".freeze]
14
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
15
+ f.match(%r{^(test|spec|features)/})
16
+ end
17
+ s.homepage = "https://github.com/openregister/govuk-registers-api-client".freeze
18
+ s.licenses = ["MIT".freeze]
19
+ s.rdoc_options = ["--main".freeze, "README.md".freeze]
20
+ s.rubygems_version = "2.6.12".freeze
21
+ s.summary = "Client library for GOV.UK Registers".freeze
22
+
23
+ if s.respond_to? :specification_version then
24
+ s.specification_version = 4
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency(%q<rest-client>.freeze, ["~> 2"])
27
+ else
28
+ s.add_dependency(%q<rest-client>.freeze, ["~> 2"])
29
+ end
30
+ else
31
+ s.add_dependency(%q<rest-client>.freeze, ["~> 2"])
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module DataStore
2
+ def add_item(item)
3
+ end
4
+
5
+ def append_entry(entry)
6
+ end
7
+
8
+ def get_item(item_hash)
9
+ end
10
+
11
+ def get_items
12
+ end
13
+
14
+ def get_entry(entry_type, entry_number)
15
+ end
16
+
17
+ def get_entries(entry_type)
18
+ end
19
+
20
+ def get_record(entry_type, key)
21
+ end
22
+
23
+ def get_records(entry_type)
24
+ end
25
+
26
+ def get_latest_entry_number(entry_type)
27
+ end
28
+
29
+ def get_root_hash
30
+ end
31
+
32
+ def set_root_hash(root_hash)
33
+ end
34
+
35
+ def after_load
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ module RegistersClient
2
+ class Entry
3
+ def initialize(rsf_line, entry_number, entry_type)
4
+ @rsf_line = rsf_line
5
+ @entry_number = entry_number
6
+ @entry_type = entry_type
7
+
8
+ @parsed_entry = nil
9
+ end
10
+
11
+ def entry_number
12
+ @entry_number
13
+ end
14
+
15
+ def key
16
+ get_entry[:key]
17
+ end
18
+
19
+ def timestamp
20
+ get_entry[:timestamp]
21
+ end
22
+
23
+ def item_hash
24
+ get_entry[:item_hash]
25
+ end
26
+
27
+ def entry_type
28
+ @entry_type
29
+ end
30
+
31
+ def value
32
+ get_entry
33
+ end
34
+
35
+ private
36
+
37
+ def get_entry
38
+ @parsed_entry ||= parse_entry
39
+ @parsed_entry
40
+ end
41
+
42
+ def parse_entry
43
+ params = @rsf_line.split("\t")
44
+
45
+ # Deallocate rsf line
46
+ @rsf_line = nil
47
+
48
+ { key: params[2], timestamp: params[3], item_hash: params[4] }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class EntryCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |entry|
12
+ yield entry
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ class InvalidHashValueError < StandardError
2
+ def initialize(msg='Value must be a sha-256 hash string prefixed with "sha-256:"')
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class InvalidRegisterError < StandardError
2
+ end
@@ -0,0 +1,106 @@
1
+ require 'data_store'
2
+ require 'date'
3
+ require 'record_collection'
4
+ require 'record_map_collection'
5
+ require 'entry_collection'
6
+ require 'item_collection'
7
+ require 'entry'
8
+ require 'item'
9
+ require 'record'
10
+
11
+ module RegistersClient
12
+ class InMemoryDataStore
13
+ include DataStore
14
+
15
+ EMPTY_ROOT_HASH = 'sha-256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
16
+
17
+ def initialize(config_options)
18
+ @config_options = config_options
19
+
20
+ @data = {
21
+ records: { user: {}, system: {} },
22
+ entries: { user: [], system: [] },
23
+ items: {},
24
+ root_hash: EMPTY_ROOT_HASH
25
+ }
26
+
27
+ end
28
+
29
+ def add_item(item)
30
+ @data[:items][item.hash.to_s] = item
31
+ end
32
+
33
+ def append_entry(entry)
34
+ entry_type = entry.entry_type == 'user' ? :user : :system
35
+
36
+ @data[:entries][entry_type] << entry
37
+
38
+ if !@data[:records][entry_type].key?(entry.key)
39
+ @data[:records][entry_type][entry.key] = []
40
+ end
41
+
42
+ @data[:records][entry_type][entry.key] << entry.entry_number
43
+ end
44
+
45
+ def get_item(item_hash)
46
+ @data[:items].has_key?(item_hash) ? @data[:items][item_hash] : nil
47
+ end
48
+
49
+ def get_items
50
+ ItemCollection.new(@data[:items].values)
51
+ end
52
+
53
+ def get_entry(entry_type, entry_number)
54
+ if (entry_number < 1 || entry_number > @data[:entries][entry_type].count)
55
+ nil
56
+ else
57
+ @data[:entries][entry_type][entry_number - 1]
58
+ end
59
+ end
60
+
61
+ def get_record(entry_type, key)
62
+ if (@data[:records][entry_type].has_key?(key))
63
+ record_entry_numbers = @data[:records][entry_type][key]
64
+ entry = get_data[:entries][entry_type][record_entry_numbers.last - 1]
65
+
66
+ Record.new(entry, get_data[:items][entry.item_hash])
67
+ end
68
+ end
69
+
70
+ def get_entries(entry_type)
71
+ EntryCollection.new(get_data[:entries][entry_type], @config_options.fetch(:page_size))
72
+ end
73
+
74
+ def get_records(entry_type)
75
+ RecordCollection.new(get_data[:records][entry_type].map do |_k, record_entry_numbers|
76
+ entry = get_data[:entries][entry_type][record_entry_numbers.last - 1]
77
+ item = get_data[:items][entry.item_hash]
78
+
79
+ Record.new(entry, item)
80
+ end, @config_options.fetch(:page_size))
81
+ end
82
+
83
+ def get_latest_entry_number(entry_type)
84
+ entries = @data[:entries][entry_type]
85
+ entry = entries.any? ? entries.last : nil
86
+ entry.nil? ? 0 : entry.entry_number
87
+ end
88
+
89
+ def get_root_hash
90
+ @data[:root_hash]
91
+ end
92
+
93
+ def set_root_hash(root_hash)
94
+ @data[:root_hash] = root_hash
95
+ end
96
+
97
+ def after_load
98
+ end
99
+
100
+ private
101
+
102
+ def get_data
103
+ @data
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+
3
+ module RegistersClient
4
+ class Item
5
+ def initialize(rsf_line)
6
+ @item_json = rsf_line.split("\t")[1]
7
+ @item_hash = 'sha-256:' + Digest::SHA256.hexdigest(@item_json)
8
+
9
+ @parsed_item = nil
10
+ end
11
+
12
+ def hash
13
+ @item_hash
14
+ end
15
+
16
+ def value
17
+ get_item
18
+ end
19
+
20
+ def has_end_date
21
+ if !@item_json.nil?
22
+ !@item_json.index("end-date").nil?
23
+ else
24
+ !@parsed_item['end-date'].nil?
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def get_item
31
+ if @parsed_item.nil?
32
+ @parsed_item = parse_item
33
+ end
34
+
35
+ @parsed_item
36
+ end
37
+
38
+ def parse_item
39
+ parsed_item = JSON.parse(@item_json)
40
+
41
+ # Deallocate item JSON
42
+ @item_json = nil
43
+
44
+ parsed_item
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class ItemCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |item|
12
+ yield item
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module RegistersClient
2
+ class Record
3
+ def initialize(entry, item)
4
+ @entry = entry
5
+ @item = item
6
+ end
7
+
8
+ def entry
9
+ @entry
10
+ end
11
+
12
+ def item
13
+ @item
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class RecordCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |record|
12
+ yield record
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class RecordMapCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |key,val|
12
+ result = {key: key, records: val}
13
+ yield result
14
+ end
15
+ end
16
+
17
+ def get_records_for_key(key)
18
+ @data.fetch(key)
19
+ end
20
+
21
+ def paginator
22
+ @data.each_slice(@page_size)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,177 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+ require 'exceptions/invalid_register_error'
4
+ require 'exceptions/invalid_hash_value_error'
5
+
6
+ module RegistersClient
7
+ class RegisterClient
8
+ def initialize(register_url, data_store, page_size)
9
+ @register_url = register_url
10
+ @data_store = data_store
11
+ @page_size = page_size
12
+
13
+ refresh_data
14
+ end
15
+
16
+ def get_item(item_hash)
17
+ @data_store.get_item(item_hash)
18
+ end
19
+
20
+ def get_items
21
+ @data_store.get_items
22
+ end
23
+
24
+ def get_entry(entry_number)
25
+ @data_store.get_entry(:user, entry_number)
26
+ end
27
+
28
+ def get_entries(since_entry_number = 0)
29
+ EntryCollection.new(get_entries_subset_for_entry_type(since_entry_number, :user), @page_size)
30
+ end
31
+
32
+ def get_record(key)
33
+ @data_store.get_record(:user, key)
34
+ end
35
+
36
+ def get_records
37
+ @data_store.get_records(:user)
38
+ end
39
+
40
+ def get_metadata_records
41
+ @data_store.get_records(:system)
42
+ end
43
+
44
+ def get_field_definitions
45
+ ordered_fields = get_register_definition.item.value['fields']
46
+ ordered_records = ordered_fields.map { |f| get_metadata_records.find { |record| record.entry.key == "field:#{f}" } }
47
+ @field_definitions ||= RecordCollection.new(ordered_records, @page_size)
48
+ @field_definitions
49
+ end
50
+
51
+ def get_register_definition
52
+ get_metadata_records.select { |record| record.entry.key.start_with?('register:') }.first
53
+ end
54
+
55
+ def get_custodian
56
+ get_metadata_records.select { |record| record.entry.key == 'custodian'}.first
57
+ end
58
+
59
+ def get_records_with_history(since_entry_number = 0)
60
+ records_with_history = get_records_with_history_for_entry_type(since_entry_number, :user)
61
+
62
+ RecordMapCollection.new(records_with_history, @page_size)
63
+ end
64
+
65
+ def get_metadata_records_with_history(since_entry_number = 0)
66
+ metadata_records_with_history = get_records_with_history_for_entry_type(since_entry_number, :system)
67
+
68
+ RecordMapCollection.new(metadata_records_with_history, @page_size)
69
+ end
70
+
71
+ def get_current_records
72
+ RecordCollection.new(get_records.select { |record| !record.item.has_end_date }, @page_size)
73
+ end
74
+
75
+ def get_expired_records
76
+ RecordCollection.new(get_records.select { |record| record.item.has_end_date }, @page_size)
77
+ end
78
+
79
+ def get_root_hash
80
+ root_hash = @data_store.get_root_hash
81
+ if !is_valid_hash_value(root_hash)
82
+ raise InvalidHashValueError
83
+ end
84
+ return root_hash
85
+ end
86
+
87
+ def refresh_data
88
+ user_entry_number = @data_store.get_latest_entry_number(:user)
89
+ rsf = download_rsf(user_entry_number)
90
+
91
+ validate_register_integrity(rsf, user_entry_number)
92
+ update_data_from_rsf(rsf, user_entry_number, @data_store)
93
+ end
94
+
95
+ private
96
+
97
+ def get_entries_subset_for_entry_type(since_entry_number, entry_type)
98
+ start_index = !since_entry_number.nil? && since_entry_number > 0 ? since_entry_number : 0
99
+ current_entry_number = @data_store.get_latest_entry_number(entry_type)
100
+ length = current_entry_number - start_index
101
+
102
+ @data_store.get_entries(entry_type).to_a.slice(start_index, length)
103
+ end
104
+
105
+ def get_records_with_history_for_entry_type(since_entry_number, entry_type)
106
+ records_with_history = {}
107
+
108
+ get_entries_subset_for_entry_type(since_entry_number, entry_type).each do |entry|
109
+ if (!records_with_history.key?(entry.key))
110
+ records_with_history[entry.key] = []
111
+ end
112
+
113
+ item = @data_store.get_item(entry.item_hash)
114
+ records_with_history[entry.key] << Record.new(entry, item)
115
+ end
116
+
117
+ records_with_history
118
+ end
119
+
120
+ def get_register_proof
121
+ @register_proof = JSON.parse(register_http_request('proof/register/merkle:sha-256'))
122
+ end
123
+
124
+ def validate_register_integrity(rsf, current_entry_number)
125
+ rsf_lines = rsf.split("\n")
126
+ expected_root_hash = rsf_lines[0].split("\t")[1]
127
+ local_root_hash = @data_store.get_root_hash
128
+
129
+ if (local_root_hash != expected_root_hash)
130
+ raise InvalidRegisterError, 'Register has been reloaded with different data - root hashes do not match'
131
+ end
132
+ end
133
+
134
+ def download_rsf(start_entry_number)
135
+ register_http_request("download-rsf/#{start_entry_number}")
136
+ end
137
+
138
+ def update_data_from_rsf(rsf, user_entry_number, data_store)
139
+ system_entry_number = @data_store.get_latest_entry_number(:system)
140
+
141
+ rsf.each_line do |line|
142
+ line.slice!("\n")
143
+ params = line.split("\t")
144
+ command = params[0]
145
+
146
+ if command == 'add-item'
147
+ data_store.add_item(RegistersClient::Item.new(line))
148
+ elsif command == 'append-entry'
149
+ if params[1] == 'user'
150
+ user_entry_number += 1
151
+
152
+ entry = Entry.new(line, user_entry_number, params[1])
153
+ else
154
+ system_entry_number += 1
155
+
156
+ entry = Entry.new(line, system_entry_number, params[1])
157
+ end
158
+
159
+ data_store.append_entry(entry)
160
+ elsif command == 'assert-root-hash'
161
+ root_hash = line.split("\t")[1]
162
+ data_store.set_root_hash(root_hash)
163
+ end
164
+ end
165
+
166
+ data_store.after_load
167
+ end
168
+
169
+ def register_http_request(path)
170
+ RestClient.get(@register_url.merge(path).to_s)
171
+ end
172
+
173
+ def is_valid_hash_value(hash_value)
174
+ (/sha-256:+\h{64}/) === hash_value
175
+ end
176
+ end
177
+ end
@@ -2,7 +2,7 @@ require 'register_client'
2
2
  require 'in_memory_data_store'
3
3
 
4
4
  module RegistersClient
5
- VERSION = '1.0.0'
5
+ VERSION = '1.0.1'
6
6
  class RegisterClientManager
7
7
  def initialize(config_options = {})
8
8
  @config_options = defaults.merge(config_options)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk-registers-api-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Registers Team (Government Digital Service)
@@ -31,9 +31,30 @@ extensions: []
31
31
  extra_rdoc_files:
32
32
  - README.md
33
33
  files:
34
+ - ".github/ISSUE_TEMPLATE.md"
35
+ - ".github/PULL_REQUEST_TEMPLATE.md"
36
+ - ".rspec"
37
+ - ".ruby-version"
38
+ - ".travis.yml"
39
+ - ".version"
34
40
  - Gemfile
41
+ - Gemfile.lock
35
42
  - LICENSE
36
43
  - README.md
44
+ - Rakefile
45
+ - govuk-registers-api-client.gemspec
46
+ - lib/data_store.rb
47
+ - lib/entry.rb
48
+ - lib/entry_collection.rb
49
+ - lib/exceptions/invalid_hash_value_error.rb
50
+ - lib/exceptions/invalid_register_error.rb
51
+ - lib/in_memory_data_store.rb
52
+ - lib/item.rb
53
+ - lib/item_collection.rb
54
+ - lib/record.rb
55
+ - lib/record_collection.rb
56
+ - lib/record_map_collection.rb
57
+ - lib/register_client.rb
37
58
  - lib/register_client_manager.rb
38
59
  homepage: https://github.com/openregister/govuk-registers-api-client
39
60
  licenses: