spout 0.12.0.beta2 → 0.12.0.rc

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8d706e8d746c0ae4c64014bbcf38f47d51e388a
4
- data.tar.gz: b2a49b2f165531f6a96ad677cb6a1d9f4045a283
3
+ metadata.gz: b6b577bc1b70562b8fd31c019ebfaf216fbb903c
4
+ data.tar.gz: 84a57bcf18809bcbe7241e969247ae45023fc583
5
5
  SHA512:
6
- metadata.gz: 9b4fa147fc6914b594afd25acc72f5de98c929dbc2631489fd8267c1a3ff3e432a16292a28ce2f5565d011879263a3cfa265b004c1af9deebcf6525d6fa5411d
7
- data.tar.gz: 9f3dfd7ee30b971b2d3152c486dac7bb87b9d3811a6d7fd30267bfdf9285ec4330cfece6956e217dffb64827fa7f902f9f43aec3aed4fd4ea25622b670965017
6
+ metadata.gz: 4498b53635947fb0810c6e3165b58048cf0855130cb6b3a75e47c24ba075d6509cbe10fe2ddcf7c2df01248a73d74f45e79b0847d4a35629c1f399c5ca4b17b0
7
+ data.tar.gz: c81170c50d3cf60e70f931a8eb75fa09dee569705ec687e513b09fdf7d67cbb418c8e5e5c7b9373d3e5fd7649b9f2feb92a114540e0987d8a18df767d47f7c0c
data/CHANGELOG.md CHANGED
@@ -3,11 +3,17 @@
3
3
  ### Enhancements
4
4
  - **General Changes**
5
5
  - Spout now provides a warning and skips columns in CSVs with blank headers
6
+ - `spout new` command now generates `CHANGELOG.md`, `README.md`, and `VERSION`
7
+ placeholder files
8
+ - Check for the latest version available using `spout update`
9
+ - Integer and numeric variables can now reference a domain for missing values
6
10
  - **Exporter Changes**
7
11
  - The export command now exports the variable forms attribute
12
+ - Dictionary exports now are in `exports` instead of `dd` folder
8
13
  - **Importer Changes**
9
14
  - The import command now reads in the `commonly_used` column
10
15
  - The import command now imports the variable forms attribute
16
+ - CSVs of forms can now be imported using `spout import <forms.csv> --forms`
11
17
  - **Gem Changes**
12
18
  - Updated to Ruby 2.4.1
13
19
  - Updated to bundler 1.13
@@ -17,6 +23,9 @@
17
23
  ### Refactoring
18
24
  - General code cleanup based on Rubocop recommendations
19
25
 
26
+ ### Bug Fixes
27
+ - The `test_units` example method now works if `nil` is defined in `VALID_UNITS`
28
+
20
29
  ## 0.11.1 (February 4, 2016)
21
30
 
22
31
  ### Bug Fix
data/README.md CHANGED
@@ -105,6 +105,29 @@ Other columns that are imported include:
105
105
  `folder`: The name of the folder path where the domain resides.
106
106
 
107
107
 
108
+ #### Importing forms from an existing CSV file
109
+
110
+ ```
111
+ spout import data_dictionary_domains.csv --forms
112
+ ```
113
+
114
+ The CSV should contain at minimal three column headers:
115
+
116
+ `folder`: This can be blank, however it is used to place forms into a folder
117
+ hiearchy. The folder column can contain forward slashes `/` to place a form
118
+ into a subfolder. An example may be, `id`: `family_history`,
119
+ `folder`: `Demographics/BaselineVisit` would create a file
120
+ `forms/Demographics/BaselineVisit/family_history.json`
121
+
122
+ `id`: The reference name of the form.
123
+
124
+ `display_name`: The name of the form.
125
+
126
+ Other columns that are imported include:
127
+
128
+ `code_book`: The file name of the document or PDF, including the file extension.
129
+
130
+
108
131
  ### Test your repository
109
132
 
110
133
  If you created your data dictionary repository using `spout new`, you can go
@@ -166,9 +189,10 @@ class DictionaryTest < Minitest::Test
166
189
 
167
190
  @variables.select { |v| %w(numeric integer).include?(v.type) }.each do |variable|
168
191
  define_method("test_units: #{variable.path}") do
169
- puts variable.class
170
- # => Spout::Models::Variable
171
- assert VALID_UNITS.include?(variable.units)
192
+ message = "\"#{variable.units}\"".colorize(:red) + " invalid units.\n" +
193
+ " Valid types: " +
194
+ VALID_UNITS.sort_by(&:to_s).collect { |u| u.inspect.colorize(:white) }.join(', ')
195
+ assert VALID_UNITS.include?(variable.units), message
172
196
  end
173
197
  end
174
198
  end
@@ -388,3 +412,11 @@ The following steps are run:
388
412
  - `README.md` and `KNOWNISSUES.md` are uploaded
389
413
  - **Server-Side Updates**
390
414
  - Server refreshes dataset folder to reflect new dataset and data dictionaries
415
+
416
+ ### Check if you are using the latest version of Spout
417
+
418
+ You can check if a newer version of Spout is available by typing:
419
+
420
+ ```
421
+ spout update
422
+ ```
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
3
  require 'erb'
5
4
  require 'fileutils'
5
+ require 'yaml'
6
6
 
7
7
  require 'spout/helpers/subject_loader'
8
8
  require 'spout/models/coverage_result'
@@ -12,67 +12,58 @@ require 'spout/helpers/array_statistics'
12
12
 
13
13
  module Spout
14
14
  module Commands
15
+ # Generate a coverage report for the data dictionary.
15
16
  class Coverage
16
17
  include Spout::Helpers::NumberHelper
17
18
 
18
19
  def initialize(standard_version, argv)
19
20
  @standard_version = standard_version
20
- @console = (argv.delete('--console') != nil)
21
-
22
- @variable_files = Dir.glob("variables/**/*.json")
21
+ @console = !argv.delete('--console').nil?
22
+ @variable_files = Dir.glob('variables/**/*.json')
23
23
  @valid_ids = []
24
24
  @number_of_rows = nil
25
-
26
25
  @config = Spout::Helpers::ConfigReader.new
27
-
28
- @subject_loader = Spout::Helpers::SubjectLoader.new(@variable_files, @valid_ids, @standard_version, @number_of_rows, @config.visit)
26
+ @subject_loader = Spout::Helpers::SubjectLoader.new(
27
+ @variable_files, @valid_ids, @standard_version, @number_of_rows, @config.visit
28
+ )
29
29
  @subject_loader.load_subjects_from_csvs_part_one! # Not Part Two which is essentially cleaning the data
30
30
  @subjects = @subject_loader.subjects
31
-
32
31
  run_coverage_report!
