laser-cutter 1.0.3 → 2.0.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 (102) hide show
  1. checksums.yaml +5 -5
  2. data/.envrc +2 -0
  3. data/.github/workflows/lint.yml +22 -0
  4. data/.github/workflows/rspec.yml +22 -0
  5. data/.gitignore +5 -4
  6. data/.plans/001.00-dry-cli-migration/plan.md +29 -0
  7. data/.relaxed_rubocop.yml +153 -0
  8. data/.rspec +1 -1
  9. data/.rubocop.yml +31 -0
  10. data/.rubocop_todo.yml +90 -0
  11. data/.ruby-version +1 -0
  12. data/CLAUDE.md +62 -0
  13. data/CONTRIBUTING.md +2 -4
  14. data/Gemfile +14 -0
  15. data/README.md +83 -111
  16. data/Rakefile +41 -0
  17. data/docs/badges/coverage_badge.svg +21 -0
  18. data/exe/laser-cutter +8 -0
  19. data/exe/lc +1 -0
  20. data/justfile +97 -0
  21. data/laser-cutter.gemspec +26 -14
  22. data/lib/laser/cutter/aggregator.rb +101 -0
  23. data/lib/{laser-cutter → laser/cutter}/box.rb +44 -52
  24. data/lib/laser/cutter/cli/command.rb +46 -0
  25. data/lib/laser/cutter/cli/completion.rb +21 -0
  26. data/lib/laser/cutter/cli/config_file.rb +36 -0
  27. data/lib/laser/cutter/cli/examples.rb +42 -0
  28. data/lib/laser/cutter/cli/generate.rb +90 -0
  29. data/lib/laser/cutter/cli/help.rb +20 -0
  30. data/lib/laser/cutter/cli/page_sizes.rb +19 -0
  31. data/lib/laser/cutter/cli/version.rb +15 -0
  32. data/lib/laser/cutter/cli.rb +49 -0
  33. data/lib/laser/cutter/configuration.rb +101 -0
  34. data/lib/{laser-cutter → laser/cutter}/geometry/dimensions.rb +2 -3
  35. data/lib/{laser-cutter/geometry/shape → laser/cutter/geometry}/line.rb +19 -18
  36. data/lib/{laser-cutter → laser/cutter}/geometry/point.rb +4 -2
  37. data/lib/{laser-cutter/geometry/shape → laser/cutter/geometry}/rect.rb +6 -7
  38. data/lib/{laser-cutter → laser/cutter}/geometry/shape.rb +7 -7
  39. data/lib/{laser-cutter → laser/cutter}/geometry/tuple.rb +40 -36
  40. data/lib/laser/cutter/launcher.rb +40 -0
  41. data/lib/laser/cutter/missing_option.rb +7 -0
  42. data/lib/laser/cutter/notching/base.rb +19 -0
  43. data/lib/{laser-cutter → laser/cutter}/notching/edge.rb +21 -20
  44. data/lib/laser/cutter/notching/infinite_iterator.rb +27 -0
  45. data/lib/{laser-cutter → laser/cutter}/notching/path_generator.rb +27 -60
  46. data/lib/laser/cutter/notching/shift.rb +17 -0
  47. data/lib/laser/cutter/page_manager.rb +50 -0
  48. data/lib/{laser-cutter → laser/cutter}/renderer/base.rb +3 -2
  49. data/lib/laser/cutter/renderer/box_renderer.rb +40 -0
  50. data/lib/laser/cutter/renderer/layout_renderer.rb +61 -0
  51. data/lib/laser/cutter/renderer/line_renderer.rb +21 -0
  52. data/lib/laser/cutter/renderer/meta_renderer.rb +83 -0
  53. data/lib/{laser-cutter → laser/cutter}/renderer/rect_renderer.rb +3 -1
  54. data/lib/laser/cutter/renderer/svg_renderer.rb +65 -0
  55. data/lib/laser/cutter/renderer.rb +24 -0
  56. data/lib/laser/cutter/units_converter.rb +15 -0
  57. data/lib/laser/cutter/version.rb +7 -0
  58. data/lib/laser/cutter/zero_value_not_allowed.rb +7 -0
  59. data/lib/laser/cutter.rb +29 -0
  60. data/lib/laser-cutter.rb +2 -14
  61. data/lib/laser_cutter.rb +3 -0
  62. data/spec/laser/cutter/aggregator_spec.rb +70 -0
  63. data/spec/laser/cutter/box_spec.rb +61 -0
  64. data/spec/laser/cutter/cli/config_file_spec.rb +31 -0
  65. data/spec/laser/cutter/cli_spec.rb +186 -0
  66. data/spec/laser/cutter/configuration_spec.rb +101 -0
  67. data/spec/{dimensions_spec.rb → laser/cutter/dimensions_spec.rb} +8 -6
  68. data/spec/laser/cutter/edge_spec.rb +64 -0
  69. data/spec/{line_spec.rb → laser/cutter/line_spec.rb} +26 -20
  70. data/spec/{page_manager_spec.rb → laser/cutter/page_manager_spec.rb} +27 -11
  71. data/spec/{path_generator_spec.rb → laser/cutter/path_generator_spec.rb} +18 -14
  72. data/spec/{point_spec.rb → laser/cutter/point_spec.rb} +27 -22
  73. data/spec/{rect_spec.rb → laser/cutter/rect_spec.rb} +9 -5
  74. data/spec/laser/cutter/renderer/layout_renderer_spec.rb +102 -0
  75. data/spec/laser/cutter/renderer/svg_renderer_spec.rb +51 -0
  76. data/spec/laser/cutter/renderer_spec.rb +19 -0
  77. data/spec/spec_helper.rb +34 -25
  78. data/spec/support/aruba.rb +10 -0
  79. metadata +206 -80
  80. data/.travis.yml +0 -11
  81. data/LICENSE +0 -22
  82. data/bin/laser-cutter +0 -19
  83. data/lib/laser-cutter/aggregator.rb +0 -57
  84. data/lib/laser-cutter/cli/opt_parser.rb +0 -131
  85. data/lib/laser-cutter/cli/serializer.rb +0 -51
  86. data/lib/laser-cutter/configuration.rb +0 -83
  87. data/lib/laser-cutter/geometry.rb +0 -11
  88. data/lib/laser-cutter/notching/base.rb +0 -13
  89. data/lib/laser-cutter/notching.rb +0 -10
  90. data/lib/laser-cutter/page_manager.rb +0 -43
  91. data/lib/laser-cutter/renderer/box_renderer.rb +0 -36
  92. data/lib/laser-cutter/renderer/layout_renderer.rb +0 -59
  93. data/lib/laser-cutter/renderer/line_renderer.rb +0 -17
  94. data/lib/laser-cutter/renderer/meta_renderer.rb +0 -77
  95. data/lib/laser-cutter/renderer.rb +0 -14
  96. data/lib/laser-cutter/version.rb +0 -5
  97. data/spec/aggregator_spec.rb +0 -65
  98. data/spec/box_spec.rb +0 -40
  99. data/spec/configuration_spec.rb +0 -81
  100. data/spec/edge_spec.rb +0 -43
  101. data/spec/renderer_spec.rb +0 -72
  102. /data/{doc → docs/images}/comparison.jpg +0 -0
