geo_pattern 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +58 -0
  3. data/.simplecov +25 -0
  4. data/.travis.yml +12 -0
  5. data/Gemfile +3 -1
  6. data/README.md +125 -23
  7. data/Rakefile +72 -9
  8. data/fixtures/generated_patterns/chevrons.svg +1 -0
  9. data/fixtures/generated_patterns/concentric_circles.svg +1 -0
  10. data/fixtures/generated_patterns/diamonds.svg +1 -0
  11. data/fixtures/generated_patterns/diamonds_with_base_color.svg +1 -0
  12. data/fixtures/generated_patterns/diamonds_with_color.svg +1 -0
  13. data/fixtures/generated_patterns/hexagons.svg +1 -0
  14. data/fixtures/generated_patterns/mosaic_squares.svg +1 -0
  15. data/fixtures/generated_patterns/nested_squares.svg +1 -0
  16. data/fixtures/generated_patterns/octagons.svg +1 -0
  17. data/fixtures/generated_patterns/overlapping_circles.svg +1 -0
  18. data/fixtures/generated_patterns/overlapping_rings.svg +1 -0
  19. data/fixtures/generated_patterns/plaid.svg +1 -0
  20. data/fixtures/generated_patterns/plus_signs.svg +1 -0
  21. data/fixtures/generated_patterns/sine_waves.svg +1 -0
  22. data/fixtures/generated_patterns/squares.svg +1 -0
  23. data/fixtures/generated_patterns/tessellation.svg +1 -0
  24. data/fixtures/generated_patterns/triangles.svg +1 -0
  25. data/fixtures/generated_patterns/xes.svg +1 -0
  26. data/geo_pattern.gemspec +8 -2
  27. data/lib/geo_pattern.rb +42 -20
  28. data/lib/geo_pattern/background.rb +25 -0
  29. data/lib/geo_pattern/background_generators/solid_generator.rb +52 -0
  30. data/lib/geo_pattern/color.rb +25 -0
  31. data/lib/geo_pattern/color_generators/base_color_generator.rb +55 -0
  32. data/lib/geo_pattern/color_generators/simple_generator.rb +27 -0
  33. data/lib/geo_pattern/color_preset.rb +26 -0
  34. data/lib/geo_pattern/errors.rb +7 -0
  35. data/lib/geo_pattern/geo_pattern_task.rb +59 -0
  36. data/lib/geo_pattern/helpers.rb +47 -0
  37. data/lib/geo_pattern/pattern.rb +84 -0
  38. data/lib/geo_pattern/pattern_generator.rb +33 -82
  39. data/lib/geo_pattern/pattern_helpers.rb +31 -2
  40. data/lib/geo_pattern/pattern_preset.rb +23 -0
  41. data/lib/geo_pattern/pattern_sieve.rb +36 -0
  42. data/lib/geo_pattern/pattern_store.rb +63 -0
  43. data/lib/geo_pattern/pattern_validator.rb +27 -0
  44. data/lib/geo_pattern/rake_task.rb +109 -0
  45. data/lib/geo_pattern/roles/comparable_metadata.rb +35 -0
  46. data/lib/geo_pattern/roles/named_generator.rb +13 -0
  47. data/lib/geo_pattern/seed.rb +21 -0
  48. data/lib/geo_pattern/structure.rb +25 -0
  49. data/lib/geo_pattern/structure_generators/base_generator.rb +85 -0
  50. data/lib/geo_pattern/structure_generators/chevrons_generator.rb +55 -0
  51. data/lib/geo_pattern/structure_generators/concentric_circles_generator.rb +56 -0
  52. data/lib/geo_pattern/structure_generators/diamonds_generator.rb +66 -0
  53. data/lib/geo_pattern/structure_generators/hexagons_generator.rb +67 -0
  54. data/lib/geo_pattern/structure_generators/mosaic_squares_generator.rb +85 -0
  55. data/lib/geo_pattern/structure_generators/nested_squares_generator.rb +60 -0
  56. data/lib/geo_pattern/structure_generators/octagons_generator.rb +44 -0
  57. data/lib/geo_pattern/structure_generators/overlapping_circles_generator.rb +55 -0
  58. data/lib/geo_pattern/structure_generators/overlapping_rings_generator.rb +55 -0
  59. data/lib/geo_pattern/structure_generators/plaid_generator.rb +55 -0
  60. data/lib/geo_pattern/structure_generators/plus_signs_generator.rb +62 -0
  61. data/lib/geo_pattern/structure_generators/sine_waves_generator.rb +43 -0
  62. data/lib/geo_pattern/structure_generators/squares_generator.rb +36 -0
  63. data/lib/geo_pattern/structure_generators/tessellation_generator.rb +103 -0
  64. data/lib/geo_pattern/structure_generators/triangles_generator.rb +61 -0
  65. data/lib/geo_pattern/structure_generators/xes_generator.rb +67 -0
  66. data/lib/geo_pattern/svg_image.rb +101 -0
  67. data/lib/geo_pattern/version.rb +1 -1
  68. data/script/bootstrap +30 -0
  69. data/script/console +8 -0
  70. data/script/test +21 -0
  71. data/spec/background_generators/solid_generator_spec.rb +50 -0
  72. data/spec/background_spec.rb +25 -0
  73. data/spec/color_generators/base_color_generator_spec.rb +31 -0
  74. data/spec/color_generators/simple_generator_spec.rb +12 -0
  75. data/spec/color_preset_spec.rb +53 -0
  76. data/spec/color_spec.rb +15 -0
  77. data/spec/geo_pattern_spec.rb +95 -24
  78. data/spec/helpers_spec.rb +65 -0
  79. data/spec/pattern_preset_spec.rb +41 -0
  80. data/spec/pattern_sieve_spec.rb +66 -0
  81. data/spec/pattern_spec.rb +72 -0
  82. data/spec/pattern_store_spec.rb +47 -0
  83. data/spec/pattern_validator_spec.rb +28 -0
  84. data/spec/seed_spec.rb +14 -0
  85. data/spec/spec_helper.rb +1 -6
  86. data/spec/structure_generators/chevrons_generator_spec.rb +5 -0
  87. data/spec/structure_generators/concentric_circles_generator_spec.rb +5 -0
  88. data/spec/structure_generators/diamonds_generator_spec.rb +5 -0
  89. data/spec/structure_generators/hexagons_generator_spec.rb +5 -0
  90. data/spec/structure_generators/mosaic_squares_generator_spec.rb +5 -0
  91. data/spec/structure_generators/nested_squares_generator_spec.rb +5 -0
  92. data/spec/structure_generators/octagons_generator_spec.rb +5 -0
  93. data/spec/structure_generators/overlapping_circles_generator_spec.rb +5 -0
  94. data/spec/structure_generators/overlapping_rings_generator_spec.rb +5 -0
  95. data/spec/structure_generators/plaid_generator_spec.rb +5 -0
  96. data/spec/structure_generators/plus_signs_generator_spec.rb +5 -0
  97. data/spec/structure_generators/sine_waves_generator_spec.rb +5 -0
  98. data/spec/structure_generators/squares_generator_spec.rb +5 -0
  99. data/spec/structure_generators/tessellation_generator_spec.rb +5 -0
  100. data/spec/structure_generators/triangles_generator_spec.rb +5 -0
  101. data/spec/structure_generators/xes_generator_spec.rb +5 -0
  102. data/spec/structure_spec.rb +38 -0
  103. data/spec/support/helpers/fixtures.rb +12 -0
  104. data/spec/support/kernel.rb +40 -0
  105. data/spec/support/matchers/image.rb +17 -0
  106. data/spec/support/matchers/name.rb +15 -0
  107. data/spec/support/rspec.rb +1 -1
  108. data/spec/support/shared_examples/generator.rb +46 -0
  109. data/spec/support/shared_examples/pattern.rb +31 -0
  110. data/spec/support/shared_examples/pattern_name.rb +7 -0
  111. data/spec/support/shared_examples/structure.rb +48 -0
  112. data/spec/svg_spec.rb +3 -3
  113. metadata +141 -25
  114. data/lib/geo_pattern/pattern/base_pattern.rb +0 -47
  115. data/lib/geo_pattern/pattern/chevron_pattern.rb +0 -45
  116. data/lib/geo_pattern/pattern/concentric_circles_pattern.rb +0 -47
  117. data/lib/geo_pattern/pattern/diamond_pattern.rb +0 -56
  118. data/lib/geo_pattern/pattern/hexagon_pattern.rb +0 -57
  119. data/lib/geo_pattern/pattern/mosaic_squares_pattern.rb +0 -76
  120. data/lib/geo_pattern/pattern/nested_squares_pattern.rb +0 -51
  121. data/lib/geo_pattern/pattern/octagon_pattern.rb +0 -35
  122. data/lib/geo_pattern/pattern/overlapping_circles_pattern.rb +0 -46
  123. data/lib/geo_pattern/pattern/overlapping_rings_pattern.rb +0 -46
  124. data/lib/geo_pattern/pattern/plaid_pattern.rb +0 -49
  125. data/lib/geo_pattern/pattern/plus_sign_pattern.rb +0 -53
  126. data/lib/geo_pattern/pattern/sine_wave_pattern.rb +0 -36
  127. data/lib/geo_pattern/pattern/square_pattern.rb +0 -27
  128. data/lib/geo_pattern/pattern/tessellation_pattern.rb +0 -93
  129. data/lib/geo_pattern/pattern/triangle_pattern.rb +0 -51
  130. data/lib/geo_pattern/pattern/xes_pattern.rb +0 -58
  131. data/lib/geo_pattern/svg.rb +0 -77
  132. data/spec/support/helpers.rb +0 -34