33
32
  end
34
33
 
35
34
  def run_coverage_report!
36
35
  puts "Generating: index.html\n\n"
37
-
38
36
  @matching_results = []
39
-
40
37
  @subject_loader.all_methods.each do |method, csv_files|
41
38
  scr = Spout::Models::CoverageResult.new(method, @subjects.collect(&method.to_sym).compact_empty.uniq)
42
- @matching_results << [ csv_files, method, scr ]
39
+ @matching_results << [csv_files, method, scr]
43
40
  end
44
-
45
- variable_ids = Dir.glob("variables/**/*.json").collect{ |file| file.gsub(/^(.*)\/|\.json$/, '').downcase }
41
+ variable_ids = Dir.glob('variables/**/*.json').collect { |file| file.gsub(%r{^(.*)/|\.json$}, '').downcase }
46
42
  @extra_variable_ids = (variable_ids - @subject_loader.all_methods.keys).sort
47
-
48
43
  @subject_loader.load_variable_domains!
49
- domain_ids = Dir.glob("domains/**/*.json").collect{ |file| file.gsub(/^(.*)\/|\.json$/, '').downcase }
44
+ domain_ids = Dir.glob('domains/**/*.json').collect { |file| file.gsub(%r{^(.*)/|\.json$}, '').downcase }
50
45
  @extra_domain_ids = (domain_ids - @subject_loader.all_domains).sort
51
-
52
- @matching_results.sort!{|a,b| [b[2].number_of_errors, a[0].to_s, a[1].to_s] <=> [a[2].number_of_errors, b[0].to_s, b[1].to_s]}
53
-
46
+ @matching_results.sort! do |a, b|
47
+ [b[2].number_of_errors, a[0].to_s, a[1].to_s] <=> [a[2].number_of_errors, b[0].to_s, b[1].to_s]
48
+ end
54
49
  @coverage_results = []
55
-
56
50
  @subject_loader.csv_files.each do |csv_file|
57
- total_column_count = @matching_results.select{|mr| mr[0].include?(csv_file)}.count
58
- mapped_column_count = @matching_results.select{|mr| mr[0].include?(csv_file) and mr[2].number_of_errors == 0}.count
59
- @coverage_results << [ csv_file, total_column_count, mapped_column_count ]
51
+ total_column_count = @matching_results.select { |mr| mr[0].include?(csv_file) }.count
52
+ mapped_column_count = @matching_results
53
+ .select { |mr| mr[0].include?(csv_file) && mr[2].number_of_errors.zero? }.count
54
+ @coverage_results << [csv_file, total_column_count, mapped_column_count]
60
55
  end
61
-
62
56
  coverage_folder = File.join(Dir.pwd, 'coverage')
63
57
  FileUtils.mkpath coverage_folder
64
58
  coverage_file = File.join(coverage_folder, 'index.html')
65
-
66
59
  File.open(coverage_file, 'w+') do |file|
67
- erb_location = File.join( File.dirname(__FILE__), '../views/index.html.erb' )
60
+ erb_location = File.join(File.dirname(__FILE__), '../views/index.html.erb')
68
61
  file.puts ERB.new(File.read(erb_location)).result(binding)
69
62
  end
70
-
71
63
  unless @console
72
- open_command = 'open' if RUBY_PLATFORM.match(/darwin/) != nil
73
- open_command = 'start' if RUBY_PLATFORM.match(/mingw/) != nil
74
-
75
- system "#{open_command} #{coverage_file}" if ['start', 'open'].include?(open_command)
64
+ open_command = 'open' unless RUBY_PLATFORM.match(/darwin/).nil?
65
+ open_command = 'start' unless RUBY_PLATFORM.match(/mingw/).nil?
66
+ system "#{open_command} #{coverage_file}" if %w(start open).include?(open_command)
76
67
  end
77
68
  puts "#{coverage_file}\n\n"
78
69
  end
@@ -10,7 +10,6 @@ require 'spout/helpers/quietly'
10
10
  require 'spout/helpers/send_file'
11
11
  require 'spout/helpers/semantic'
12
12
  require 'spout/helpers/json_request'
13
- require 'spout/helpers/json_request_generic'
14
13
 
15
14
  # - **User Authorization**
16
15
  # - User authenticates via token, the user must be a dataset editor
@@ -37,6 +36,7 @@ end
37
36
 
38
37
  module Spout
39
38
  module Commands
39
+ # Deploys a data dictionary and associated dataset to the server.
40
40
  class Deploy
41
41
  include Spout::Helpers::Quietly
42
42
 
@@ -211,22 +211,17 @@ module Spout
211
211
  end
212
212
 
213
213
  def user_authorization
214
- puts " Get your token here: " + "#{@url}/token".colorize(:blue).on_white.underline
215
- print " Enter your token: "
214
+ puts ' Get your token here: ' + "#{@url}/token".colorize(:blue).on_white.underline
215
+ print ' Enter your token: '
216
216
  @token = STDIN.noecho(&:gets).chomp if @token.to_s.strip == ''
217
-
218
- response = Spout::Helpers::JsonRequest.get("#{@url}/datasets/#{@slug}/a/#{@token}/editor.json")
219
-
220
- if response.is_a?(Hash) and response['editor']
217
+ (json, _status) = Spout::Helpers::JsonRequest.get("#{@url}/datasets/#{@slug}/a/#{@token}/editor.json")
218
+ if json.is_a?(Hash) && json['editor']
221
219
  puts 'AUTHORIZED'.colorize(:green)
222
220
  else
223
221
  puts 'UNAUTHORIZED'.colorize(:red)
224
222
  puts "#{INDENT}You are not set as an editor on the #{@slug} dataset or you mistyped your token."
225
- fail DeployError
223
+ raise DeployError
226
224
  end
227
-
228
- # failure ''
229
- # puts 'PASS'.colorize(:green)
230
225
  end
231
226
 
232
227
  def upload_variables
@@ -328,12 +323,12 @@ module Spout
328
323
 
329
324
  print 'Launch Server Scripts: '
330
325
  params = { auth_token: @token, dataset: @slug, version: @version, folders: @created_folders.compact.uniq }
331
- (response, status) = Spout::Helpers::JsonRequestGeneric.post("#{@url}/api/v1/dictionary/refresh.json", params)
332
- if response.is_a?(Hash) && response['refresh'] == 'success'
326
+ (json, _status) = Spout::Helpers::JsonRequest.post("#{@url}/api/v1/dictionary/refresh.json", params)
327
+ if json.is_a?(Hash) && json['refresh'] == 'success'
333
328
  puts 'DONE'.colorize(:green)
334
329
  else
335
330
  puts 'FAIL'.colorize(:red)