@@ -1,22 +1,35 @@
1
- require_relative 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
2
4
 
3
5
  module Laser
4
6
  module Cutter
5
- describe PageManager do
7
+ RSpec.describe PageManager do
6
8
  let(:pm) { Laser::Cutter::PageManager.new(units) }
7
9
 
8
10
  context 'a single value' do
9
11
  context 'to inches' do
10
12
  let(:units) { 'in' }
11
- it 'should be correct' do
12
- expect(pm.value_from_units(150)).to be_within(0.0001).of(150.0/72.0)
13
- expect(pm.value_from_units(150, 'mm')).to be_within(0.0001).of(150.0/25.4)
13
+
14
+ it 'is correct' do
15
+ expect(pm.value_from_units(150)).to be_within(0.0001).of(150.0 / 72.0)
16
+ expect(pm.value_from_units(150, 'mm')).to be_within(0.0001).of(150.0 / 25.4)
14
17
  expect(pm.value_from_units(150, 'in')).to be_within(0.0001).of(150.0)
15
18
  end
16
19
  end
20
+
21
+ context 'when units are a Symbol, as the configuration defaults them' do
22
+ let(:units) { :in }
23
+
24
+ it 'reads them as inches' do
25
+ expect(pm.value_from_units(72)).to be_within(0.0001).of(1.0)
26
+ end
27
+ end
28
+
17
29
  context 'to mm' do