@@ -0,0 +1,84 @@
1
+ module GeoPattern
2
+ class Pattern
3
+ private
4
+
5
+ attr_reader :svg_image
6
+
7
+ public
8
+
9
+ attr_accessor :background, :structure, :height, :width
10
+
11
+ def initialize(svg_image = SvgImage.new)
12
+ @svg_image = svg_image
13
+ end
14
+
15
+ # Check if string is included in pattern
16
+ #
17
+ # @param [String] string
18
+ # The checked string
19
+ def include?(string)
20
+ image.include?(string)
21
+ end
22
+
23
+ # Generate things for the pattern
24
+ #
25
+ # @param [#generate) generator
26
+ # The generator which should do things with this pattern - e.g. adding
27
+ # background or a structure
28
+ def generate_me(generator)
29
+ generator.generate self
30
+ end
31
+
32
+ # Convert pattern to svg
33
+ def to_svg
34
+ image.to_s
35
+ end
36
+ alias_method :to_s, :to_svg
37
+
38
+ # Convert to base64
39
+ def to_base64
40
+ Base64.strict_encode64(to_svg)
41
+ end
42
+
43
+ # Convert to data uri
44
+ def to_data_uri
45
+ "url(data:image/svg+xml;base64,#{to_base64});"
46
+ end
47
+
48
+ # @see #to_data_uri
49
+ # @deprecated
50
+ def uri_image
51
+ $stderr.puts 'Using "#uri_image" is deprecated as of 1.3.1. Please use "#to_data_uri" instead.'
52
+
53
+ to_data_uri
54
+ end
55
+
56
+ # @see #to_svg
57
+ # @deprecated
58
+ def svg_string
59
+ $stderr.puts 'Using "#svg_string" is deprecated as of 1.3.1. Please use "#to_svg" instead.'
60
+
61
+ to_svg
62
+ end
63
+
64
+ # @see #to_base64
65
+ # @deprecated
66
+ def base64_string
67
+ $stderr.puts 'Using "#base64_string" is deprecated as of 1.3.1. Please use "#to_base64" instead.'
68
+
69
+ to_base64
70
+ end
71
+
72
+ private
73
+
74
+ def image
75
+ svg_image.height = height
76
+ svg_image.width = width
77
+
78
+ svg_image << background.image if background
79
+ svg_image << structure.image if structure
80
+
81
+ svg_image
82
+ end
83
+ end
84
+ end
@@ -1,101 +1,52 @@
1
1
  module GeoPattern