336
- fail DeployError
331
+ raise DeployError
337
332
  end
338
333
  end
339
334
 
@@ -342,11 +337,12 @@ module Spout
342
337
  puts ' Set Default Version: ' + 'SKIP'.colorize(:blue)
343
338
  return
344
339
  end
345
-
346
340
  print ' Set Default Version: '
347
341
  params = { auth_token: @token, dataset: @slug, version: @version }
348
- (response, status) = Spout::Helpers::JsonRequestGeneric.post("#{@url}/api/v1/dictionary/update_default_version.json", params)
349
- if response.is_a?(Hash) && response['version_update'] == 'success'
342
+ (json, _status) = Spout::Helpers::JsonRequest.post(
343
+ "#{@url}/api/v1/dictionary/update_default_version.json", params
344
+ )
345
+ if json.is_a?(Hash) && json['version_update'] == 'success'
350
346
  puts @version.to_s.colorize(:green)
351
347
  else
352
348
  failure("#{INDENT}Unable to set default version\n#{INDENT}to " + @version.to_s.colorize(:white) + ' for ' + @slug.to_s.colorize(:white) + ' dataset.')
@@ -356,7 +352,7 @@ module Spout
356
352
  def failure(message)
357
353
  puts 'FAIL'.colorize(:red)
358
354
  puts message
359
- fail DeployError
355
+ raise DeployError
360
356
  end
361
357
 
362
358
  def upload_file(file, folder)
@@ -21,7 +21,7 @@ module Spout
21
21
  private
22
22
 
23
23
  def expanded_export!
24
- folder = "dd/#{@standard_version}"
24
+ folder = "exports/#{@standard_version}"
25
25
  puts ' create'.colorize(:green) + " #{folder}" unless @quiet
26
26
  FileUtils.mkpath folder
27
27
  generic_export(
@@ -14,7 +14,7 @@ require 'spout/models/graphables'
14
14
  require 'spout/models/tables'
15
15
  require 'spout/helpers/config_reader'
16
16
  require 'spout/helpers/send_file'
17
- require 'spout/helpers/json_request_generic'
17
+ require 'spout/helpers/json_request'
18
18
  require 'spout/version'
19
19
 
20
20
  module Spout
@@ -176,12 +176,12 @@ module Spout
176
176
  domain: (variable.domain ? variable.domain.deploy_params : nil),
177
177
  forms: variable.forms.collect(&:deploy_params) }
178
178
  params[:variable][:spout_stats] = stats.to_json
179
- (response, status) = Spout::Helpers::JsonRequestGeneric.post("#{@url}/api/v1/variables/create_or_update.json", params)
180
- if response.is_a?(Hash) && status.is_a?(Net::HTTPSuccess)
179
+ (json, status) = Spout::Helpers::JsonRequest.post("#{@url}/api/v1/variables/create_or_update.json", params)
180
+ if json.is_a?(Hash) && status.is_a?(Net::HTTPSuccess)
181
181
  @progress[variable.id]['uploaded'] << @webserver_name
182
182
  else
183
183
  puts "\nUPLOAD FAILED: ".colorize(:red) + variable.id
184
- puts "- Error: #{response.inspect}"
184
+ puts "- Error: #{json.inspect}"
185
185
  end
186
186
  end
187
187
  end
@@ -29,6 +29,7 @@ The most common spout commands are:
29
29
  in `<project_name>/graphs/<version>/`
30
30
  [d]eploy NAME Push dataset and data dictionary to a
31
31
  webserver specified in `.spout.yml`
32
+ [u]pdate Update the Spout gem
32
33
  [v]ersion Returns the version of Spout
33
34
 
34
35
  Commands can be referenced by the first letter:
@@ -42,7 +43,9 @@ EOT
42
43
 
43
44
  def new_project
44
45
  puts <<-EOT
45
- Usage: spout new <project_name>
46
+ Usage: spout new <project_name> [--skip-gemfile]
47
+
48
+ Use `--skip-gemfile` to skip installing gems after project creation.
46
49
 
47
50
  More information here:
48
51
 
@@ -55,6 +58,8 @@ EOT
55
58
  puts <<-EOT
56
59
  Usage: spout version
57
60
 
61
+ Returns version of spout.
62
+
58
63
  EOT
59
64
  end
60
65
 
@@ -62,6 +67,15 @@ EOT
62
67
  puts <<-EOT
63
68
  Usage: spout test
64
69
 
70
+ EOT
71
+ end
72
+
73
+ def update
74
+ puts <<-EOT
75
+ Usage: spout update
76
+
77
+ Checks if a newer version of Spout is available.
78
+
65
79
  EOT
66
80
  end
67
81
 
@@ -71,10 +85,12 @@ Usage: spout import <csv_file>
71
85
 
72
86
  Optional Flags:
73
87
  --domains Specify to import CSV of domains
88
+ --forms Specify to import CSV of forms
74
89
 
75
90
  More information:
76
91
  https://github.com/sleepepi/spout#generate-a-new-repository-from-an-existing-csv-file
77
92
  https://github.com/sleepepi/spout#importing-domains-from-an-existing-csv-file
93
+ https://github.com/sleepepi/spout#importing-forms-from-an-existing-csv-file
78
94
  EOT
79
95
  end
80
96
 
@@ -10,6 +10,7 @@ module Spout
10
10
  class Importer
11
11
  def initialize(argv)
12
12
  use_domains = !argv.delete('--domains').nil?
13
+ use_forms = !argv.delete('--forms').nil?
13
14
  @csv_file = argv[1].to_s
14
15
  unless File.exist?(@csv_file)
15
16
  puts csv_usage
@@ -17,6 +18,8 @@ module Spout
17
18
  end
18
19
  if use_domains
19
20
  import_domains
21
+ elsif use_forms
22
+ import_forms
20
23
  else
21
24
  import_variables
22
25
  end
@@ -62,7 +65,7 @@ EOT
62
65
  hash['forms'] = forms unless forms.empty?
63
66
  hash['other'] = row unless row.empty?
64
67
 
65
- file_name = File.join(folder, id + '.json')
68
+ file_name = File.join(folder, "#{id}.json")
66
69
  File.open(file_name, 'w') do |file|
67
70
  file.write(JSON.pretty_generate(hash) + "\n")
68
71
  end
@@ -117,11 +120,45 @@ EOT
117
120
  end
118
121
  end
119
122
 