18
30
  let(:units) { 'mm' }
19
- it 'should be correct' do
31
+
32
+ it 'is correct' do
20
33
  expect(pm.value_from_units(150)).to be_within(0.0001).of(25.4 * 150.0 / 72.0)
21
34
  expect(pm.value_from_units(150, 'in')).to be_within(0.0001).of(150.0 * 25.4)
22
35
  expect(pm.value_from_units(150, 'mm')).to be_within(0.0001).of(150.0)
@@ -24,18 +37,21 @@ module Laser
24
37
  end
25
38
  end
26
39
 
27
- context '#list_page_sizes' do
40
+ describe '#list_page_sizes' do
28
41
  context 'formatting of output' do
29
42
  context 'when using inches' do
30
43
  let(:units) { "in" }
31
- it 'should return the list in inches' do
32
- expect(pm.all_page_sizes).to match %r(.*B10\:\s+1\.2\s+x\s+1\.7)
44
+
45
+ it 'returns the list in inches' do
46
+ expect(pm.all_page_sizes).to match /.*B10:\s+1\.2\s+x\s+1\.7/
33
47
  end
34
48
  end
49
+
35
50
  context 'when using mm' do
36
51
  let(:units) { "mm" }
37
- it 'should return the list in mm' do
38
- expect(pm.all_page_sizes).to match %r(.*B10\:\s+31\.0\s+x\s+44\.0)
52
+
53
+ it 'returns the list in mm' do
54
+ expect(pm.all_page_sizes).to match /.*B10:\s+31\.0\s+x\s+44\.0/
39
55
  end
40
56
  end
41
57
  end
@@ -1,18 +1,22 @@
1
- require_relative 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
2
4
 
3
5
  module Laser
4
6
  module Cutter
5
7
  module Notching
6
- describe PathGenerator do
8
+ RSpec.describe PathGenerator do
7
9
  let(:notch) { 2 }
8
10
  let(:thickness) { 1 }
9
11
  let(:center_out) { true }
10
12
  let(:corners) { true }
11
13
 
12
- let(:options) { {notch_width: notch,
13
- thickness: thickness,
14
- center_out: center_out,
15
- corners: corners} }
14
+ let(:options) {
15
+ { notch_width: notch,
16
+ thickness: thickness,
17
+ center_out: center_out,
18
+ corners: corners }
19
+ }
16
20
 
17
21
  let(:outside) { Geometry::Line.new(from: [0, 0], to: [10, 0]) }
18
22
  let(:inside) { Geometry::Line.new(from: [1, 1], to: [9, 1]) }
@@ -20,14 +24,16 @@ module Laser
20
24
  let(:generator) { PathGenerator.new(edge) }
21
25
 
22
26
  context 'edge' do
23
- it 'should properly calculate notch size' do
27
+ it 'properlies calculate notch size' do
24
28
  expect(edge.notch_width).to be_within(0.001).of(1.6)
25
29
  end
30
+
26
31
  context 'edge cases with the edge :)' do
27
32
  let(:notch) { 15 } # too big
