commons-integrity 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9e522abef1caaeb6fd5b7fa1839bb8428c404d5
4
+ data.tar.gz: 3f90f3f405be99d6469f1833fccc2e4adaa1117d
5
+ SHA512:
6
+ metadata.gz: 1c7cd33fe3d7844c6e90e5a28085cfcdb5cff2526a62a4be27e16a15b076e21dc8a355eb77e363b3fd4f4179be95012b31e10ea51822567754d01f1a061365a4
7
+ data.tar.gz: d154d5216ab6ce881ee07b42b27d6baaa0c00a3fb00d7d145433bc1a9e752b24a0474e485f6b73461925f501005349510c0408d38aa29fbee520db360abc34e5
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'Vagrantfile'
4
+ - 'vendor/**/*'
5
+ TargetRubyVersion: 2.4
6
+
7
+ inherit_from:
8
+ - https://raw.githubusercontent.com/everypolitician/everypolitician-data/master/.rubocop_base.yml
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm: 2.4
4
+ script:
5
+ - bundle exec rake
6
+ - bash <(curl -fsSL https://github.com/everypolitician/ensure-regression-tests/raw/master/ensure-regression-tests)
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ ruby '~> 2.4.0'
6
+
7
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
+
9
+ # Specify your gem's dependencies in commons-builder.gemspec
10
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,97 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ commons-integrity (0.1)
5
+ activesupport
6
+ json
7
+ require_all
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (5.2.0)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ addressable (2.5.2)
18
+ public_suffix (>= 2.0.2, < 4.0)
19
+ ast (2.4.0)
20
+ axiom-types (0.1.1)
21
+ descendants_tracker (~> 0.0.4)
22
+ ice_nine (~> 0.11.0)
23
+ thread_safe (~> 0.3, >= 0.3.1)
24
+ codeclimate-engine-rb (0.4.1)
25
+ virtus (~> 1.0)
26
+ coderay (1.1.2)
27
+ coercible (1.0.0)
28
+ descendants_tracker (~> 0.0.1)
29
+ concurrent-ruby (1.0.5)
30
+ crack (0.4.3)
31
+ safe_yaml (~> 1.0.0)
32
+ descendants_tracker (0.0.4)
33
+ thread_safe (~> 0.3, >= 0.3.1)
34
+ equalizer (0.0.11)
35
+ hashdiff (0.3.7)
36
+ i18n (1.0.1)
37
+ concurrent-ruby (~> 1.0)
38
+ ice_nine (0.11.2)
39
+ json (2.1.0)
40
+ method_source (0.9.0)
41
+ minitest (5.11.3)
42
+ parallel (1.12.1)
43
+ parser (2.5.1.0)
44
+ ast (~> 2.4.0)
45
+ powerpack (0.1.1)
46
+ pry (0.11.3)
47
+ coderay (~> 1.1.0)
48
+ method_source (~> 0.9.0)
49
+ public_suffix (3.0.2)
50
+ rainbow (3.0.0)
51
+ rake (10.5.0)
52
+ reek (4.8.0)
53
+ codeclimate-engine-rb (~> 0.4.0)
54
+ parser (>= 2.5.0.0, < 2.6)
55
+ rainbow (~> 3.0)
56
+ require_all (2.0.0)
57
+ rubocop (0.55.0)
58
+ parallel (~> 1.10)
59
+ parser (>= 2.5)
60
+ powerpack (~> 0.1)
61
+ rainbow (>= 2.2.2, < 4.0)
62
+ ruby-progressbar (~> 1.7)
63
+ unicode-display_width (~> 1.0, >= 1.0.1)
64
+ ruby-progressbar (1.9.0)
65
+ safe_yaml (1.0.4)
66
+ thread_safe (0.3.6)
67
+ tzinfo (1.2.5)
68
+ thread_safe (~> 0.1)
69
+ unicode-display_width (1.3.2)
70
+ virtus (1.0.5)
71
+ axiom-types (~> 0.1)
72
+ coercible (~> 1.0)
73
+ descendants_tracker (~> 0.0, >= 0.0.3)
74
+ equalizer (~> 0.0, >= 0.0.9)
75
+ webmock (2.3.2)
76
+ addressable (>= 2.3.6)
77
+ crack (>= 0.3.2)
78
+ hashdiff
79
+
80
+ PLATFORMS
81
+ ruby
82
+
83
+ DEPENDENCIES
84
+ bundler (~> 1.16)
85
+ commons-integrity!
86
+ minitest (~> 5.0)
87
+ pry
88
+ rake (~> 10.0)
89
+ reek
90
+ rubocop
91
+ webmock (~> 2.0)
92
+
93
+ RUBY VERSION
94
+ ruby 2.4.1p111
95
+
96
+ BUNDLED WITH
97
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # commons-integrity
2
+
3
+ [![Build Status](https://travis-ci.org/everypolitician/commons-integrity.svg?branch=master)](https://travis-ci.org/everypolitician/commons-integrity)
4
+
5
+ ## Usage
6
+
7
+ ### Installation
8
+
9
+ Add to the `Gemfile` dependencies for a proto-commons- repository:
10
+
11
+ gem 'commons-integrity', :github => 'everypolitician/commons-integrity'
12
+
13
+ ### Configuration
14
+
15
+ Each check you wish to run within your project should be listed in a
16
+ configuration file (by default `.integrity.yml` in the project's root
17
+ directory, though this can also be specified separately.)
18
+
19
+ This will usually specify which file(s) to run against, and any options
20
+ specific to that individual check.
21
+
22
+ For example:
23
+
24
+ ```yaml
25
+ WikidataIdentifiers:
26
+ AppliesTo: 'boundaries/**/*.csv'
27
+ column_name: 'WIKIDATA'
28
+ column_case: 'fixed'
29
+ ```
30
+
31
+ ## Orchestration
32
+
33
+ Currently you need to create your own script to run this, and choose how
34
+ to display the errors. We plan to make both of these much simpler.
35
+
36
+ An example `bin/check` could do something like:
37
+
38
+ ```ruby
39
+ require 'commons/integrity'
40
+
41
+ root = Pathname.new(ARGV.first || '.')
42
+ files = Pathname.glob(root + '**/*')
43
+ errors = files.map { |file| Commons::Integrity::Report.new(file: file).errors }
44
+ puts errors
45
+ ```
46
+
47
+ ## Creating new Checks
48
+
49
+ Add new checks in `lib/commons/integrity/check/` such that they look
50
+ like existing checks.
51
+
52
+ Each should inherit from `Commons::Integrity::Check::Base` and supply
53
+ its own `errors` method.
54
+
55
+ Documentation for the Check should be added in [YARD](https://yardoc.org/)
56
+ format.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+ require 'reek/rake/task'
5
+ require 'rubocop/rake_task'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ t.libs << 'lib'
10
+ t.warning = true
11
+ t.verbose = true
12
+ t.test_files = FileList['test/**/*.rb']
13
+ end
14
+
15
+ RuboCop::RakeTask.new
16
+ Reek::Rake::Task.new
17
+
18
+ task default: %w[test rubocop reek]
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'commons-integrity'
5
+ spec.version = '0.1'
6
+ spec.authors = ['Tony Bowden', 'Alex Dutton']
7
+ spec.email = ['parliaments@mysociety.org']
8
+
9
+ spec.summary = 'Check the integrity of Democratic Commons data'
10
+ spec.homepage = 'https://github.com/everypolitician/commons-integrity'
11
+ spec.license = 'MIT'
12
+
13
+ spec.required_ruby_version = '~> 2.4.0'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = 'bin'
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activesupport'
22
+ spec.add_dependency 'json'
23
+ spec.add_dependency 'require_all'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.16'
26
+ spec.add_development_dependency 'minitest', '~> 5.0'
27
+ spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'reek'
30
+ spec.add_development_dependency 'rubocop'
31
+ spec.add_development_dependency 'webmock', '~> 2.0'
32
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'require_all'
4
+
5
+ require_rel 'integrity/check'
6
+ require_rel 'integrity/report'
7
+
8
+ module Commons
9
+ # This module contains all the library code for checking the
10
+ # integrity of data.
11
+ module Integrity
12
+ end
13
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'csv'
5
+ require 'json'
6
+
7
+ module Commons
8
+ module Integrity
9
+ class Check
10
+ # Ensure we have rich position data for positions associated with boundaries
11
+ #
12
+ # This check should be applied to a
13
+ # boundaries/build/position-data.json file. It'll check
14
+ # that that file has data for every position mentioned in
15
+ # the index.json file in the same directory.
16
+ #
17
+ # == Configuration Options
18
+ class AreaPositionsKnown < Base
19
+ # @return [Array<Error>]
20
+ # Errors will be in the category `:position_id_message`
21
+ def errors
22
+ positions_missing_role_data.map do |position|
23
+ error(
24
+ position_id_message: "#{position} was found in #{boundaries_index_pathname} but not in #{pathname}"
25
+ )
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def positions_missing_role_data
32
+ (position_items_from_index - positions_with_role_data).sort
33
+ end
34
+
35
+ def boundaries_index_pathname
36
+ pathname.dirname.join('index.json')
37
+ end
38
+
39
+ def boundaries_index_data
40
+ @boundaries_index_data ||= JSON.parse(
41
+ boundaries_index_pathname.read,
42
+ symbolize_names: true
43
+ )
44
+ end
45
+
46
+ def position_items_from_index
47
+ Set.new(
48
+ boundaries_index_data.flat_map do |entry|
49
+ BoundaryIndexEntry.new(entry).associated_positions
50
+ end
51
+ )
52
+ end
53
+
54
+ def positions_with_role_data
55
+ Set.new(
56
+ JSON.parse(pathname.read, symbolize_names: true).map do |role|
57
+ role[:role_id]
58
+ end
59
+ )
60
+ end
61
+ end
62
+ end
63
+
64
+ # This encapsulates a top-level entry in the JSON file which
65
+ # acts as an index of the boundary data directories
66
+ class BoundaryIndexEntry
67
+ def initialize(entry_data)
68
+ @entry_data = entry_data
69
+ end
70
+
71
+ def associated_positions
72
+ entry_data[:associations].map { |association| association[:position_item_id] }
73
+ end
74
+
75
+ private
76
+
77
+ attr_reader :entry_data
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commons
4
+ module Integrity
5
+ class Check
6
+ # All other checks should inherit from here, and override `errors`
7
+ class Base
8
+ def initialize(filename, config: nil)
9
+ @filename = filename
10
+ @given_config = config
11
+ end
12
+
13
+ def errors
14
+ []
15
+ end
16
+
17
+ def self.moniker
18
+ name.sub('Commons::Integrity::Check::', '')
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :filename, :given_config
24
+
25
+ # Simple struct to represent any errors raised
26
+ Error = Struct.new(:category, :message, :filename)
27
+
28
+ def error(pair)
29
+ Error.new(*pair.first, filename)
30
+ end
31
+
32
+ def config
33
+ @config ||= @given_config || Config.new
34
+ end
35
+
36
+ def pathname
37
+ @pathname ||= Pathname.new(filename)
38
+ end
39
+
40
+ def my_config
41
+ config.for(self.class.moniker)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'csv'
5
+
6
+ module Commons
7
+ module Integrity
8
+ class Check
9
+ # Check that any values in a "wikidata" column look like valid IDs
10
+ #
11
+ # Given a CSV file with a column of Wikidata identifiers, this
12
+ # Check will ensure that all the values in that column look like
13
+ # valid identifiers (i.e. are Q-numbers). It does *not* check that
14
+ # they are _actually_ valid IDs: only that they are of the correct
15
+ # form.
16
+ #
17
+ # == Configuration Options
18
+ # * column_name: the column containing the IDs (default: "wikidata")
19
+ # * column_case: "fixed" if the column_name must be exactly as specified (default: "any")
20
+ class WikidataIdentifiers < Base
21
+ # @return [Array<Error>]
22
+ # Errors will be in the category `:wikidata_id_format`
23
+ def errors
24
+ problematic_values.map { |val| error(wikidata_id_format: "Invalid wikidata ID: #{val}") }
25
+ end
26
+
27
+ private
28
+
29
+ DEFAULT_COLUMN_NAME = 'wikidata'
30
+ DEFAULT_COLUMN_CASE = 'any'
31
+
32
+ def csv
33
+ @csv ||= CSV.read(pathname, headers: true)
34
+ end
35
+
36
+ def headers
37
+ csv.headers
38
+ end
39
+
40
+ def column_name
41
+ my_config.to_h['column_name'] || DEFAULT_COLUMN_NAME
42
+ end
43
+
44
+ def comparison_type
45
+ my_config.to_h['column_case'] || DEFAULT_COLUMN_CASE
46
+ end
47
+
48
+ def comparison_conversion
49
+ return :to_s if comparison_type == 'fixed'
50
+ :downcase
51
+ end
52
+
53
+ def wikidata_column
54
+ headers.find { |header| header.send(comparison_conversion) == column_name.send(comparison_conversion) }
55
+ end
56
+
57
+ def wikidata_column?
58
+ wikidata_column
59
+ end
60
+
61
+ def wikidata_values
62
+ return [] unless wikidata_column?
63
+ csv.map { |row| row[wikidata_column] }
64
+ end
65
+
66
+ def problematic_values
67
+ wikidata_values.reject { |value| value =~ /^Q[1-9][0-9]*$/ }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Commons
6
+ module Integrity
7
+ # This represents the configuration. For now we only have a single
8
+ # config file, but I expect this will become more complex later, and
9
+ # will likely need split into an abstract `Config` combining
10
+ # multiple `Config::File` objects.
11
+ class Config
12
+ def initialize(supplied_location = nil)
13
+ @supplied_location = supplied_location
14
+ end
15
+
16
+ def for(check)
17
+ yaml[check]
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :supplied_location
23
+
24
+ DEFAULT_LOCATION = Pathname.pwd + '.integrity.yml'
25
+
26
+ def yaml
27
+ return {} unless config_exists?
28
+ @yaml ||= YAML.load_file(pathname)
29
+ end
30
+
31
+ def possible_file_locations
32
+ [supplied_location, DEFAULT_LOCATION]
33
+ end
34
+
35
+ def pathname
36
+ possible_file_locations.compact.find { |file| Pathname.new(file).exist? }
37
+ end
38
+
39
+ def config_exists?
40
+ pathname
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/class/subclasses'
4
+ require 'require_all'
5
+
6
+ require_relative 'config'
7
+ require_rel 'check'
8
+
9
+ module Commons
10
+ module Integrity
11
+ # Collate the errors from all reports applying to a file
12
+ class Report
13
+ def initialize(file:, config: nil)
14
+ @file = Pathname.new(file)
15
+ @config = config
16
+ end
17
+
18
+ def errors
19
+ relevant_checks.flat_map { |check| check.new(file).errors }
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :file, :config
25
+
26
+ ALL_CHECKS = Commons::Integrity::Check::Base.descendants
27
+
28
+ def relevant_checks
29
+ return [] unless config
30
+ ALL_CHECKS.select do |check|
31
+ check_config = config.for(check.moniker)
32
+ if check_config
33
+ pattern = check_config.dig('AppliesTo')
34
+ file.fnmatch pattern if pattern
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: commons-integrity
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Tony Bowden
8
+ - Alex Dutton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-06-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: require_all
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.16'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.16'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pry
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rake
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '10.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '10.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: reek
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rubocop
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: webmock
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '2.0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '2.0'
154
+ description:
155
+ email:
156
+ - parliaments@mysociety.org
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".rubocop.yml"
162
+ - ".travis.yml"
163
+ - Gemfile
164
+ - Gemfile.lock
165
+ - README.md
166
+ - Rakefile
167
+ - commons-integrity.gemspec
168
+ - lib/commons/integrity.rb
169
+ - lib/commons/integrity/check/area_positions_known.rb
170
+ - lib/commons/integrity/check/base.rb
171
+ - lib/commons/integrity/check/wikidata_identifiers.rb
172
+ - lib/commons/integrity/config.rb
173
+ - lib/commons/integrity/report.rb
174
+ homepage: https://github.com/everypolitician/commons-integrity
175
+ licenses:
176
+ - MIT
177
+ metadata: {}
178
+ post_install_message:
179
+ rdoc_options: []
180
+ require_paths:
181
+ - lib
182
+ required_ruby_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: 2.4.0
187
+ required_rubygems_version: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ requirements: []
193
+ rubyforge_project:
194
+ rubygems_version: 2.6.14.1
195
+ signing_key:
196
+ specification_version: 4
197
+ summary: Check the integrity of Democratic Commons data
198
+ test_files: []