123
+ def import_forms
124
+ CSV.parse(File.open(@csv_file, 'r:iso-8859-1:utf-8', &:read), headers: true) do |line|
125
+ row = line.to_hash
126
+ unless row.keys.include?('id')
127
+ puts "\nMissing column header `".colorize(:red) +
128
+ 'id'.colorize(:light_cyan) +
129
+ '` in data dictionary.'.colorize(:red) +
130
+ additional_csv_info
131
+ exit(1)
132
+ end
133
+ unless row.keys.include?('display_name')
134
+ puts "\nMissing column header `".colorize(:red) +
135
+ 'display_name'.colorize(:light_cyan) +
136
+ '` in data dictionary.'.colorize(:red) +
137
+ additional_csv_info
138
+ exit(1)
139
+ end
140
+ next if row['id'] == ''
141
+ folder = File.join('forms', row.delete('folder').to_s)
142
+ FileUtils.mkpath folder
143
+ hash = {}
144
+ id = row.delete('id').to_s.downcase
145
+ hash['id'] = id
146
+ hash['display_name'] = tenderize(row.delete('display_name').to_s)
147
+ hash['code_book'] = row.delete('code_book').to_s
148
+ hash['other'] = row unless row.empty?
149
+ file_name = File.join(folder, "#{id}.json")
150
+ File.open(file_name, 'w') do |file|
151
+ file.write(JSON.pretty_generate(hash) + "\n")
152
+ end
153
+ puts ' create'.colorize(:green) + " #{file_name}"
154
+ end
155
+ end
156
+
120
157
  # Converts ALL-CAPS display names to title case
121
158
  # Ex: BODY MASS INDEX changes to Body Mass Index
122
159
  # Ex: Patient ID stays the same as Patient ID
123
160
  def tenderize(text)
124
- if text.match(/[a-z]/)
161
+ if /[a-z]/ =~ text
125
162
  text
126
163
  else
127
164
  text.downcase.gsub(/\b\w/) { $&.upcase }
@@ -131,7 +168,8 @@ EOT
131
168
  private
132
169
 
133
170
  def additional_csv_info
134
- "\n\nFor additional information on specifying CSV column headers before import see:\n\n " + "https://github.com/sleepepi/spout#generate-a-new-repository-from-an-existing-csv-file".colorize( :light_cyan ) + "\n\n"
171
+ "\n\nFor additional information on specifying CSV column headers before import see:\n\n " +
172
+ "https://github.com/sleepepi/spout#generate-a-new-repository-from-an-existing-csv-file".colorize(:light_cyan) + "\n\n"
135
173
  end
136
174
  end
137
175
  end
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'colorize'
4
+ require 'date'
5
+ require 'erb'
4
6
  require 'fileutils'
5
7
 
6
8
  TEMPLATES_DIRECTORY = File.expand_path('../../templates', __FILE__)
7
9
 
8
10
  module Spout
9
11
  module Commands
12
+ # Generates folder and file structure for a new spout data dictionary.
10
13
  class ProjectGenerator
11
14
  def initialize(argv)
12
15
  generate_folder_structure!(argv)
@@ -14,7 +17,8 @@ module Spout
14
17
 
15
18
  def generate_folder_structure!(argv)
16
19
  skip_gemfile = !argv.delete('--skip-gemfile').nil?
17
- @full_path = File.join(argv[1].to_s.strip)
20
+ @project_name = argv[1].to_s.strip
21
+ @full_path = File.join(@project_name)
18
22
  usage = <<-EOT
19
23
 
20
24
  Usage: spout new FOLDER
@@ -30,9 +34,12 @@ EOT
30
34
  copy_file 'gitignore', '.gitignore'
31
35
  copy_file 'ruby-version', '.ruby-version'
32
36
  copy_file 'travis.yml', '.travis.yml'
33
- copy_file 'spout.yml', '.spout.yml'
37
+ evaluate_file 'spout.yml.erb', '.spout.yml'
38
+ evaluate_file 'CHANGELOG.md.erb', 'CHANGELOG.md'
34
39
  copy_file 'Gemfile'
35
40
  copy_file 'Rakefile'
41
+ evaluate_file 'README.md.erb', 'README.md'
42
+ copy_file 'VERSION'
36
43
  directory 'domains'
37
44
  copy_file 'keep', 'domains/.keep'
38
45
  directory 'variables'
@@ -42,11 +49,10 @@ EOT
42
49
  directory 'test'
43
50
  copy_file 'test/dictionary_test.rb'
44
51
  copy_file 'test/test_helper.rb'
45
- unless skip_gemfile
46
- puts " run".colorize( :green ) + " bundle install".colorize( :light_cyan )
47
- Dir.chdir(@full_path)
48
- system "bundle install"
49
- end
52
+ return if skip_gemfile
53
+ puts ' run'.colorize(:green) + ' bundle install'.colorize(:light_cyan)
54
+ Dir.chdir(@full_path)
55
+ system 'bundle install'
50
56
  end
51
57
 
52
58
  private
@@ -55,16 +61,26 @@ EOT
55
61
  file_name = template_file if file_name == ''
56
62
  file_path = File.join(@full_path, file_name)
57
63
  template_file_path = File.join(TEMPLATES_DIRECTORY, template_file)
58
- puts " create".colorize( :green ) + " #{file_name}"
64
+ puts ' create'.colorize(:green) + " #{file_name}"
59
65
  FileUtils.copy(template_file_path, file_path)
60
66
  end
61
67
 
68
+ def evaluate_file(template_file, file_name)
69
+ template_file_path = File.join(TEMPLATES_DIRECTORY, template_file)
70
+ template = ERB.new(File.read(template_file_path))
71
+ file_path = File.join(@full_path, file_name)
72
+ file_out = File.new(file_path, 'w')
73
+ file_out.syswrite(template.result(binding))
74
+ puts ' create'.colorize(:green) + " #{file_name}"
75
+ ensure
76
+ file_out.close if file_out
77
+ end
78
+
62
79
  def directory(directory_name)
63
80
  directory_path = File.join(@full_path, directory_name)
64
- puts " create".colorize( :green ) + " #{directory_name}"
81
+ puts ' create'.colorize(:green) + " #{directory_name}"
65
82
  FileUtils.mkpath(directory_path)
66
83
  end
67
-
68
84
  end
69
85
  end
70
86
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+ require 'spout/helpers/json_request'
5
+
6
+ module Spout
7
+ module Commands
8
+ # Command to check if there is an updated version of the gem available.
9
+ class Update
10
+ class << self
11
+ def start(*args)
12
+ new(*args).start
13
+ end
14
+ end
15
+
16
+ def initialize(argv)
17
+ end
18
+
19
+ def start
20
+ (json, _status) = Spout::Helpers::JsonRequest.get('https://rubygems.org/api/v1/gems/spout.json')
21
+ if json
22
+ if json['version'] == Spout::VERSION::STRING
23
+ puts 'The spout gem is ' + 'up-to-date'.colorize(:green) + '!'
24
+ else
25
+ puts
26
+ puts "A newer version (v#{json['version']}) is available! Type the following command to update:"
27
+ puts
28
+ puts ' gem install spout --no-document'.colorize(:white)
29
+ puts
30
+ end
31
+ else
32
+ puts 'Unable to connect to RubyGems.org. Please try again later.'
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,42 +3,89 @@
3
3
  require 'openssl'