28
- it 'should properly handle edge cases' do
33
+
34
+ it 'properlies handle edge cases' do
29
35
  # 3 is the minimum number of notches we support per side
30
- expect(edge.notch_width).to be_within(0.001).of(8.0/3.0)
36
+ expect(edge.notch_width).to be_within(0.001).of(8.0 / 3.0)
31
37
  end
32
38
  end
33
39
  end
@@ -35,7 +41,8 @@ module Laser
35
41
  context 'alternating iterator' do
36
42
  let(:a) { "hello" }
37
43
  let(:b) { "again" }
38
- let(:iterator) { InfiniteIterator.new([a,b]) }
44
+ let(:iterator) { InfiniteIterator.new([a, b]) }
45
+
39
46
  it 'returns things in alternating order' do
40
47
  expect(iterator.next).to eq(a)
41
48
  expect(iterator.next).to eq(b)
@@ -44,7 +51,6 @@ module Laser
44
51
  end
45
52
 
46
53
  context 'shift definition' do
47
-
48
54
  it 'correctly defines shifts' do
49
55
  shifts = generator.send(:define_shifts)
50
56
  expect(edge.outside.length).to eql(10.0)
@@ -55,7 +61,6 @@ module Laser
55
61
  end
56
62
  end
57
63
 
58
-
59
64
  context 'path generation' do
60
65
  # let(:outside) { Line.new(
61
66
  # from: inside.p1.plus(-thickness, -thickness),
@@ -63,7 +68,7 @@ module Laser
63
68
 
64
69
  context 'center out' do
65
70
  it 'generates correct path vertices' do
66
- expect(inside.p1).to_not eql(inside.p2)
71
+ expect(inside.p1).not_to eql(inside.p2)
67
72
  lines = generator.generate
68
73
  expect(lines.size).to be > 5
69
74
 
@@ -81,7 +86,6 @@ module Laser
81
86
  end
82
87
  end
83
88
  end
84
-
85
89
  end
86
90
  end
87
91
  end
@@ -1,25 +1,28 @@
1
- require_relative 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
2
4
 
3
5
  module Laser
4
6
  module Cutter
5
7
  module Geometry
6
- describe Point do
8
+ RSpec.describe Point do
7
9
  let(:p1) { Point.new(1, 2) }
10
+
8
11
  context 'creation' do
9
12
  context 'from string' do
10
- it 'should instantiate correctly' do
11
- expect(Point.new "1,2").to eql(p1)
13
+ it 'instantiates correctly' do
14
+ expect(Point.new("1,2")).to eql(p1)
12
15
  end
13
16
  end
14
17
 
15
18
  context 'from a point' do
16
- it 'should instantiate correctly' do
19
+ it 'instantiates correctly' do
17
20
  expect(Point.new(Point.new("1,2"))).to eql(p1)
18
21
  end
19
22
  end
20
23
 
21
24
  context 'from an array' do
22
- it 'should properly duplicate underlying coordinates and not bug out' do
25
+ it 'properlies duplicate underlying coordinates and not bug out' do
23
26
  a = [1, 2]
24
27
  p2 = Point.new(a)
25
28
  expect(p2).to eql(p1)
@@ -28,25 +31,26 @@ module Laser
28
31
  expect(p2).to eql(p1)
29
32
  end
30
33
 
31
- it 'should create point from class method' do
32
- expect(Point[1,2]).to eql(p1)
34
+ it 'creates point from class method' do
35
+ expect(Point[1, 2]).to eql(p1)
33
36
  end
34
37
  end
35
38
 
36
39
  context 'from a hash' do
37
- it 'should instantiate correctly' do
40
+ it 'instantiates correctly' do
38
41
  expect(Point.new(x: 1, y: 2)).to eql(p1)
39
42
  end
40
43
  end
41
44
  end
42
45
 
43
46
  context 'move by' do
44
- it 'should move properly' do
47
+ it 'moves properly' do
45
48
  p = p1.plus(10, -2)
