squib 0.8.0 → 0.9.0

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.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +14 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +201 -175
  5. data/Gemfile +2 -4
  6. data/README.md +650 -645
  7. data/RELEASE TODO.md +18 -18
  8. data/Rakefile +106 -99
  9. data/appveyor.yml +29 -0
  10. data/lib/squib.rb +32 -32
  11. data/lib/squib/api/background.rb +20 -20
  12. data/lib/squib/api/data.rb +131 -131
  13. data/lib/squib/api/image.rb +108 -90
  14. data/lib/squib/api/save.rb +151 -149
  15. data/lib/squib/api/settings.rb +35 -35
  16. data/lib/squib/api/shapes.rb +255 -230
  17. data/lib/squib/api/text.rb +65 -65
  18. data/lib/squib/api/text_embed.rb +96 -96
  19. data/lib/squib/args/arg_loader.rb +138 -138
  20. data/lib/squib/args/box.rb +54 -54
  21. data/lib/squib/args/card_range.rb +32 -32
  22. data/lib/squib/args/color_validator.rb +11 -11
  23. data/lib/squib/args/coords.rb +32 -32
  24. data/lib/squib/args/dir_validator.rb +16 -16
  25. data/lib/squib/args/draw.rb +92 -92
  26. data/lib/squib/args/embed_adjust.rb +25 -25
  27. data/lib/squib/args/embed_key.rb +17 -17
  28. data/lib/squib/args/hand_special.rb +37 -37
  29. data/lib/squib/args/import.rb +39 -39
  30. data/lib/squib/args/input_file.rb +37 -37
  31. data/lib/squib/args/paint.rb +43 -43
  32. data/lib/squib/args/paragraph.rb +116 -115
  33. data/lib/squib/args/save_batch.rb +63 -60
  34. data/lib/squib/args/scale_box.rb +53 -53
  35. data/lib/squib/args/sheet.rb +72 -72
  36. data/lib/squib/args/showcase_special.rb +38 -38
  37. data/lib/squib/args/svg_special.rb +37 -37
  38. data/lib/squib/args/transform.rb +60 -24
  39. data/lib/squib/args/typographer.rb +117 -117
  40. data/lib/squib/card.rb +66 -67
  41. data/lib/squib/conf.rb +131 -117
  42. data/lib/squib/constants.rb +12 -178
  43. data/lib/squib/deck.rb +113 -113
  44. data/lib/squib/graphics/cairo_context_wrapper.rb +113 -99
  45. data/lib/squib/graphics/gradient_regex.rb +46 -46
  46. data/lib/squib/graphics/hand.rb +42 -42
  47. data/lib/squib/graphics/image.rb +103 -76
  48. data/lib/squib/graphics/save_doc.rb +103 -103
  49. data/lib/squib/graphics/save_images.rb +39 -33
  50. data/lib/squib/graphics/shapes.rb +135 -119
  51. data/lib/squib/graphics/showcase.rb +85 -85
  52. data/lib/squib/graphics/text.rb +176 -176
  53. data/lib/squib/layout_parser.rb +91 -91
  54. data/lib/squib/layouts/economy.yml +85 -85
  55. data/lib/squib/layouts/fantasy.yml +101 -101
  56. data/lib/squib/layouts/hand.yml +62 -62
  57. data/lib/squib/layouts/playing-card.yml +35 -35
  58. data/lib/squib/layouts/tuck_box.yml +45 -45
  59. data/lib/squib/project_template/IDEAS.md +22 -0
  60. data/lib/squib/project_template/PLAYTESTING.md +26 -0
  61. data/lib/squib/project_template/RULES.md +21 -0
  62. data/lib/squib/project_template/config.yml +49 -45
  63. data/lib/squib/sample_helpers.rb +34 -0
  64. data/lib/squib/version.rb +10 -10
  65. data/samples/autoscale_font/_autoscale_font.rb +29 -0
  66. data/samples/color_shortcuts.rb +6 -6
  67. data/samples/csv_import.rb +26 -26
  68. data/samples/custom-config.yml +5 -5
  69. data/samples/custom_config.rb +18 -18
  70. data/samples/draw_shapes.rb +48 -45
  71. data/samples/embed_text.rb +88 -88
  72. data/samples/excel.rb +55 -55
  73. data/samples/hand.rb +24 -24
  74. data/samples/images/_images.rb +104 -0
  75. data/samples/intro/01_hello.rb +9 -0
  76. data/samples/intro/02_options.rb +15 -0
  77. data/samples/intro/03_layout.rb +12 -0
  78. data/samples/intro/04_arrays.rb +16 -0
  79. data/samples/intro/05_excel.rb +15 -0
  80. data/samples/layouts.rb +62 -62
  81. data/samples/layouts_builtin.rb +51 -51
  82. data/samples/load_images.rb +99 -78
  83. data/samples/load_images_config.yml +1 -0
  84. data/samples/quantity_explosion.csv +2 -2
  85. data/samples/ranges.rb +64 -64
  86. data/samples/sample.csv +2 -2
  87. data/samples/saves.rb +9 -1
  88. data/samples/sprites.png +0 -0
  89. data/samples/text/_text.rb +46 -0
  90. data/samples/text_options.rb +102 -102
  91. data/spec/api/api_data_spec.rb +117 -117
  92. data/spec/api/api_settings_spec.rb +37 -37
  93. data/spec/args/box_spec.rb +127 -127
  94. data/spec/args/draw_spec.rb +101 -95
  95. data/spec/args/embed_key_spec.rb +13 -13
  96. data/spec/args/input_file_spec.rb +21 -21
  97. data/spec/args/paint_spec.rb +21 -21
  98. data/spec/args/paragraph_spec.rb +152 -152
  99. data/spec/args/range_spec.rb +40 -40
  100. data/spec/args/save_batch_spec.rb +51 -51
  101. data/spec/args/scale_box_spec.rb +71 -71
  102. data/spec/args/sheet_spec.rb +58 -58
  103. data/spec/args/showcase_special_spec.rb +15 -15
  104. data/spec/args/transform_spec.rb +25 -0
  105. data/spec/card_spec.rb +11 -0
  106. data/spec/conf_spec.rb +13 -3
  107. data/spec/data/conf/unrecognized.yml +4 -0
  108. data/spec/data/csv/qty.csv +2 -2
  109. data/spec/data/csv/qty_named.csv +2 -2
  110. data/spec/data/csv/with_spaces.csv +2 -2
  111. data/spec/data/samples/autoscale_font.rb.txt +84 -84
  112. data/spec/data/samples/basic.rb.txt +227 -209
  113. data/spec/data/samples/config_text_markup.rb.txt +72 -72
  114. data/spec/data/samples/csv_import.rb.txt +213 -213
  115. data/spec/data/samples/custom_config.rb.txt +57 -48
  116. data/spec/data/samples/draw_shapes.rb.txt +555 -3
  117. data/spec/data/samples/embed_text.rb.txt +283 -283
  118. data/spec/data/samples/excel.rb.txt +661 -661
  119. data/spec/data/samples/gradients.rb.txt +77 -79
  120. data/spec/data/samples/hand.rb.txt +538 -538
  121. data/spec/data/samples/hello_world.rb.txt +36 -36
  122. data/spec/data/samples/load_images.rb.txt +170 -0
  123. data/spec/data/samples/portrait-landscape.rb.txt +51 -49
  124. data/spec/data/samples/ranges.rb.txt +472 -460
  125. data/spec/data/samples/saves.rb.txt +810 -801
  126. data/spec/data/samples/showcase.rb.txt +5926 -5910
  127. data/spec/data/samples/text_options.rb.txt +1125 -1125
  128. data/spec/data/samples/tgc_proofs.rb.txt +95 -81
  129. data/spec/graphics/cairo_context_wrapper_spec.rb +104 -84
  130. data/spec/graphics/graphics_save_doc_spec.rb +67 -67
  131. data/spec/samples/diff-with-css.example.html +39 -0
  132. data/spec/samples/expected/load_images_00.png +0 -0
  133. data/spec/samples/expected/shape_00.png +0 -0
  134. data/spec/samples/run_samples_spec.rb +17 -0
  135. data/spec/samples/samples_regression_spec.rb +72 -82
  136. data/spec/spec_helper.rb +9 -1
  137. data/squib.gemspec +49 -48
  138. data/squib.sublime-project +42 -42
  139. metadata +94 -48
  140. data/spec/graphics/graphics_images_spec.rb +0 -94