2
2
  class PatternGenerator
3
- DEFAULTS = {
4
- :base_color => '#933c3c'
5
- }
3
+ private
6
4
 
7
- PATTERNS = {
8
- 'chevrons' => ChevronPattern,
9
- 'concentric_circles' => ConcentricCirclesPattern,
10
- 'diamonds' => DiamondPattern,
11
- 'hexagons' => HexagonPattern,
12
- 'mosaic_squares' => MosaicSquaresPattern,
13
- 'nested_squares' => NestedSquaresPattern,
14
- 'octagons' => OctagonPattern,
15
- 'overlapping_circles' => OverlappingCirclesPattern,
16
- 'overlapping_rings' => OverlappingRingsPattern,
17
- 'plaid' => PlaidPattern,
18
- 'plus_signs' => PlusSignPattern,
19
- 'sine_waves' => SineWavePattern,
20
- 'squares' => SquarePattern,
21
- 'tessellation' => TessellationPattern,
22
- 'triangles' => TrianglePattern,
23
- 'xes' => XesPattern,
24
- }.freeze
5
+ attr_reader :background_generator, :structure_generator
25
6
 
26
- FILL_COLOR_DARK = "#222"
27
- FILL_COLOR_LIGHT = "#ddd"
28
- STROKE_COLOR = "#000"
29
- STROKE_OPACITY = 0.02
30
- OPACITY_MIN = 0.02
31
- OPACITY_MAX = 0.15
7
+ public
32
8
 