46
49
  expect(p.x).to be_within(0.001).of(11)
47
50
  expect(p.y).to be_within(0.001).of(0)
48
51
  end
49
- it 'should move cloned version properly' do
52
+
53
+ it 'moves cloned version properly' do
50
54
  p2 = p1.clone.plus(10, -2)
51
55
  expect(p2.x).to be_within(0.001).of(11)
52
56
  expect(p2.y).to be_within(0.001).of(0)
@@ -54,20 +58,21 @@ module Laser
54
58
  end
55
59
 
56
60
  context 'ordering and equality' do
57
- let(:p1) { Point[0,0]}
58
- let(:p2) { Point[10,0]}
59
- let(:p3) { Point[0,10]}
60
- let(:p4) { Point[10,10]}
61
- let(:p5) { Point[-1,-1]}
62
- it 'should propertly sort' do
63
- expect([p3,p2,p1,p4,p5].sort).to eql([p5,p1,p3,p2,p4])
61
+ let(:p1) { Point[0, 0] }
62
+ let(:p2) { Point[10, 0] }
63
+ let(:p3) { Point[0, 10] }
64
+ let(:p4) { Point[10, 10] }
65
+ let(:p5) { Point[-1, -1] }
66
+
67
+ it 'propertlies sort' do
68
+ expect([p3, p2, p1, p4, p5].sort).to eql([p5, p1, p3, p2, p4])
64
69
  end
65
- it 'should detect equality' do
66
- expect(p1).to_not eql(p2)
67
- expect(p2).to eql(Point[10,0])
70
+
71
+ it 'detects equality' do
72
+ expect(p1).not_to eql(p2)
73
+ expect(p2).to eql(Point[10, 0])
68
74
  end
69
75
  end
70
-
71
76
  end
72
77
  end
73
78
  end
@@ -1,10 +1,12 @@
1
- require_relative 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
2
4
 
3
5
  module Laser
4
6
  module Cutter
5
7
  module Geometry
6
- describe Rect do
7
- let(:p1) { Point[ 1.0, 3.0] }
8
+ RSpec.describe Rect do
9
+ let(:p1) { Point[1.0, 3.0] }
8
10
  let(:p2) { Point[11.0, 23.0] }
9
11
 
10
12
  let(:rect1) { Rect.new(p1, p2) }
@@ -22,14 +24,16 @@ module Laser
22
24
  expect(rect1.position).to eql(rect1.vertices[0])
23
25
  expect(rect1.p2).to eql(rect1.vertices[2])
24
26
  end
25
- it 'it generates four side lines' do
27
+
28
+ it 'generates four side lines' do
26
29
  expect(rect1.sides.size).to eql(4)
27
- expect(rect1.sides.first).to be_kind_of(Line)
30
+ expect(rect1.sides.first).to be_a(Line)
28
31
  expect(rect1.sides.first.p1).to eql(p1)
29
32
  expect(rect1.sides[0].p1.to_s).to eql("{1.00000,3.00000}")
30
33
  expect(rect1.sides[1].p1.to_s).to eql("{11.00000,3.00000}")
31
34
  expect(rect1.sides[2].p1).to eql(p1.plus(10, 20))
32
35
  end
36
+
33
37
  it 'can be moved' do
34
38
  expect(rect1.sides[0].p1.to_s).to eql("{1.00000,3.00000}")
