commons-integrity 0.1

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: 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: []