4
4
  require 'net/http'
5
5
  require 'json'
6
+ require 'cgi'
6
7
 
7
- # TODO: Deprecated, use JsonRequestGeneric instead
8
8
  module Spout
9
9
  module Helpers
10
+ # Generates JSON web requests for GET, POST, and PATCH.
10
11
  class JsonRequest
11
12
  class << self
12
- def get(*args)
13
- new(*args).get
13
+ def get(url, *args)
14
+ new(url, *args).get
15
+ end
16
+
17
+ def post(url, *args)
18
+ new(url, *args).post
19
+ end
20
+
21
+ def patch(url, *args)
22
+ new(url, *args).patch
14
23
  end
15
24
  end
16
25
 
17
26
  attr_reader :url
18
27
 
19
- def initialize(url)
20
- begin
21
- @url = URI.parse(url)
22
- @http = Net::HTTP.new(@url.host, @url.port)
23
- if @url.scheme == 'https'
24
- @http.use_ssl = true
25
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
26
- end
27
- rescue
28
+ def initialize(url, args = {})
29
+ @params = nested_hash_to_params(args)
30
+ @url = URI.parse(url)
31
+
32
+ @http = Net::HTTP.new(@url.host, @url.port)
33
+ if @url.scheme == 'https'
34
+ @http.use_ssl = true
35
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
28
36
  end
37
+ rescue
38
+ @error = "Invalid URL: #{url.inspect}"
39
+ puts @error.colorize(:red)
29
40
  end
30
41
 
31
42
  def get
32
- begin
33
- full_path = @url.path
34
- full_path += "?#{@url.query}" if @url.query
35
- req = Net::HTTP::Get.new(full_path)
36
- response = @http.start do |http|
37
- http.request(req)
43
+ return unless @error.nil?
44
+ full_path = @url.path
45
+ query = ([@url.query] + @params).flatten.compact.join('&')
46
+ full_path += "?#{query}" if query.to_s != ''
47
+ response = @http.start do |http|
48
+ http.get(full_path)
49
+ end
50
+ [JSON.parse(response.body), response]
51
+ rescue => e
52
+ puts "GET Error: #{e}".colorize(:red)
53
+ end
54
+
55
+ def post
56
+ return unless @error.nil?
57
+ response = @http.start do |http|
58
+ http.post(@url.path, @params.flatten.compact.join('&'))
59
+ end
60
+ [JSON.parse(response.body), response]
61
+ rescue => e
62
+ puts "POST ERROR: #{e}".colorize(:red)
63
+ nil
64
+ end
65
+
66
+ def patch
67
+ @params << '_method=patch'
68
+ post
69
+ end
70
+
71
+ def nested_hash_to_params(args)
72
+ args.collect do |key, value|
73
+ key_value_to_string(key, value, nil)
74
+ end
75
+ end
76
+
77
+ def key_value_to_string(key, value, scope = nil)
78
+ current_scope = (scope ? "#{scope}[#{key}]" : key)
79
+ if value.is_a? Hash
80
+ value.collect do |k,v|
81
+ key_value_to_string(k, v, current_scope)
82
+ end.join('&')
83
+ elsif value.is_a? Array
84
+ value.collect do |v|
85
+ key_value_to_string('', v, current_scope)
38
86
  end
39
- JSON.parse(response.body)
40
- rescue
41
- nil
87
+ else
88
+ "#{current_scope}=#{CGI.escape(value.to_s)}"
42
89
  end
43
90
  end
44
91
  end
@@ -126,7 +126,7 @@ module Spout
126
126
  @variable_files.each do |variable_file|
127
127
  json = JSON.parse(File.read(variable_file)) rescue json = nil
128
128
  next unless json
129
- next unless ['choices'].include?(json['type'])
129
+ next unless json['type'] == 'choices' || json['domain'].to_s.downcase.strip != ''
130
130
  domain = json['domain'].to_s.downcase
131
131
  @all_domains << domain
132
132
  end
@@ -4,8 +4,11 @@ require 'spout/tests/variable_type_validation'
4
4
 
5
5
  module Spout
6
6
  module Models
7
+ # Contains the coverage of a specific variable.
7
8
  class CoverageResult
8
- attr_accessor :error, :error_message, :file_name_test, :json_id_test, :values_test, :valid_values, :csv_values, :variable_type_test, :json, :domain_test
9
+ attr_accessor :error, :error_message, :file_name_test, :json_id_test,
10
+ :values_test, :valid_values, :csv_values,
11
+ :variable_type_test, :json, :domain_test
9
12
 
10
13
  def initialize(column, csv_values)
11
14
  load_json(column)
@@ -26,10 +29,10 @@ module Spout
26
29
 
27
30
  def load_valid_values
28
31
  valid_values = []
29
- if @json['type'] == 'choices'
32
+ if @json['type'] == 'choices' || domain_name != ''
30
33
  file = Dir.glob("domains/**/#{@json['domain'].to_s.downcase}.json", File::FNM_CASEFOLD).first
31
34
  if json = JSON.parse(File.read(file)) rescue false
32
- valid_values = json.collect{|hash| hash['value']}
35
+ valid_values = json.collect { |hash| hash['value'] }
33
36
  end
34
37
  end
35
38
  @valid_values = valid_values
@@ -48,7 +51,7 @@ module Spout
48
51
  end
49
52
 
50
53
  def check_domain_specified
51
- if @json['type'] != 'choices'
54
+ if @json['type'] != 'choices' && domain_name == ''
52
55
  true
53
56
  else
54
57
  domain_file = Dir.glob("domains/**/#{@json['domain'].to_s.downcase}.json", File::FNM_CASEFOLD).first
@@ -62,6 +65,10 @@ module Spout
62
65
  def errored?
63
66
  error == true
64
67
  end
68
+
69
+ def domain_name
70
+ @json['domain'].to_s.downcase.strip
71
+ end
65
72
  end
66
73
  end