33
- attr_reader :opts, :hash, :svg
9
+ def initialize(string, generator: nil, patterns: nil, base_color: nil, color: nil)
10
+ $stderr.puts 'Using generator key is deprecated as of 1.3.1' if generator
34
11
 
35
- def initialize(string, opts={})
36
- @opts = DEFAULTS.merge(opts)
37
- @hash = Digest::SHA1.hexdigest string
38
- @svg = SVG.new
12
+ requested_patterns = (Array(generator) | Array(patterns)).flatten.compact
39
13
 
40
- generate_background
41
- generate_pattern
42
- end
14
+ pattern_preset = PatternPreset.new(
15
+ fill_color_dark: '#222',
16
+ fill_color_light: '#ddd',
17
+ stroke_color: '#000',
18
+ stroke_opacity: 0.02,
19
+ opacity_min: 0.02,
20
+ opacity_max: 0.15
21
+ )
43
22
 
44
- def svg_string
45
- svg.to_s
46
- end
47
- alias_method :to_s, :svg_string
23
+ color_preset = ColorPreset.new(
24
+ base_color: '#933c3c'
25
+ )
26
+ color_preset.color = color if color
27
+ color_preset.base_color = base_color if base_color
48
28
 
49
- def base64_string
50
- Base64.strict_encode64(svg_string)
51
- end
29
+ seed = Seed.new(string)
52
30
 
53
- def uri_image
54
- "url(data:image/svg+xml;base64,#{base64_string});"
55
- end
31
+ pattern_validator = PatternValidator.new
32
+ pattern_validator.validate(requested_patterns)
56
33
 
57
- def generate_background
58
- if opts[:color]
59
- rgb = Color::RGB.from_html(opts[:color])
60
- else
61
- hue_offset = PatternHelpers.map(PatternHelpers.hex_val(hash, 14, 3), 0, 4095, 0, 359)
62
- sat_offset = PatternHelpers.hex_val(hash, 17, 1)
63
- base_color = Color::RGB.from_html(opts[:base_color]).to_hsl
64
- base_color.hue = base_color.hue - hue_offset;
34
+ pattern_sieve = PatternSieve.new(requested_patterns, seed)
65
35
 