@@ -1,18 +1,18 @@
1
- Be sure to remember to do the following for releases. (Copy into a GitHub issue)
2
-
3
- - [ ] CHANGELOG is written for all new changes
4
- - [ ] README is updated
5
- - [ ] Samples are updated
6
- - [ ] `rake doc`
7
- - [ ] Check `sample_regression_spec.rb` regression tests are all enabled (i.e. `overwrite_sample` is commented out)
8
- - [ ] Bump version.rb
9
- - [ ] Do a full rake locally
10
- - [ ] `rake sanity`, and check visually
11
- - [ ] Travis is passing on dev branch
12
- - [ ] Merge master branch
13
- - [ ] Create GitHub release tag
14
- - [ ] `gem push pkg/squib-x.y.z.gem`
15
- - [ ] Github milestone closed
16
- - [ ] Push `rake doc` to website
17
- - [ ] Bump version.rb to the next alpha
18
- - [ ] Publish on BoardGameGeek thread
1
+ Be sure to remember to do the following for releases. (Copy into a GitHub issue)
2
+
3
+ - [ ] CHANGELOG is written for all new changes
4
+ - [ ] README is updated
5
+ - [ ] Samples are updated
6
+ - [ ] `rake doc`
7
+ - [ ] Check `sample_regression_spec.rb` regression tests are all enabled (i.e. `overwrite_sample` is commented out)
8
+ - [ ] Bump version.rb
9
+ - [ ] Do a full rake locally
10
+ - [ ] `rake sanity`, and check visually
11
+ - [ ] Travis is passing on dev branch
12
+ - [ ] Merge master branch
13
+ - [ ] Create GitHub release tag
14
+ - [ ] `gem push pkg/squib-x.y.z.gem`
15
+ - [ ] Github milestone closed
16
+ - [ ] Push `rake doc` to website
17
+ - [ ] Bump version.rb to the next alpha
18
+ - [ ] Publish on BoardGameGeek thread
data/Rakefile CHANGED
@@ -1,99 +1,106 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
- require 'yard'
4
- require 'benchmark'
5
- require 'byebug'
6
-
7
- task default: [:install, :spec]
8
-
9
- # Useful for hooking up with SublimeText.
10
- # e.g. rake sample[basic.rb]
11
- task :run,[:file] => :install do |t, args|
12
- args.with_defaults(file: 'basic.rb')
13
- Dir.chdir('samples') do
14
- args[:file] << ".rb" unless args[:file].end_with? '.rb'
15
- puts "Running samples/#{args[:file]}"
16
- load args[:file]
17
- end
18
- end
19
-
20
-
21
- RSpec::Core::RakeTask.new(:spec)
22
-
23
- RSpec::Core::RakeTask.new(:spec_fastonly) do |t|
24
- t.rspec_opts = "--tag ~slow"
25
- end
26
-
27
- task doc: [:yarddoc, :apply_google_analytics]
28
-
29
- YARD::Rake::YardocTask.new(:yarddoc) do |t|
30
- t.files = ['lib/**/*.rb', 'samples/**/*.rb'] # optional
31
- #t.options = ['--any', '--extra', '--opts'] # optional
32
- end
33
-
34
- task benchmark: [:install] do
35
- require 'squib'
36
- Squib::logger.level = Logger::ERROR #silence warnings
37
- Dir.chdir('benchmarks') do
38
- Benchmark.bm(15) do |bm|
39
- Dir['*.rb'].each do | script |
40
- GC.start
41
- bm.report(script) { load script }
42
- end
43
- end
44
- end
45
- end
46
-
47
- task :sanity_only do
48
- require_relative 'spec/samples/sanity.rb'
49
- Sanity.new.run
50
- end
51
-
52
- task sanity: [:install, :spec, :sanity_only]
53
-
54
- task :apply_google_analytics do
55
- # The string to replace in the html document. This is chosen to be the end
56
- # body </body> tag. So the script can be injected as the last thing in the
57
- # document body.
58
- string_to_replace = "</body>"
59
- # This is the string to replace with. It include the google analytics script
60
- # as well as the end </body> tag.
61
- string_to_replace_with = <<-EOF
62
- <script>
63
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
64
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
65
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
66
- })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
67
-
68
- ga('create', 'UA-54811605-1', 'auto');
69
- ga('send', 'pageview');
70
-
71
- </script>
72
- </body>
73
- EOF
74
-
75
- files = Dir.glob("doc/**/*.html")
76
-
77
- files.each do |html_file|
78
- puts "Processing file: #{html_file}"
79
- contents = ""
80
- # Read the file contents
81
- file = File.open(html_file)
82
- file.each { |line| contents << line }
83
- file.close
84
-
85
- # If the file already has google analytics tracking info, skip it.
86
- if contents.include?(string_to_replace_with)
87
- puts "Skipped..."
88
- next
89
- end
90
-
91
- # Apply google analytics tracking info to the html file
92
- contents.gsub!(string_to_replace, string_to_replace_with)
93
-
94
- # Write the contents with the google analytics info to the file
95
- file = File.open(html_file, "w")
96
- file.write(contents)
97
- file.close
98
- end
99
- end
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+ require 'benchmark'
5
+ # require 'byebug'
6
+
7
+ desc 'install + spec'
8
+ task default: [:install, :spec]
9
+
10
+ # Useful for hooking up with SublimeText.
11
+ # e.g. rake sample[basic.rb]
12
+ desc 'Run a specific sample'
13
+ task :run,[:file] => :install do |t, args|
14
+ args.with_defaults(file: 'basic.rb')
15
+ Dir.chdir('samples') do
16
+ args[:file] << ".rb" unless args[:file].end_with? '.rb'
17
+ puts "Running samples/#{args[:file]}"
18
+ load args[:file]
19
+ end
20
+ end
21
+
22
+
23
+ RSpec::Core::RakeTask.new(:spec)
24
+
25
+ RSpec::Core::RakeTask.new(:spec_fastonly) do |t|
26
+ t.rspec_opts = "--tag ~slow"
27
+ end
28
+
29
+ desc 'Build API docs'
30
+ task doc: [:yarddoc, :apply_google_analytics]
31
+
32
+ YARD::Rake::YardocTask.new(:yarddoc) do |t|
33
+ t.files = ['lib/**/*.rb', 'samples/**/*.rb'] # optional
34
+ #t.options = ['--any', '--extra', '--opts'] # optional
35
+ end
36
+
37
+ desc 'Run some performance benchmarks'
38
+ task benchmark: [:install] do
39
+ require 'squib'
40
+ Squib::logger.level = Logger::ERROR #silence warnings
41
+ Dir.chdir('benchmarks') do
42
+ Benchmark.bm(15) do |bm|
43
+ Dir['*.rb'].each do | script |
44
+ GC.start
45
+ bm.report(script) { load script }
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ desc 'Run sanity tests without a full rebuild'
52
+ task :sanity_only do
53
+ require_relative 'spec/samples/sanity.rb'
54
+ Sanity.new.run
55
+ end
56
+
57
+ desc 'Run full rebuild with sanity tests'
58
+ task sanity: [:install, :spec, :sanity_only]
59
+
60
+ desc 'Insert Google Analytics into documentation build'
61
+ task :apply_google_analytics do
62
+ # The string to replace in the html document. This is chosen to be the end
63
+ # body </body> tag. So the script can be injected as the last thing in the
64
+ # document body.
65
+ string_to_replace = "</body>"
66
+ # This is the string to replace with. It include the google analytics script
67
+ # as well as the end </body> tag.
68
+ string_to_replace_with = <<-EOF
69
+ <script>
70
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
71
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
72
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
73
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
74
+
75
+ ga('create', 'UA-54811605-1', 'auto');
76
+ ga('send', 'pageview');
77
+
78
+ </script>
79
+ </body>
80
+ EOF
81
+
82
+ files = Dir.glob("doc/**/*.html")
83
+
84
+ files.each do |html_file|
85
+ puts "Processing file: #{html_file}"
86
+ contents = ""
87
+ # Read the file contents
88
+ file = File.open(html_file)
89
+ file.each { |line| contents << line }
90
+ file.close
91
+
92
+ # If the file already has google analytics tracking info, skip it.
93
+ if contents.include?(string_to_replace_with)
94
+ puts "Skipped..."
95
+ next
96
+ end
97
+
98
+ # Apply google analytics tracking info to the html file
99
+ contents.gsub!(string_to_replace, string_to_replace_with)
100
+
101
+ # Write the contents with the google analytics info to the file
102
+ file = File.open(html_file, "w")
103
+ file.write(contents)
104
+ file.close
105
+ end
106
+ end
@@ -0,0 +1,29 @@
1
+ version: '{build}'
2
+
3
+ skip_tags: true
4
+
5
+ environment:
6
+ matrix:
7
+ - ruby_version: "200"
8
+ - ruby_version: "200-x64"
9
+ - ruby_version: "21"
10
+ - ruby_version: "21-x64"
11
+ - ruby_version: "22"
12
+ - ruby_version: "22-x64"
13
+
14
+ install:
15
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
16
+ - ruby --version
17
+ - gem --version
18
+ - gem install bundler --quiet --no-ri --no-rdoc
19
+ - bundler --version
20
+ - bundle install
21
+ - git submodule update --init --recursive
22
+
23
+ test_script:
24
+ - rake
25
+
26
+ artifacts:
27
+ - path: pkg\*.gem
28
+
29
+ build: off
@@ -1,32 +1,32 @@
1
- require 'logger'
2
- require 'cairo'
3
- require 'pango'
4
- require 'rsvg2'
5
- require 'squib/version'
6
- require 'squib/commands/new'
7
- require 'squib/deck'
8
- require 'squib/card'
9
-
10
- module Squib
11
-
12
- # Access the internal logger that Squib uses. By default, Squib configure the logger to the WARN level
13
- # Use this to suppress or increase output levels.
14
- # @example
15
- # Squib.logger.level = Logger::DEBUG #show waaaay more information than you probably need, unless you're a dev
16
- # Squib.logger.level = Logger::ERROR #basically turns it off
17
- #
18
- # @return [Logger] the ruby logger
19
- # @api public
20
- def logger
21
- if @logger.nil?
22
- @logger = Logger.new($stdout)
23
- @logger.level = Logger::WARN
24
- @logger.formatter = proc do |severity, datetime, m_progname, msg|
25
- "#{datetime} #{severity}: #{msg}\n"
26
- end
27
- end
28
- @logger
29
- end
30
- module_function :logger
31
-
32
- end
1
+ require 'logger'
2
+ require 'cairo'
3
+ require 'pango'
4
+ require 'rsvg2'
5
+ require 'squib/version'
6
+ require 'squib/commands/new'
7
+ require 'squib/deck'
8
+ require 'squib/card'
9
+
10
+ module Squib
11
+
12
+ # Access the internal logger that Squib uses. By default, Squib configure the logger to the WARN level
13
+ # Use this to suppress or increase output levels.
14
+ # @example
15
+ # Squib.logger.level = Logger::DEBUG #show waaaay more information than you probably need, unless you're a dev
16
+ # Squib.logger.level = Logger::ERROR #basically turns it off
17
+ #
18
+ # @return [Logger] the ruby logger
19
+ # @api public
20
+ def logger
21
+ if @logger.nil?
22
+ @logger = Logger.new($stdout)
23
+ @logger.level = Logger::WARN
24
+ @logger.formatter = proc do |severity, datetime, m_progname, msg|
25
+ "#{datetime} #{severity}: #{msg}\n"
26
+ end
27
+ end
28
+ @logger
29
+ end
30
+ module_function :logger
31
+
32
+ end
@@ -1,20 +1,20 @@
1
- module Squib
2
- class Deck
3
- # Fills the background with the given color
4
- # @example
5
- # background color: :white
6
- #
7
- # Options support Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
8
- #
9
- # @option opts range [Enumerable] (:all) the range of cards over which this will be rendered. See {file:README.md#Specifying_Ranges Specifying Ranges}
10
- # @option opts color [String] (:black) the color the font will render to. See {file:README.md#Specifying_Colors___Gradients Specifying Colors & Gradients}.
11
- # @return [nil] nothing
12
- # @api public
13
- def background(opts = {})
14
- range = Args::CardRange.new(opts[:range], deck_size: size)
15
- draw = Args::Draw.new(custom_colors).load!(opts, expand_by: size, layout: layout, dpi: dpi)
16
- range.each { |i| @cards[i].background(draw.color[i]) }
17
- end
18
-
19
- end
20
- end
1
+ module Squib
2
+ class Deck
3
+ # Fills the background with the given color
4
+ # @example
5
+ # background color: :white
6
+ #
7
+ # Options support Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion}
8
+ #
9
+ # @option opts range [Enumerable] (:all) the range of cards over which this will be rendered. See {file:README.md#Specifying_Ranges Specifying Ranges}
10
+ # @option opts color [String] (:black) the color the font will render to. See {file:README.md#Specifying_Colors___Gradients Specifying Colors & Gradients}.
11
+ # @return [nil] nothing
12
+ # @api public
13
+ def background(opts = {})
14
+ range = Args::CardRange.new(opts[:range], deck_size: size)
15
+ draw = Args::Draw.new(custom_colors).load!(opts, expand_by: size, layout: layout, dpi: dpi)
16
+ range.each { |i| @cards[i].background(draw.color[i]) }
17
+ end
18
+
19
+ end
20
+ end
@@ -1,131 +1,131 @@
1
- require 'roo'
2
- require 'csv'
3
- require 'squib/args/input_file'
4
- require 'squib/args/import'
5
-
6
- module Squib
7
-
8
- # Pulls Excel data from `.xlsx` files into a column-based hash
9
- #
10
- # Pulls the data into a Hash of arrays based on the columns. First row is assumed to be the header row.
11
- # See the example `samples/excel.rb` in the [source repository](https://github.com/andymeneely/squib/tree/master/samples)
12
- #
13
- # @example
14
- # # Excel file looks like this:
15
- # # | h1 | h2 |
16
- # # ------------
17
- # # | 1 | 2 |
18
- # # | 3 | 4 |
19
- # data = xlsx file: 'data.xlsx', sheet: 0
20
- # => {'h1' => [1,3], 'h2' => [2,4]}
21
- #
22
- # @option opts file [String] the file to open. Must end in `.xlsx`. Opens relative to the current directory.
23
- # @option opts sheet [Integer] (0) The zero-based index of the sheet from which to read.
24
- # @option opts strip [Boolean] (true) When true, strips leading and trailing whitespace on values and headers
25
- # @option opts explode [String] ('qty') Quantity explosion will be applied to the column this name. See README for example.
26
- # @return [Hash] a hash of arrays based on columns in the spreadsheet
27
- # @api public
28
- def xlsx(opts = {})
29
- input = Args::InputFile.new(file: 'deck.xlsx').load!(opts)
30
- import = Args::Import.new.load!(opts)
31
- s = Roo::Excelx.new(input.file[0])
32
- s.default_sheet = s.sheets[input.sheet[0]]
33
- data = {}
34
- s.first_column.upto(s.last_column) do |col|
35
- header = s.cell(s.first_row,col).to_s
36
- header.strip! if import.strip?
37
- data[header] = []
38
- (s.first_row + 1).upto(s.last_row) do |row|
39
- cell = s.cell(row,col)
40
- # Roo hack for avoiding unnecessary .0's on whole integers (https://github.com/roo-rb/roo/issues/139)
41
- cell = s.excelx_value(row,col) if s.excelx_type(row,col) == [:numeric_or_formula, 'General']
42
- cell.strip! if cell.respond_to?(:strip) && import.strip?
43
- cell = yield(header, cell) if block_given?
44
- data[header] << cell
45
- end#row
46
- end#col
47
- explode_quantities(data, import.explode)
48
- end#xlsx
49
- module_function :xlsx
50
-
51
- # Pulls CSV data from `.csv` files into a column-based hash
52
- #
53
- # Pulls the data into a Hash of arrays based on the columns. First row is assumed to be the header row.
54
- # See the example `samples/csv.rb` in the [source repository](https://github.com/andymeneely/squib/tree/master/samples)
55
- #
56
- # @example
57
- # # File data.csv looks like this (without the comment symbols)
58
- # # h1,h2
59
- # # 1,2
60
- # # 3,4
61
- # data = csv file: 'data.csv'
62
- # => {'h1' => [1,3], 'h2' => [2,4]}
63
- #
64
- # Parsing uses Ruby's CSV, with options `{headers: true, converters: :numeric}`
65
- # http://www.ruby-doc.org/stdlib-2.0/libdoc/csv/rdoc/CSV.html
66
- #
67
- # @option opts file [String] the CSV-formatted file to open. Opens relative to the current directory.
68
- # @option opts strip [Boolean] (true) When true, strips leading and trailing whitespace on values and headers
69
- # @option opts explode [String] ('qty') Quantity explosion will be applied to the column this name. See README for example.
70
- # @return [Hash] a hash of arrays based on columns in the table
71
- # @api public
72
- def csv(opts = {})
73
- file = Args::InputFile.new(file: 'deck.csv').load!(opts).file[0]
74
- import = Args::Import.new.load!(opts)
75
- table = CSV.read(file, headers: true, converters: :numeric)
76
- check_duplicate_csv_headers(table)
77
- hash = Hash.new
78
- table.headers.each do |header|
79
- new_header = header.to_s
80
- new_header = new_header.strip if import.strip?
81
- hash[new_header] ||= table[header]
82
- end
83
- if import.strip?
84
- new_hash = Hash.new
85
- hash.each do |header, col|
86
- new_hash[header] = col.map { |str| str = str.strip if str.respond_to?(:strip); str }
87
- end
88
- hash = new_hash
89
- end
90
- return explode_quantities(hash, import.explode)
91
- end
92
- module_function :csv
93
-
94
- # Check if the given CSV table has duplicate columns, and throw a warning
95
- # @api private
96
- def check_duplicate_csv_headers(table)
97
- if table.headers.size != table.headers.uniq.size
98
- dups = table.headers.select{|e| table.headers.count(e) > 1 }
99
- Squib.logger.warn "CSV duplicated the following column keys: #{dups.join(',')}"
100
- end
101
- end
102
- module_function :check_duplicate_csv_headers
103
-
104
- def explode_quantities(data, qty)
105
- return data unless data.key? qty.to_s.strip
106
- qtys = data[qty]
107
- new_data = {}
108
- data.each do |col, arr|
109
- new_data[col] = []
110
- qtys.each_with_index do |qty, index|
111
- qty.to_i.times { new_data[col] << arr[index] }
112
- end
113
- end
114
- return new_data
115
- end
116
- module_function :explode_quantities
117
-
118
- class Deck
119
-
120
- # Convenience call on deck goes to the module function
121
- def xlsx(opts = {})
122
- Squib.xlsx(opts)
123
- end
124
-
125
- # Convenience call on deck goes to the module function
126
- def csv(opts = {})
127
- Squib.csv(opts)
128
- end
129
-
130
- end
131
- end
1
+ require 'roo'
2
+ require 'csv'
3
+ require 'squib/args/input_file'
4
+ require 'squib/args/import'
5
+
6
+ module Squib
7
+
8
+ # Pulls Excel data from `.xlsx` files into a column-based hash
9
+ #
10
+ # Pulls the data into a Hash of arrays based on the columns. First row is assumed to be the header row.
11
+ # See the example `samples/excel.rb` in the [source repository](https://github.com/andymeneely/squib/tree/master/samples)
12
+ #
13
+ # @example
14
+ # # Excel file looks like this:
15
+ # # | h1 | h2 |
16
+ # # ------------
17
+ # # | 1 | 2 |
18
+ # # | 3 | 4 |
19
+ # data = xlsx file: 'data.xlsx', sheet: 0
20
+ # => {'h1' => [1,3], 'h2' => [2,4]}
21
+ #
22
+ # @option opts file [String] the file to open. Must end in `.xlsx`. Opens relative to the current directory.
23
+ # @option opts sheet [Integer] (0) The zero-based index of the sheet from which to read.
24
+ # @option opts strip [Boolean] (true) When true, strips leading and trailing whitespace on values and headers
25
+ # @option opts explode [String] ('qty') Quantity explosion will be applied to the column this name. See README for example.
26
+ # @return [Hash] a hash of arrays based on columns in the spreadsheet
27
+ # @api public
28
+ def xlsx(opts = {})
29
+ input = Args::InputFile.new(file: 'deck.xlsx').load!(opts)
30
+ import = Args::Import.new.load!(opts)
31
+ s = Roo::Excelx.new(input.file[0])
32
+ s.default_sheet = s.sheets[input.sheet[0]]
33
+ data = {}
34
+ s.first_column.upto(s.last_column) do |col|
35
+ header = s.cell(s.first_row,col).to_s
36
+ header.strip! if import.strip?
37
+ data[header] = []
38
+ (s.first_row + 1).upto(s.last_row) do |row|
39
+ cell = s.cell(row,col)
40
+ # Roo hack for avoiding unnecessary .0's on whole integers (https://github.com/roo-rb/roo/issues/139)
41
+ cell = s.excelx_value(row,col) if s.excelx_type(row,col) == [:numeric_or_formula, 'General']
42
+ cell.strip! if cell.respond_to?(:strip) && import.strip?
43
+ cell = yield(header, cell) if block_given?
44
+ data[header] << cell
45
+ end#row
46
+ end#col
47
+ explode_quantities(data, import.explode)
48
+ end#xlsx
49
+ module_function :xlsx
50
+
51
+ # Pulls CSV data from `.csv` files into a column-based hash
52
+ #
53
+ # Pulls the data into a Hash of arrays based on the columns. First row is assumed to be the header row.
54
+ # See the example `samples/csv.rb` in the [source repository](https://github.com/andymeneely/squib/tree/master/samples)
55
+ #
56
+ # @example
57
+ # # File data.csv looks like this (without the comment symbols)
58
+ # # h1,h2
59
+ # # 1,2
60
+ # # 3,4
61
+ # data = csv file: 'data.csv'
62
+ # => {'h1' => [1,3], 'h2' => [2,4]}
63
+ #
64
+ # Parsing uses Ruby's CSV, with options `{headers: true, converters: :numeric}`
65
+ # http://www.ruby-doc.org/stdlib-2.0/libdoc/csv/rdoc/CSV.html
66
+ #
67
+ # @option opts file [String] the CSV-formatted file to open. Opens relative to the current directory.
68
+ # @option opts strip [Boolean] (true) When true, strips leading and trailing whitespace on values and headers
69
+ # @option opts explode [String] ('qty') Quantity explosion will be applied to the column this name. See README for example.
70
+ # @return [Hash] a hash of arrays based on columns in the table
71
+ # @api public
72
+ def csv(opts = {})
73
+ file = Args::InputFile.new(file: 'deck.csv').load!(opts).file[0]
74
+ import = Args::Import.new.load!(opts)
75
+ table = CSV.read(file, headers: true, converters: :numeric)
76
+ check_duplicate_csv_headers(table)
77
+ hash = Hash.new
78
+ table.headers.each do |header|
79
+ new_header = header.to_s
80
+ new_header = new_header.strip if import.strip?
81
+ hash[new_header] ||= table[header]
82
+ end
83
+ if import.strip?
84
+ new_hash = Hash.new
85
+ hash.each do |header, col|
86
+ new_hash[header] = col.map { |str| str = str.strip if str.respond_to?(:strip); str }
87
+ end
88
+ hash = new_hash
89
+ end
90
+ return explode_quantities(hash, import.explode)
91
+ end
92
+ module_function :csv
93
+
94
+ # Check if the given CSV table has duplicate columns, and throw a warning
95
+ # @api private
96
+ def check_duplicate_csv_headers(table)
97
+ if table.headers.size != table.headers.uniq.size
98
+ dups = table.headers.select{|e| table.headers.count(e) > 1 }
99
+ Squib.logger.warn "CSV duplicated the following column keys: #{dups.join(',')}"
100
+ end
101
+ end
102
+ module_function :check_duplicate_csv_headers
103
+
104
+ def explode_quantities(data, qty)
105
+ return data unless data.key? qty.to_s.strip
106
+ qtys = data[qty]
107
+ new_data = {}
108
+ data.each do |col, arr|
109
+ new_data[col] = []
110
+ qtys.each_with_index do |qty, index|
111
+ qty.to_i.times { new_data[col] << arr[index] }
112
+ end
113
+ end
114
+ return new_data
115
+ end
116
+ module_function :explode_quantities
117
+
118
+ class Deck
119
+
120
+ # Convenience call on deck goes to the module function
121
+ def xlsx(opts = {})
122
+ Squib.xlsx(opts)
123
+ end
124
+
125
+ # Convenience call on deck goes to the module function
126
+ def csv(opts = {})
127
+ Squib.csv(opts)
128
+ end
129
+
130
+ end
131
+ end