67
74
  end
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (<%= Date.today.strftime('%B %-d, %Y') %>)
2
+
3
+ - Created the <%= @project_name.downcase %> data dictionary repository using Spout v<%= Spout::VERSION::STRING %>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gem 'spout'
@@ -0,0 +1,46 @@
1
+ <%= @project_name.capitalize %> Data Dictionary
2
+ ======================
3
+
4
+ [![Build Status](https://travis-ci.org/<REPOSITORY>/<%= @project_name.downcase %>-data-dictionary.svg?branch=master)](https://travis-ci.org/<REPOSITORY>/<%= @project_name.downcase %>-data-dictionary)
5
+
6
+ ### Exports
7
+
8
+ The <%= @project_name.downcase %> data dictionary can be exported to CSV by typing:
9
+
10
+ ```
11
+ spout export
12
+ ```
13
+
14
+ The `spout export` command will generate CSV files that describe the data
15
+ dictionary.
16
+
17
+
18
+ ### Testing
19
+
20
+ The <%= @project_name.downcase %> data dictionary is tested using the
21
+ [Spout Gem](https://github.com/sleepepi/spout).
22
+
23
+ Data dictionary tests can be run by typing:
24
+
25
+ ```
26
+ spout test
27
+ ```
28
+
29
+
30
+ ### Releases
31
+
32
+ The data dictionary is tagged at various time points using
33
+ [Git tags](http://git-scm.com/book/en/Git-Basics-Tagging). The tags are used to
34
+ reference a series of CSV files that correspond to the data dictionary itself.
35
+
36
+ For example, CSV files of the underlying data that have been tagged as `v0.1.0`
37
+ will be compatible with the Data Dictionary `~> 0.1.0`,
38
+ (including `0.1.1`, `0.1.2`, `0.1.3`). However if the data dictionary contains
39
+ changes to the underlying dataset, then the minor version number is bumped, and
40
+ the patch level is reset to zero. If, for example, the CSV dataset changed to
41
+ `v0.2.0`, then it would be compatible with `0.2.0`, `0.2.1`, `0.2.2`, etc. The
42
+ approach for changing version numbers uses a variation on
43
+ [Semantic Versioning](http://semver.org).
44
+
45
+ A full list of changes for each version can be viewed in the
46
+ [CHANGELOG](https://github.com/<REPOSITORY>/<%= @project_name.downcase %>-data-dictionary/blob/master/CHANGELOG.md).
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spout/tasks'
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -5,8 +5,7 @@
5
5
  /Gemfile.lock
6
6
 
7
7
  # Data Dictionary folders
8
- /dd
9
8
  /coverage
10
9
  /csvs
10
+ /exports
11
11
  /graphs
12
- /images
@@ -0,0 +1,3 @@
1
+ slug: <%= @project_name.downcase %>
2
+ visit: visit
3
+ charts: []
@@ -15,8 +15,9 @@ class DictionaryTest < Minitest::Test
15
15
  # VALID_UNITS = ['minutes', 'hours'] # Add your own valid units to this array
16
16
  # @variables.select { |v| %w(numeric integer).include?(v.type) }.each do |variable|
17
17
  # define_method("test_units: #{variable.path}") do
18
- # message = "\"#{variable.units}\"".colorize(:red) + " invalid units.\n Valid types: " +
19
- # VALID_UNITS.sort.collect { |u| u.inspect.colorize(:white) }.join(', ')
18
+ # message = "\"#{variable.units}\"".colorize(:red) + " invalid units.\n" +
19
+ # " Valid types: " +
20
+ # VALID_UNITS.sort_by(&:to_s).collect { |u| u.inspect.colorize(:white) }.join(', ')
20
21
  # assert VALID_UNITS.include?(variable.units), message
21
22
  # end
22
23
  # end
@@ -2,32 +2,31 @@
2
2
 
3
3
  module Spout
4
4
  module Tests
5
+ # If a variable references a domain, then the domain should exist and be
6
+ # defined.
5
7
  module DomainExistenceValidation
6
-
7
8
  def assert_domain_existence(item)
8
- domain_names = Dir.glob("domains/**/*.json").collect{|file| file.split('/').last.to_s.downcase.split('.json').first}
9
-
9
+ domain_names = Dir.glob('domains/**/*.json').collect do |file|
10
+ file.split('/').last.to_s.downcase.split('.json').first
11
+ end
10
12
  result = begin
11
- domain_name = JSON.parse(File.read(item))["domain"]
13
+ domain_name = JSON.parse(File.read(item))['domain']
12
14
  domain_names.include?(domain_name)
13
15
  rescue JSON::ParserError
14
16
  domain_name = ''
15
17
  false
16
18
  end
17
-
18
19
  message = "The domain #{domain_name} referenced by #{item} does not exist."
19
-
20
20
  assert result, message
21
21
  end
22
22
 
23
- Dir.glob("variables/**/*.json").each do |file|
24
- if (not [nil, ''].include?(JSON.parse(File.read(file))["domain"]) rescue false)
25
- define_method("test_domain_exists: "+file) do
23
+ Dir.glob('variables/**/*.json').each do |file|
24
+ if (not [nil, ''].include?(JSON.parse(File.read(file))['domain']) rescue false)
25
+ define_method("test_domain_exists: #{file}") do
26
26
  assert_domain_existence file
27
27
  end
28
28
  end
29
29
  end
30
-
31
30
  end
32
31
  end
33
32
  end
@@ -3,30 +3,30 @@
3
3
  module Spout
4
4
  module Tests
5
5
  module DomainFormat
6
-
6
+ # Verifies the format of a domain.
7
7
  def assert_domain_format(item)
8
8
  result = begin
9
9
  json = JSON.parse(File.read(item))
10
10
  if json.is_a?(Array)
11
- json.empty? or json.select{|o| not o.is_a?(Hash)}.size == 0
11
+ json.empty? || json.select { |o| !o.is_a?(Hash) }.empty?
12
12
  else
13
13
  false
14
14
  end
15
15
  rescue JSON::ParserError
16
16
  false
17
17
  end
18
-
19
- message = "Must be an array of choice hashes. Ex:\n[\n {\n \"value\": \"1\",\n \"display_name\": \"Option 1\",\n \"description\": \"...\"\n },\n { ... },\n ...\n]"
20
-
18
+ message = \
19
+ "Must be an array of choice hashes. Ex:\n[\n {\n \"value\": "\
20
+ " \"1\",\n \"display_name\": \"Option 1\",\n \"description\""\
21
+ ": \"...\"\n },\n { ... },\n ...\n]"
21
22
  assert result, message
22
23
  end
23
24
 
24
- Dir.glob("domains/**/*.json").each do |file|
25
- define_method("test_domain_format: "+file) do
25
+ Dir.glob('domains/**/*.json').each do |file|
26
+ define_method("test_domain_format: #{file}") do
26
27
  assert_domain_format file
27
28
  end
28
29
  end
29
-
30
30
  end
31
31
  end
32
32
  end
data/lib/spout/version.rb CHANGED
@@ -5,7 +5,7 @@ module Spout
5
5
  MAJOR = 0
6
6
  MINOR = 12
7
7
  TINY = 0
8
- BUILD = 'beta2' # 'pre', 'rc', 'rc2', nil
8
+ BUILD = 'rc' # 'pre', 'rc', 'rc2', nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.').freeze
11
11
  end
@@ -102,7 +102,7 @@ tfoot td {
102
102
  <tr>
103
103
  <td class="text-muted">Variables Not Found in Any CSV</td>
104
104
  <td></td>
105
- <td><code class="default"><%= number_with_delimiter( @extra_variable_ids.size ) %></code></td>
105
+ <td><code class="default"><%= number_with_delimiter(@extra_variable_ids.size) %></code></td>
106
106
  <td></td>
107
107
  <td></td>
108
108
  </tr>
@@ -111,7 +111,7 @@ tfoot td {
111
111
  <tr>
112
112
  <td class="text-muted">Domains Not Referenced by Any Variable</td>
113
113
  <td></td>
114
- <td><code class="default"><%= number_with_delimiter( @extra_domain_ids.size ) %></code></td>
114
+ <td><code class="default"><%= number_with_delimiter(@extra_domain_ids.size) %></code></td>
115
115
  <td></td>
116
116
  <td></td>
117
117
  </tr>
@@ -126,9 +126,9 @@ tfoot td {
126
126
  <span class="text-muted">---</span>
127
127
  <% end %>
128
128
  </td>
129
- <td><code class="default"><%= number_with_delimiter( total_column_count ) %></code></td>
130
- <td><code class="success"><%= number_with_delimiter( mapped_column_count ) %></code></td>
131
- <td><code><%= number_with_delimiter( total_column_count - mapped_column_count ) %></code></td>
129
+ <td><code class="default"><%= number_with_delimiter(total_column_count) %></code></td>
130
+ <td><code class="success"><%= number_with_delimiter(mapped_column_count) %></code></td>
131
+ <td><code><%= number_with_delimiter(total_column_count - mapped_column_count) %></code></td>
132
132
  </tr>
133
133
  <% end %>
134
134
  </tbody>
@@ -177,7 +177,7 @@ tfoot td {
177
177
  <% matched = @matching_results.count{|csv_files, column, scr| scr.file_name_test} %>
178
178
  <% matched_percent = (total_count == 0 ? 0 : (matched * 100.0 / total_count).floor) %>
179
179
  <% missing_percent = 100 - matched_percent %>
180
- <%= number_with_delimiter( matched ) %> of <%= number_with_delimiter( total_count ) %>
180
+ <%= number_with_delimiter(matched) %> of <%= number_with_delimiter(total_count) %>
181
181
  <div class="progress progress-striped">
182
182
  <div class="progress-bar progress-bar-success" style="width: <%= matched_percent %>%"></div>
183
183
  <div class="progress-bar progress-bar-danger" style="width: <%= missing_percent %>%"></div>
@@ -187,7 +187,7 @@ tfoot td {
187
187
  <% matched = @matching_results.count{|csv_files, column, scr| scr.json_id_test} %>
188
188
  <% matched_percent = (total_count == 0 ? 0 : (matched * 100.0 / total_count).floor) %>
189
189
  <% missing_percent = 100 - matched_percent %>
190
- <%= number_with_delimiter( matched ) %> of <%= number_with_delimiter( total_count ) %>
190
+ <%= number_with_delimiter(matched) %> of <%= number_with_delimiter(total_count) %>
191
191
  <div class="progress progress-striped">
192
192
  <div class="progress-bar progress-bar-success" style="width: <%= matched_percent %>%"></div>
193
193
  <div class="progress-bar progress-bar-danger" style="width: <%= missing_percent %>%"></div>
@@ -197,7 +197,7 @@ tfoot td {
197
197
  <% matched = @matching_results.count{|csv_files, column, scr| scr.variable_type_test} %>
198
198
  <% matched_percent = (total_count == 0 ? 0 : (matched * 100.0 / total_count).floor) %>
199
199
  <% missing_percent = 100 - matched_percent %>
200
- <%= number_with_delimiter( matched ) %> of <%= number_with_delimiter( total_count ) %>
200
+ <%= number_with_delimiter(matched) %> of <%= number_with_delimiter(total_count) %>
201
201
  <div class="progress progress-striped">
202
202
  <div class="progress-bar progress-bar-success" style="width: <%= matched_percent %>%"></div>
203
203
  <div class="progress-bar progress-bar-danger" style="width: <%= missing_percent %>%"></div>
@@ -208,7 +208,7 @@ tfoot td {
208
208
  <% total_count = @matching_results.count{|csv_files, column, scr| scr.json['type'] == 'choices'} %>
209
209
  <% matched_percent = (total_count == 0 ? 0 : (matched * 100.0 / total_count).floor) %>
210
210
  <% missing_percent = 100 - matched_percent %>
211
- <%= number_with_delimiter( matched ) %> of <%= number_with_delimiter( total_count ) %>
211
+ <%= number_with_delimiter(matched) %> of <%= number_with_delimiter(total_count) %>
212
212
  <div class="progress progress-striped">
213
213
  <div class="progress-bar progress-bar-success" style="width: <%= matched_percent %>%"></div>
214
214
  <div class="progress-bar progress-bar-danger" style="width: <%= missing_percent %>%"></div>
@@ -262,8 +262,8 @@ tfoot td {
262
262
  <% end %>
263
263
  </td>
264
264
  <td>
265
- <% if scr.json['type'] == 'choices' && scr.file_name_test %>
266
- <% if scr.domain_test or scr.json['domain'].to_s.strip == '' %>
265
+ <% if (scr.json['type'] == 'choices' || scr.json['domain'].to_s.downcase.strip != '') && scr.file_name_test %>
266
+ <% if scr.domain_test || scr.json['domain'].to_s.strip == '' %>
267
267
  <code class="<%= 'success' if scr.domain_test %>">"domain": <%= scr.json['domain'].inspect %></code>
268
268
  <% else %>
269
269
  <span class="text-danger"><code><%= scr.json['domain'] %>.json</code> missing</span>
@@ -271,15 +271,18 @@ tfoot td {
271
271
  <% end %>
272
272
  </td>
273
273
  <td style="white-space:nowrap">
274
- <% if scr.json['type'] == 'choices' %>
275
- <% unused_values = scr.valid_values - scr.csv_values %>
276
- <% if scr.values_test && unused_values.size == 0 %>
274
+ <% if scr.json['type'] == 'choices' || scr.json['domain'].to_s.downcase.strip != '' %>
275
+ <% unused_domain_values = scr.valid_values - scr.csv_values %>
276
+ <% unused_values = (scr.valid_values | scr.csv_values) - (scr.valid_values & scr.csv_values) %>
277
+ <% if scr.values_test && unused_values.empty? %>
277
278
  <div class="text-success" style="text-align:center"><span class="glyphicon glyphicon-ok"></span></div>
278
279
  <% else %>
279
280
  <% (scr.valid_values + scr.csv_values.compact.sort).uniq.each do |value| %>
281
+ <% value_as_number = format('%g', value) rescue value_as_number = nil %>
282
+ <% next if !scr.valid_values.include?(value) && %w(numeric integer).include?(scr.json['type']) && !value_as_number.nil? %>
280
283
  <% class_type = '' %>
281
284
  <% class_type = 'success' if scr.valid_values.include?(value) %>
282
- <% class_type = 'default' if unused_values.include?(value) %>
285
+ <% class_type = 'default' if unused_domain_values.include?(value) %>
283
286
  <code class="<%= class_type %>"><%= value %></code>
284
287
  <% end %>
285
288
  <% end %>
data/lib/spout.rb CHANGED
@@ -5,17 +5,19 @@ require 'spout/version'
5
5
  require 'spout/models/dictionary'
6
6
 
7
7
  Spout::COMMANDS = {
8
- 'n' => :new_project,
9
- 'v' => :version,
10
- 't' => :test,
11
- 'i' => :importer,
12
- 'e' => :exporter,
13
8
  'c' => :coverage_report,
9
+ 'd' => :deploy,
10
+ 'e' => :exporter,
14
11
  'g' => :generate_charts_and_tables,
12
+ 'i' => :importer,
13
+ 'n' => :new_project,
15
14
  'o' => :outliers_report,
16
- 'd' => :deploy
15
+ 't' => :test,
16
+ 'u' => :update,
17
+ 'v' => :version
17
18
  }
18
19
 
20
+ # Launch spout commands from command line.
19
21
  module Spout
20
22
  def self.launch(argv)
21
23
  send((Spout::COMMANDS[argv.first.to_s.scan(/\w/).first] || :help), argv)
@@ -70,6 +72,11 @@ module Spout
70
72
  # Spout::Commands::TestRunner.new(argv)
71
73
  end
72
74
 
75
+ def self.update(argv)
76
+ require 'spout/commands/update'
77
+ Spout::Commands::Update.start(argv)
78
+ end
79
+
73
80
  def self.version(_argv)
74
81
  puts "Spout #{Spout::VERSION::STRING}"
75
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0.beta2
4
+ version: 0.12.0.rc
5
5
  platform: ruby
6
6
  authors:
7
7
  - Remo Mueller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-24 00:00:00.000000000 Z
11
+ date: 2017-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -117,13 +117,13 @@ files:
117
117
  - lib/spout/commands/importer.rb
118
118
  - lib/spout/commands/outliers.rb
119
119
  - lib/spout/commands/project_generator.rb
120
+ - lib/spout/commands/update.rb
120
121
  - lib/spout/helpers/array_statistics.rb
121
122
  - lib/spout/helpers/chart_types.rb
122
123
  - lib/spout/helpers/config_reader.rb
123
124
  - lib/spout/helpers/iterators.rb
124
125
  - lib/spout/helpers/json_loader.rb
125
126
  - lib/spout/helpers/json_request.rb
126
- - lib/spout/helpers/json_request_generic.rb
127
127
  - lib/spout/helpers/number_helper.rb
128
128
  - lib/spout/helpers/quietly.rb
129
129
  - lib/spout/helpers/semantic.rb
@@ -156,12 +156,15 @@ files:
156
156
  - lib/spout/models/variable.rb
157
157
  - lib/spout/tasks.rb
158
158
  - lib/spout/tasks/engine.rake
159
+ - lib/spout/templates/CHANGELOG.md.erb
159
160
  - lib/spout/templates/Gemfile
161
+ - lib/spout/templates/README.md.erb
160
162
  - lib/spout/templates/Rakefile
163
+ - lib/spout/templates/VERSION
161
164
  - lib/spout/templates/gitignore
162
165
  - lib/spout/templates/keep
163
166
  - lib/spout/templates/ruby-version
164
- - lib/spout/templates/spout.yml
167
+ - lib/spout/templates/spout.yml.erb
165
168
  - lib/spout/templates/test/dictionary_test.rb
166
169
  - lib/spout/templates/test/test_helper.rb
167
170
  - lib/spout/templates/travis.yml
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openssl'
4
- require 'net/http'
5
- require 'json'
6
- require 'cgi'
7
-
8
- module Spout
9
- module Helpers
10
- class JsonRequestGeneric
11
- class << self
12
- def get(url, *args)
13
- new(url, *args).get
14
- end
15
-
16
- def post(url, *args)
17
- new(url, *args).post
18
- end
19
-
20
- def patch(url, *args)
21
- new(url, *args).patch
22
- end
23
- end
24
-
25
- attr_reader :url
26
-
27
- def initialize(url, args = {})
28
- @params = nested_hash_to_params(args)
29
- @url = URI.parse(url)
30
-
31
- @http = Net::HTTP.new(@url.host, @url.port)
32
- if @url.scheme == 'https'
33
- @http.use_ssl = true
34
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
- end
36
- rescue => e
37
- puts "Error sending JsonRequestGeneric: #{e}".colorize(:red)
38
- end
39
-
40
- def get
41
- full_path = @url.path
42
- query = ([@url.query] + @params).flatten.compact.join('&')
43
- full_path += "?#{query}" if query.to_s != ''
44
- response = @http.start do |http|
45
- http.get(full_path)
46
- end
47
- [JSON.parse(response.body), response]
48
- rescue => e
49
- puts "GET Error: #{e}".colorize(:red)
50
- end
51
-
52
- def post
53
- response = @http.start do |http|
54
- http.post(@url.path, @params.flatten.compact.join('&'))
55
- end
56
- [JSON.parse(response.body), response]
57
- rescue => e
58
- puts "POST ERROR: #{e}".colorize(:red)
59
- nil
60
- end
61
-
62
- def patch
63
- @params << '_method=patch'
64
- post
65
- end
66
-
67
- def nested_hash_to_params(args)
68
- args.collect do |key, value|
69
- key_value_to_string(key, value, nil)
70
- end
71
- end
72
-
73
- def key_value_to_string(key, value, scope = nil)
74
- current_scope = (scope ? "#{scope}[#{key}]" : key)
75
- if value.is_a? Hash
76
- value.collect do |k,v|
77
- key_value_to_string(k, v, current_scope)
78
- end.join('&')
79
- elsif value.is_a? Array
80
- value.collect do |v|
81
- key_value_to_string('', v, current_scope)
82
- end
83
- else
84
- "#{current_scope}=#{CGI.escape(value.to_s)}"
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,3 +0,0 @@
1
- # slug: myrepo
2
- visit: visit
3
- charts: []