35
39
  rect1.x = 1000
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module Laser
6
+ module Cutter
7
+ module Renderer
8
+ RSpec.describe LayoutRenderer do
9
+ describe '#render' do
10
+ let(:renderer) { LayoutRenderer.new(config) }
11
+ let(:file) { File.expand_path("../../../laser-cutter-pdf-test.#{$$}.pdf", __FILE__) }
12
+
13
+ def render_file(filename)
14
+ real_file = ENV['RSPEC_SAVE_PDF'] ? true : false
15
+ config.validate!
16
+ expect(!File.exist?(filename)) if real_file
17
+ renderer.render
18
+ expect(File.exist?(filename)) if real_file
19
+ expect(File.size(filename) > 0) if real_file
20
+ rescue Exception => e
21
+ warn e.backtrace.join("\n")
22
+ fail e.message
23
+ ensure
24
+ if real_file
25
+ begin
26
+ File.delete(filename)
27
+ rescue StandardError
28
+ nil
29
+ end
30
+ expect(!File.exist?(filename))
31
+ end
32
+ end
33
+
34
+ before do
35
+ unless ENV['RSPEC_SAVE_PDF']
36
+ expect_any_instance_of(Prawn::Document).to receive(:render_file).once
37
+ end
38
+ end
39
+
40
+ context 'metric' do
41
+ let(:config) {
42
+ Laser::Cutter::Configuration.new(
43
+ 'width' => 50,
44
+ 'height' => 60,
45
+ 'depth' => 20,
46
+ 'thickness' => 6,
47
+ 'margin' => 5,
48
+ 'padding' => 3,
49
+ 'notch' => 10,
50
+ 'file' => file
51
+ )
52
+ }
53
+
54
+ it 'is able to generate a PDF file' do
55
+ render_file file
56
+ end
57
+ end
58
+
59
+ context 'imperial' do
60
+ context 'margins and padding provided' do
61
+ let(:config) {
62
+ Laser::Cutter::Configuration.new(
63
+ 'width' => 2.5,
64
+ 'height' => 3.5,
65
+ 'depth' => 2.0,
66
+ 'thickness' => 0.125,
67
+ 'margin' => 0,
68
+ 'padding' => 0.125,
69
+ 'notch' => 0.25,
70
+ 'file' => file,
71
+ 'units' => 'in'
72
+ )
73
+ }
74
+
75
+ it 'is able to generate a PDF file' do
76
+ render_file file
77
+ end
78
+ end
79
+
80
+ context 'margins and padding are defaults' do
81
+ let(:config) {
82
+ Laser::Cutter::Configuration.new(
83
+ 'width' => 2.5,
84
+ 'height' => 2,
85
+ 'depth' => 2.0,
86
+ 'thickness' => 0.125,
87
+ 'notch' => 0.25,
88
+ 'file' => file,
89
+ 'units' => 'in'
90
+ )
91
+ }
92
+
93
+ it 'is able to generate a PDF file' do
94
+ render_file file
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tmpdir'
4
+
5
+ RSpec.describe Laser::Cutter::Renderer::SvgRenderer do
6
+ subject(:renderer) { described_class.new(config) }
7
+
8
+ let(:dir) { Dir.mktmpdir }
9
+ let(:file) { File.join(dir, 'box.svg') }
10
+ let(:settings) { { box: '4x3x2/0.125/0.5', file: file } }
11
+ let(:config) { Laser::Cutter::Configuration.new(settings) }
12
+ let(:svg) { File.read(file) }
13
+
14
+ after { FileUtils.remove_entry(dir) }
15
+
16
+ its(:total) { is_expected.to eq(328) }
17
+
18
+ it 'draws every line, yielding each' do
19
+ yielded = 0
20
+ renderer.render { yielded += 1 }
21
+ expect(yielded).to eq(328)
22
+ expect(svg.scan('<line').size).to eq(328)
23
+ end
24
+
25
+ it 'sizes the page in the units of the box' do
26
+ renderer.render
27
+ expect(svg).to include('width="9.2012in" height="11.5512in"')
28
+ end
29
+
30
+ # Kerf moves the outline out by half its width, into the margin.
31
+ it 'keeps every line on the page' do
32
+ renderer.render
33
+ coordinates = svg.scan(/[xy][12]="([\d.-]+)"/).flatten.map(&:to_f)
34
+ expect(coordinates.min).to be_within(0.002).of(0.125)
35
+ expect(coordinates.max).to be_within(0.002).of(11.5512 - 0.125)
36
+ end
37
+
38
+ it 'describes the box' do
39
+ renderer.render
40
+ expect(svg).to match(/<desc>\s*Made with laser-cutter.*thickness: 0\.125/)
41
+ end
42
+
43
+ context 'without metadata' do
44
+ let(:settings) { super().merge(metadata: false) }
45
+
46
+ it 'leaves the description out' do
47
+ renderer.render
48
+ expect(svg).not_to include('<desc>')
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Laser::Cutter::Renderer do
4
+ let(:config) { Laser::Cutter::Configuration.new(box: '4x3x2/0.125', file: 'box') }
5
+
6
+ describe '.for' do
7
+ it 'writes a PDF with the layout renderer' do
8
+ expect(described_class.for('PDF', config)).to be_a(described_class::LayoutRenderer)
9
+ end
10
+
11
+ it 'writes an SVG with the SVG renderer' do
12
+ expect(described_class.for(:svg, config)).to be_a(described_class::SvgRenderer)
13
+ end
14
+
15
+ it 'raises on a format it does not write' do
16
+ expect { described_class.for('dxf', config) }.to raise_error(Laser::Cutter::Error, /unknown format/)
17
+ end
18
+ end
19
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,32 +1,41 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
-
8
- if ENV['CODECLIMATE_REPO_TOKEN']
9
- require 'codeclimate-test-reporter'
10
- CodeClimate::TestReporter.start
1
+ # frozen_string_literal: true
2
+
3
+ # SimpleCov must start before the gem's own code loads, or that code counts as
4
+ # never run. The badge lands in docs/badges/coverage_badge.svg for the README.
5
+ require "simplecov"
6
+ require "coverage/badge"
7
+ require "fileutils"
8
+
9
+ SimpleCov.start do
10
+ # SimpleCov 1.x gives project-relative paths without a leading slash; 0.22 kept it.
11
+ skip %r{\A/?(spec|test)/}
12
+ minimum_coverage 95
13
+ formatter SimpleCov::Formatter::MultiFormatter.new(
14
+ [SimpleCov::Formatter::HTMLFormatter, Coverage::Badge::Formatter]
15
+ )
11
16
  end