66
- if (sat_offset % 2 == 0)
67
- base_color.saturation = base_color.saturation + sat_offset
68
- else
69
- base_color.saturation = base_color.saturation - sat_offset
70
- end
71
- rgb = base_color.to_rgb
72
- end
73
- r = (rgb.r * 255).round
74
- g = (rgb.g * 255).round
75
- b = (rgb.b * 255).round
76
- svg.rect(0, 0, "100%", "100%", {"fill" => "rgb(#{r}, #{g}, #{b})"})
36
+ @background_generator = BackgroundGenerators::SolidGenerator.new(seed, color_preset)
37
+ @structure_generator = begin
38
+ generator_klass = pattern_sieve.fetch
39
+ generator_klass.new(seed, pattern_preset)
40
+ end
77
41
  end
78
42
 
79
- def generate_pattern
80
- unless opts[:generator].nil?
81
- if opts[:generator].is_a? String
82
- generator = PATTERNS[opts[:generator]]
83
- puts SVG.as_comment("String pattern references are deprecated as of 1.3.0")
84
- elsif opts[:generator] < BasePattern
85
- if PATTERNS.values.include? opts[:generator]
86
- generator = opts[:generator]
87
- else
88
- abort("Error: the requested generator is invalid")
89
- generator = nil
90
- end
91
- end
92
- end
43
+ def generate
44
+ pattern = Pattern.new
93
45
 
94
- generator ||= PATTERNS.values[[PatternHelpers.hex_val(hash, 20, 1), PATTERNS.length - 1].min]
46
+ pattern.generate_me background_generator
47
+ pattern.generate_me structure_generator
95
48
 
96
- # Instantiate the generator with the needed references
97
- # and render the pattern to the svg object
98
- generator.new(svg, hash).render_to_svg
49
+ pattern
99
50
  end
100
51
  end
101
52
  end
@@ -1,17 +1,46 @@
1
1
  module GeoPattern
2
2
  module PatternHelpers
3
- def self.hex_val(hash, index, length)
3
+ def hex_val(hash, index, length)
4
4
  hash[index, length || 1].to_i(16)
5
5
  end
6
6
 
7
7
  # Ruby implementation of Processing's map function
8
8
  # http://processing.org/reference/map_.html
9
- def self.map(value, v_min, v_max, d_min, d_max) # v for value, d for desired
9
+ def map(value, v_min, v_max, d_min, d_max) # v for value, d for desired
10
10
  v_value = value.to_f # so it returns float
11
11
 
12
12
  v_range = v_max - v_min
13
13
  d_range = d_max - d_min
14
14
  (v_value - v_min) * d_range / v_range + d_min
15
15
  end
16
+
17
+ def html_to_rgb(color)
18
+ generate_rgb_string(::Color::RGB.from_html(color))
19
+ end
20
+
21
+ def html_to_rgb_for_string(seed, base_color)
22
+ hue_offset = map(seed.to_i(14, 3), 0, 4095, 0, 359)
23
+ sat_offset = seed.to_i(17, 1)
24
+ base_color = ::Color::RGB.from_html(base_color).to_hsl
25
+ base_color.hue = base_color.hue - hue_offset
26
+
27
+ if (sat_offset % 2 == 0)
28
+ base_color.saturation = base_color.saturation + sat_offset
29
+ else
30
+ base_color.saturation = base_color.saturation - sat_offset
31
+ end
32
+
33
+ generate_rgb_string(base_color.to_rgb)
34
+ end
35
+
36
+ def generate_rgb_string(rgb)
37
+ r = (rgb.r * 255).round
38
+ g = (rgb.g * 255).round
39
+ b = (rgb.b * 255).round
40
+
41
+ format('rgb(%d, %d, %d)', r, g, b)
42
+ end
43
+
44
+ module_function :hex_val, :map, :html_to_rgb, :html_to_rgb_for_string, :generate_rgb_string
16
45
  end
17
46
  end