12
17
 
13
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
14
- require 'rubygems'
15
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
16
- require 'laser-cutter'
17
- require "codeclimate-test-reporter"
18
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
19
+
20
+ SimpleCov.at_exit do
21
+ SimpleCov.result.format!
22
+ puts "Coverage: #{SimpleCov.result.covered_percent.round(2)}%" # rubocop:disable RSpec/Output
23
+ FileUtils.mkdir_p("docs/badges")
24
+ FileUtils.mv("coverage/badge.svg", "docs/badges/coverage_badge.svg")
25
+ end
18
26
 
27
+ require "rspec/its"
28
+ require "laser/cutter"
19
29
 
30
+ Dir[File.expand_path("support/**/*.rb", __dir__)].each { |file| require file }
20
31
 
21
32
  RSpec.configure do |config|
22
- #config.treat_symbols_as_metadata_keys_with_true_values = true
23
- config.run_all_when_everything_filtered = true
24
- config.filter_run :focus
25
-
26
- # Run specs in random order to surface order dependencies. If you find an
27
- # order dependency and want to debug it, you can fix the order by providing
28
- # the seed, which is printed after each run.
29
- # --seed 1234
30
- config.order = 'random'
31
- end
33
+ config.example_status_persistence_file_path = ".rspec_status"
34
+ config.disable_monkey_patching!
35
+ config.order = :random
36
+ Kernel.srand config.seed
32
37
 
38
+ config.expect_with :rspec do |c|
39
+ c.syntax = :expect
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aruba/rspec"
4
+
5
+ # In-process: the suite builds a Launcher per example instead of forking, so
6
+ # the CLI's own code counts towards coverage.
7
+ Aruba.configure do |config|
8
+ config.command_launcher = :in_process
9
+ config.main_class = Laser::Cutter::Launcher
10
+ end