@@ -0,0 +1,23 @@
1
+ module GeoPattern
2
+ class PatternPreset
3
+ private
4
+
5
+ attr_accessor :options
6
+
7
+ public
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ [:fill_color_dark, :fill_color_light, :stroke_color, :stroke_opacity, :opacity_min, :opacity_max].each do |m|
14
+ define_method m do
15
+ options[m]
16
+ end
17
+ end
18
+
19
+ def update(opts)
20
+ options.merge! opts
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ module GeoPattern
2
+ class PatternSieve
3
+ private
4
+
5
+ attr_reader :pattern_store, :requested_patterns, :seed, :available_patterns, :index
6
+
7
+ public
8
+
9
+ def initialize(requested_patterns, seed, pattern_store = PatternStore.new)
10
+ @requested_patterns = requested_patterns
11
+ @seed = seed
12
+ @pattern_store = pattern_store
13
+
14
+ @available_patterns = determine_available_patterns
15
+ @index = determine_index
16
+ end
17
+
18
+ def fetch
19
+ available_patterns[index]
20
+ end
21
+
22
+ private
23
+
24
+ def determine_index
25
+ [seed.to_i(20, 1), available_patterns.length - 1].min
26
+ end
27
+
28
+ def determine_available_patterns
29
+ patterns = Array(requested_patterns).compact
30
+
31
+ return pattern_store.all if patterns.empty?
32
+
33
+ patterns.map { |p| pattern_store[p] }.compact
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,63 @@
1
+ module GeoPattern
2
+ # rubocop:disable Style/ConstantName
3
+ ChevronPattern = :chevrons
4
+ ConcentricCirclesPattern = :concentric_circles
5
+ DiamondPattern = :diamonds
6
+ HexagonPattern = :hexagons
7
+ MosaicSquaresPattern = :mosaic_squares
8
+ NestedSquaresPattern = :nested_squares
9
+ OctagonPattern = :octagons
10
+ OverlappingCirclesPattern = :overlapping_circles
11
+ OverlappingRingsPattern = :overlapping_rings
12
+ PlaidPattern = :plaid
13
+ PlusSignPattern = :plus_signs
14
+ SineWavePattern = :sine_waves
15
+ SquarePattern = :squares
16
+ TessellationPattern = :tessellation
17
+ TrianglePattern = :triangles
18
+ XesPattern = :xes
19
+ # rubocop:enable Style/ConstantName
20
+
21
+ class PatternStore
22
+ private
23
+
24
+ attr_reader :store
25
+
26
+ public
27
+
28
+ def initialize
29
+ @store = {
30
+ chevrons: StructureGenerators::ChevronsGenerator,
31
+ concentric_circles: StructureGenerators::ConcentricCirclesGenerator,
32
+ diamonds: StructureGenerators::DiamondsGenerator,
33
+ hexagons: StructureGenerators::HexagonsGenerator,
34
+ mosaic_squares: StructureGenerators::MosaicSquaresGenerator,
35
+ nested_squares: StructureGenerators::NestedSquaresGenerator,
36
+ octagons: StructureGenerators::OctagonsGenerator,
37
+ overlapping_circles: StructureGenerators::OverlappingCirclesGenerator,
38
+ overlapping_rings: StructureGenerators::OverlappingRingsGenerator,
39
+ plaid: StructureGenerators::PlaidGenerator,
40
+ plus_signs: StructureGenerators::PlusSignsGenerator,
41
+ sine_waves: StructureGenerators::SineWavesGenerator,
42
+ squares: StructureGenerators::SquaresGenerator,
43
+ tessellation: StructureGenerators::TessellationGenerator,
44
+ triangles: StructureGenerators::TrianglesGenerator,
45
+ xes: StructureGenerators::XesGenerator
46
+ }
47
+ end
48
+
49
+ def [](pattern)
50
+ $stderr.puts 'String pattern references are deprecated as of 1.3.0' if pattern.is_a?(String)
51
+
52
+ store[pattern.to_s.to_sym]
53
+ end
54
+
55
+ def all
56
+ store.values
57
+ end
58
+
59
+ def known?(pattern)
60
+ store.key?(pattern.to_s.to_sym)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,27 @@
1
+ module GeoPattern
2
+ class PatternValidator
3
+ private
4
+
5
+ attr_reader :pattern_store
6
+
7
+ public
8
+
9
+ def initialize(pattern_store = PatternStore.new)
10
+ @pattern_store = pattern_store
11
+ end
12
+
13
+ def validate(requested_patterns)
14
+ message = "Error: At least one of the requested patterns \"#{requested_patterns.join(', ')}\" is invalid"
15
+
16
+ fail InvalidPatternError, message unless valid?(requested_patterns)
17
+
18
+ self
19
+ end
20
+
21
+ private
22
+
23
+ def valid?(requested_patterns)
24
+ requested_patterns.all? { |p| pattern_store.known? p }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,109 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'logger'
4
+
5
+ module GeoPattern
6
+ # Rake Task
7
+ #
8
+ # This task can be used to generate pattern files
9
+ class RakeTask < ::Rake::TaskLib
10
+ include ::Rake::DSL if defined?(::Rake::DSL)
11
+
12
+ # @!attribute [r] name
13
+ # Name of task.
14
+ attr_reader :name
15
+
16
+ # @!attribute [r] description
17
+ # A description for the task
18
+ attr_reader :description
19
+
20
+ # @!attribute [r] verbose (true)
21
+ # Use verbose output. If this is set to true, the task will print the
22
+ # executed spec command to stdout.
23
+ attr_reader :verbose
24
+
25
+ private
26
+
27
+ attr_reader :task_arguments, :task_block, :logger, :working_directory
28
+
29
+ # Create task instance
30
+ #
31
+ # @param [String] description
32
+ # A description for the task
33
+ #
34
+ # @param [String] name
35
+ # The name for the task (including namespace), e.g. namespace1:task1
36
+ #
37
+ # @param [Array] arguments
38
+ # Arguments for the task. Look
39
+ # [here](http://viget.com/extend/protip-passing-parameters-to-your-rake-tasks)
40
+ # for a better description on how to use arguments in rake tasks
41
+ #
42
+ # @yield
43
+ # A block which is called before the "run_task"-method is called. The
44
+ # parameters it taskes depends on the number of parameters the block
45
+ # can take. If the block is defined which two parameters, it takes two
46
+ # parameters from the paramter 'arguments'.
47
+ def initialize(opts = {}, &task_block)
48
+ @options = {
49
+ description: nil,
50
+ name: GeoPattern::Helpers.underscore(self.class.to_s.split(/::/).slice(-2..-1).join(':').gsub(/Task$/, '')),
51
+ arguments: [],
52
+ logger: ::Logger.new($stderr),
53
+ working_directory: Dir.getwd
54
+ }.merge opts
55
+
56
+ before_initialize
57
+
58
+ fail ArgumentError, :description if @options[:description].nil?
59
+
60
+ @description = @options[:description]
61
+ @task_arguments = Array(@options[:arguments])
62
+ @task_block = task_block
63
+ @logger = @options[:logger]
64
+ @working_directory = @options[:working_directory]
65
+ @name = @options[:name]
66
+
67
+ after_initialize
68
+
69
+ define_task
70
+ end
71
+
72
+ private
73
+
74
+ # Run code after initialize
75
+ def after_initialize; end
76
+
77
+ # Run code before initialize
78
+ def before_initialize; end
79
+
80
+ # Define task
81
+ def define_task
82
+ desc description unless ::Rake.application.last_comment
83
+
84
+ task name, *task_arguments do |_, task_args|
85
+ RakeFileUtils.__send__(:verbose, verbose) do
86
+ instance_exec(*[self, task_args].slice(0, task_block.arity), &task_block) if task_block.respond_to? :call
87
+ run_task verbose
88
+ end
89
+ end
90
+ end
91
+
92
+ # Run code if task is executed
93
+ def run_task(_verbose); end
94
+
95
+ public
96
+
97
+ # Binding to instance
98
+ def instance_binding
99
+ binding
100
+ end
101
+
102
+ # Include module in instance
103
+ def include(modules)
104
+ modules = Array(modules)
105
+
106
+ modules.each { |m| self.class.include m }
107
+ end
108
+ end
109
+ end