gruff 0.13.0 → 0.16.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +79 -0
  3. data/.rubocop.yml +29 -31
  4. data/CHANGELOG.md +43 -0
  5. data/README.md +11 -5
  6. data/gruff.gemspec +8 -10
  7. data/lib/gruff/accumulator_bar.rb +4 -2
  8. data/lib/gruff/area.rb +9 -12
  9. data/lib/gruff/bar.rb +46 -31
  10. data/lib/gruff/base.rb +236 -207
  11. data/lib/gruff/bezier.rb +6 -8
  12. data/lib/gruff/box_plot.rb +174 -0
  13. data/lib/gruff/bullet.rb +17 -16
  14. data/lib/gruff/candlestick.rb +112 -0
  15. data/lib/gruff/dot.rb +14 -14
  16. data/lib/gruff/font.rb +42 -0
  17. data/lib/gruff/helper/bar_conversion.rb +5 -5
  18. data/lib/gruff/helper/bar_value_label.rb +26 -20
  19. data/lib/gruff/helper/stacked_mixin.rb +4 -3
  20. data/lib/gruff/histogram.rb +9 -7
  21. data/lib/gruff/line.rb +96 -83
  22. data/lib/gruff/mini/bar.rb +9 -6
  23. data/lib/gruff/mini/legend.rb +16 -12
  24. data/lib/gruff/mini/pie.rb +9 -6
  25. data/lib/gruff/mini/side_bar.rb +9 -6
  26. data/lib/gruff/net.rb +16 -22
  27. data/lib/gruff/patch/rmagick.rb +0 -1
  28. data/lib/gruff/patch/string.rb +2 -1
  29. data/lib/gruff/pie.rb +42 -75
  30. data/lib/gruff/renderer/bezier.rb +8 -9
  31. data/lib/gruff/renderer/circle.rb +8 -9
  32. data/lib/gruff/renderer/dash_line.rb +10 -10
  33. data/lib/gruff/renderer/dot.rb +15 -14
  34. data/lib/gruff/renderer/ellipse.rb +8 -9
  35. data/lib/gruff/renderer/line.rb +8 -11
  36. data/lib/gruff/renderer/polygon.rb +9 -10
  37. data/lib/gruff/renderer/polyline.rb +8 -9
  38. data/lib/gruff/renderer/rectangle.rb +11 -8
  39. data/lib/gruff/renderer/renderer.rb +25 -40
  40. data/lib/gruff/renderer/text.rb +21 -37
  41. data/lib/gruff/scatter.rb +86 -85
  42. data/lib/gruff/side_bar.rb +50 -37
  43. data/lib/gruff/side_stacked_bar.rb +26 -35
  44. data/lib/gruff/spider.rb +52 -28
  45. data/lib/gruff/stacked_area.rb +20 -16
  46. data/lib/gruff/stacked_bar.rb +44 -22
  47. data/lib/gruff/store/store.rb +6 -10
  48. data/lib/gruff/store/xy_data.rb +2 -0
  49. data/lib/gruff/themes.rb +6 -6
  50. data/lib/gruff/version.rb +1 -1
  51. data/lib/gruff.rb +70 -57
  52. data/rails_generators/gruff/templates/controller.rb +1 -1
  53. metadata +25 -28
  54. data/.rubocop_todo.yml +0 -182
  55. data/.travis.yml +0 -23
  56. data/assets/plastik/blue.png +0 -0
  57. data/assets/plastik/green.png +0 -0
  58. data/assets/plastik/red.png +0 -0
  59. data/lib/gruff/photo_bar.rb +0 -93
  60. data/lib/gruff/scene.rb +0 -198
  61. data/lib/gruff/store/custom_data.rb +0 -36
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gruff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geoffrey Grosenbach
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-05-09 00:00:00.000000000 Z
12
+ date: 2022-05-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rmagick
@@ -17,64 +17,64 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '0'
20
+ version: '4.2'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '0'
27
+ version: '4.2'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rubocop
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: 1.12.1
34
+ version: 1.27.0
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: 1.12.1
41
+ version: 1.27.0
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rubocop-performance
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: 1.10.2
48
+ version: 1.13.3
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: 1.10.2
55
+ version: 1.13.3
56
56
  - !ruby/object:Gem::Dependency
57
- name: histogram
57
+ name: rubocop-rake
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">="
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '0'
63
- type: :runtime
62
+ version: 0.6.0
63
+ type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">="
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: 0.6.0
70
70
  - !ruby/object:Gem::Dependency
71
- name: rake
71
+ name: histogram
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
- type: :development
77
+ type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
@@ -82,7 +82,7 @@ dependencies:
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  - !ruby/object:Gem::Dependency
85
- name: parallel
85
+ name: rake
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - ">="
@@ -146,10 +146,9 @@ extra_rdoc_files: []
146
146
  files:
147
147
  - ".editorconfig"
148
148
  - ".github/ISSUE_TEMPLATE.md"
149
+ - ".github/workflows/ci.yml"
149
150
  - ".gitignore"
150
151
  - ".rubocop.yml"
151
- - ".rubocop_todo.yml"
152
- - ".travis.yml"
153
152
  - ".yardopts"
154
153
  - CHANGELOG.md
155
154
  - Gemfile
@@ -158,9 +157,6 @@ files:
158
157
  - assets/fonts/LICENSE.txt
159
158
  - assets/fonts/Roboto-Bold.ttf
160
159
  - assets/fonts/Roboto-Regular.ttf
161
- - assets/plastik/blue.png
162
- - assets/plastik/green.png
163
- - assets/plastik/red.png
164
160
  - gruff.gemspec
165
161
  - init.rb
166
162
  - lib/gruff.rb
@@ -169,8 +165,11 @@ files:
169
165
  - lib/gruff/bar.rb
170
166
  - lib/gruff/base.rb
171
167
  - lib/gruff/bezier.rb
168
+ - lib/gruff/box_plot.rb
172
169
  - lib/gruff/bullet.rb
170
+ - lib/gruff/candlestick.rb
173
171
  - lib/gruff/dot.rb
172
+ - lib/gruff/font.rb
174
173
  - lib/gruff/helper/bar_conversion.rb
175
174
  - lib/gruff/helper/bar_value_label.rb
176
175
  - lib/gruff/helper/stacked_mixin.rb
@@ -183,7 +182,6 @@ files:
183
182
  - lib/gruff/net.rb
184
183
  - lib/gruff/patch/rmagick.rb
185
184
  - lib/gruff/patch/string.rb
186
- - lib/gruff/photo_bar.rb
187
185
  - lib/gruff/pie.rb
188
186
  - lib/gruff/renderer/bezier.rb
189
187
  - lib/gruff/renderer/circle.rb
@@ -197,14 +195,12 @@ files:
197
195
  - lib/gruff/renderer/renderer.rb
198
196
  - lib/gruff/renderer/text.rb
199
197
  - lib/gruff/scatter.rb
200
- - lib/gruff/scene.rb
201
198
  - lib/gruff/side_bar.rb
202
199
  - lib/gruff/side_stacked_bar.rb
203
200
  - lib/gruff/spider.rb
204
201
  - lib/gruff/stacked_area.rb
205
202
  - lib/gruff/stacked_bar.rb
206
203
  - lib/gruff/store/basic_data.rb
207
- - lib/gruff/store/custom_data.rb
208
204
  - lib/gruff/store/store.rb
209
205
  - lib/gruff/store/xy_data.rb
210
206
  - lib/gruff/themes.rb
@@ -215,7 +211,8 @@ files:
215
211
  homepage: https://github.com/topfunky/gruff
216
212
  licenses:
217
213
  - MIT
218
- metadata: {}
214
+ metadata:
215
+ rubygems_mfa_required: 'true'
219
216
  post_install_message:
220
217
  rdoc_options: []
221
218
  require_paths:
@@ -224,14 +221,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
221
  requirements:
225
222
  - - ">="
226
223
  - !ruby/object:Gem::Version
227
- version: 2.4.0
224
+ version: 2.5.0
228
225
  required_rubygems_version: !ruby/object:Gem::Requirement
229
226
  requirements:
230
227
  - - ">="
231
228
  - !ruby/object:Gem::Version
232
229
  version: '0'
233
230
  requirements: []
234
- rubygems_version: 3.1.4
231
+ rubygems_version: 3.3.7
235
232
  signing_key:
236
233
  specification_version: 4
237
234
  summary: Beautiful graphs for one or multiple datasets.
data/.rubocop_todo.yml DELETED
@@ -1,182 +0,0 @@
1
- # This configuration was generated by
2
- # `rubocop --auto-gen-config`
3
- # on 2021-05-07 13:38:24 UTC using RuboCop version 1.12.1.
4
- # The point is for the user to remove these configuration records
5
- # one by one as the offenses are removed from the code base.
6
- # Note that changes in the inspected code, or installation of new
7
- # versions of RuboCop, may require this file to be generated again.
8
-
9
- # Offense count: 1
10
- # Cop supports --auto-correct.
11
- # Configuration parameters: Include.
12
- # Include: **/*.gemspec
13
- Gemspec/DateAssignment:
14
- Exclude:
15
- - 'gruff.gemspec'
16
-
17
- # Offense count: 1
18
- # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
19
- Lint/DuplicateBranch:
20
- Exclude:
21
- - 'test/gruff_test_case.rb'
22
-
23
- # Offense count: 1
24
- Lint/DuplicateMethods:
25
- Exclude:
26
- - 'lib/gruff/photo_bar.rb'
27
-
28
- # Offense count: 13
29
- Lint/FloatComparison:
30
- Exclude:
31
- - 'Rakefile'
32
- - 'lib/gruff/base.rb'
33
- - 'lib/gruff/renderer/renderer.rb'
34
- - 'lib/gruff/scatter.rb'
35
-
36
- # Offense count: 1
37
- Lint/UnreachableCode:
38
- Exclude:
39
- - 'lib/gruff/photo_bar.rb'
40
-
41
- # Offense count: 1
42
- Lint/UselessAssignment:
43
- Exclude:
44
- - 'lib/gruff/photo_bar.rb'
45
-
46
- # Offense count: 1
47
- # Configuration parameters: CheckForMethodsWithNoSideEffects.
48
- Lint/Void:
49
- Exclude:
50
- - 'lib/gruff/patch/rmagick.rb'
51
-
52
- # Offense count: 56
53
- # Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
54
- Metrics/AbcSize:
55
- Max: 75
56
-
57
- # Offense count: 1
58
- # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
59
- # IgnoredMethods: refine
60
- Metrics/BlockLength:
61
- Max: 29
62
-
63
- # Offense count: 14
64
- # Configuration parameters: CountComments, CountAsOne.
65
- Metrics/ClassLength:
66
- Max: 669
67
-
68
- # Offense count: 8
69
- # Configuration parameters: IgnoredMethods.
70
- Metrics/CyclomaticComplexity:
71
- Max: 21
72
-
73
- # Offense count: 126
74
- # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
75
- Metrics/MethodLength:
76
- Max: 81
77
-
78
- # Offense count: 3
79
- # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
80
- Metrics/ParameterLists:
81
- Max: 7
82
-
83
- # Offense count: 7
84
- # Configuration parameters: IgnoredMethods.
85
- Metrics/PerceivedComplexity:
86
- Max: 22
87
-
88
- # Offense count: 1
89
- # Configuration parameters: EnforcedStyleForLeadingUnderscores.
90
- # SupportedStylesForLeadingUnderscores: disallowed, required, optional
91
- Naming/MemoizedInstanceVariableName:
92
- Exclude:
93
- - 'lib/gruff/scene.rb'
94
-
95
- # Offense count: 9
96
- # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
97
- # AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to
98
- Naming/MethodParameterName:
99
- Exclude:
100
- - 'lib/gruff/base.rb'
101
- - 'lib/gruff/patch/rmagick.rb'
102
- - 'lib/gruff/pie.rb'
103
- - 'lib/gruff/renderer/text.rb'
104
-
105
- # Offense count: 6
106
- # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers.
107
- # SupportedStyles: snake_case, normalcase, non_integer
108
- # AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339
109
- Naming/VariableNumber:
110
- Exclude:
111
- - 'test/test_bar.rb'
112
- - 'test/test_bezier.rb'
113
- - 'test/test_line.rb'
114
-
115
- # Offense count: 1
116
- # Configuration parameters: MinSize.
117
- Performance/CollectionLiteralInLoop:
118
- Exclude:
119
- - 'test/test_legend.rb'
120
-
121
- # Offense count: 1
122
- # Cop supports --auto-correct.
123
- Performance/RedundantMatch:
124
- Exclude:
125
- - 'lib/gruff/photo_bar.rb'
126
-
127
- # Offense count: 1
128
- # Cop supports --auto-correct.
129
- Performance/RegexpMatch:
130
- Exclude:
131
- - 'lib/gruff/photo_bar.rb'
132
-
133
- # Offense count: 2
134
- # Cop supports --auto-correct.
135
- Performance/StringReplacement:
136
- Exclude:
137
- - 'lib/gruff/scene.rb'
138
-
139
- # Offense count: 2
140
- Security/Eval:
141
- Exclude:
142
- - 'test/gruff_test_case.rb'
143
-
144
- # Offense count: 1
145
- Style/CombinableLoops:
146
- Exclude:
147
- - 'test/test_line.rb'
148
-
149
- # Offense count: 3
150
- Style/DocumentDynamicEvalDefinition:
151
- Exclude:
152
- - 'test/gruff_test_case.rb'
153
-
154
- # Offense count: 137
155
- # Configuration parameters: RequireForNonPublicMethods.
156
- Style/DocumentationMethod:
157
- Enabled: false
158
-
159
- # Offense count: 3
160
- # Cop supports --auto-correct.
161
- Style/EvalWithLocation:
162
- Exclude:
163
- - 'test/gruff_test_case.rb'
164
-
165
- # Offense count: 1
166
- Style/MissingRespondToMissing:
167
- Exclude:
168
- - 'lib/gruff/scene.rb'
169
-
170
- # Offense count: 1
171
- # Configuration parameters: AllowedMethods.
172
- # AllowedMethods: respond_to_missing?
173
- Style/OptionalBooleanParameter:
174
- Exclude:
175
- - 'lib/gruff/base.rb'
176
-
177
- # Offense count: 3
178
- # Cop supports --auto-correct.
179
- Style/StringConcatenation:
180
- Exclude:
181
- - 'lib/gruff/photo_bar.rb'
182
- - 'test/gruff_test_case.rb'
data/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- language: ruby
2
- dist: bionic
3
- cache: bundler
4
-
5
- rvm:
6
- - 2.4
7
- - 2.5
8
- - 2.6
9
- - 2.7
10
- - 3.0
11
- - jruby-9.2.14.0
12
-
13
- jobs:
14
- include:
15
- - name: 'Lint'
16
- rvm: 2.4
17
- script: bundle exec rubocop
18
-
19
- addons:
20
- apt:
21
- packages:
22
- - gsfonts
23
- - ghostscript
Binary file
Binary file
Binary file
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # EXPERIMENTAL!
4
- #
5
- # Doesn't work yet.
6
- #
7
- class Gruff::PhotoBar < Gruff::Base
8
- # TODO
9
- #
10
- # define base and cap in yml
11
- # allow for image directory to be located elsewhere
12
- # more exact measurements for bar heights (go all the way to the bottom of the graph)
13
- # option to tile images instead of use a single image
14
- # drop base label a few px lower so photo bar graphs can have a base dropping over the lower marker line
15
- #
16
-
17
- # The name of a pre-packaged photo-based theme.
18
- attr_reader :theme
19
-
20
- # def initialize(target_width=800)
21
- # super
22
- # init_photo_bar_graphics()
23
- # end
24
-
25
- def draw
26
- super
27
- return unless data_given?
28
-
29
- return # TODO: Remove for further development
30
-
31
- init_photo_bar_graphics
32
-
33
- #Draw#define_clip_path()
34
- #Draw#clip_path(pathname)
35
- #Draw#composite....with bar graph image OverCompositeOp
36
- #
37
- # See also
38
- #
39
- # Draw.pattern # define an image to tile as the filling of a draw object
40
- #
41
-
42
- # Setup spacing.
43
- #
44
- # Columns sit side-by-side.
45
- spacing_factor = 0.9
46
- bar_width = store.norm_data[0].color.columns
47
-
48
- store.norm_data.each_with_index do |data_row, row_index|
49
- data_row.points.each_with_index do |data_point, point_index|
50
- data_point = 0 if data_point.nil?
51
- # Use incremented x and scaled y
52
- left_x = @graph_left + (bar_width * (row_index + point_index + ((store.length - 1) * point_index)))
53
- left_y = @graph_top + (@graph_height - data_point * @graph_height) + 1
54
- right_x = left_x + bar_width * spacing_factor
55
- right_y = @graph_top + @graph_height - 1
56
-
57
- bar_image_width = data_row.color.columns
58
- bar_image_height = right_y.to_f - left_y.to_f
59
-
60
- # Crop to scale for data
61
- bar_image = data_row.color.crop(0, 0, bar_image_width, bar_image_height)
62
-
63
- @d.gravity = Magick::NorthWestGravity
64
- @d.composite(left_x, left_y, bar_image_width, bar_image_height, bar_image)
65
-
66
- # Calculate center based on bar_width and current row
67
- label_center = @graph_left + (store.length * bar_width * point_index) + (store.length * bar_width / 2.0)
68
- draw_label(label_center, point_index)
69
- end
70
- end
71
- end
72
-
73
- # Return the chosen theme or the default
74
- def theme
75
- @theme || 'plastik'
76
- end
77
-
78
- protected
79
-
80
- # Sets up colors with a list of images that will be used.
81
- # Images should be 340px tall
82
- def init_photo_bar_graphics
83
- color_list = []
84
- theme_dir = File.dirname(__FILE__) + '/../../assets/' + theme
85
-
86
- Dir.open(theme_dir).each do |file|
87
- next unless /\.png$/.match(file)
88
-
89
- color_list << Magick::Image.read("#{theme_dir}/#{file}").first
90
- end
91
- @colors = color_list
92
- end
93
- end
data/lib/gruff/scene.rb DELETED
@@ -1,198 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'observer'
4
-
5
- # A scene is a non-linear graph that assembles layers together to tell a story.
6
- # Layers are folders with appropriately named files (see below). You can group
7
- # layers and control them together or just set their values individually.
8
- #
9
- # Examples:
10
- #
11
- # * A city scene that changes with the time of day and the weather conditions.
12
- # * A traffic map that shows red lines on streets that are crowded and green on free-flowing ones.
13
- #
14
- # g = Gruff::Scene.new("500x100", "path/to/city_scene_directory")
15
- #
16
- # # Define order of layers, back to front
17
- # g.layers = %w(background haze sky clouds)
18
- #
19
- # # Define groups that will be controlled by the same input
20
- # g.weather_group = %w(clouds)
21
- # g.time_group = %w(background sky)
22
- #
23
- # # Set values for the layers or groups
24
- # g.weather = "cloudy"
25
- # g.time = Time.now
26
- # g.haze = true
27
- #
28
- # # Write the final graph to disk
29
- # g.write "hazy_daytime_city_scene.png"
30
- #
31
- # There are several rules that will magically select a layer when possible.
32
- #
33
- # * Numbered files will be selected according to the closest value that is less than the input value.
34
- # * +'true.png'+ and +'false.png'+ will be used as booleans.
35
- # * Other named files will be used if the input matches the filename (without the filetype extension).
36
- # * If there is a file named +'default.png'+, it will be used unless other input values are set for the corresponding layer.
37
- class Gruff::Scene < Gruff::Base
38
- # An array listing the folder names that will be rendered, from back to front.
39
- #
40
- # @example
41
- # g.layers = %w(sky clouds buildings street people)
42
- attr_reader :layers
43
-
44
- def initialize(target_width, base_dir)
45
- @base_dir = base_dir
46
- @groups = {}
47
- @layers = []
48
- super target_width
49
- end
50
-
51
- def draw
52
- # Join all the custom paths and filter out the empty ones
53
- image_paths = @layers.map(&:path).reject(&:empty?)
54
- images = Magick::ImageList.new(*image_paths)
55
- Gruff::Renderer.background_image = images.flatten_images
56
- end
57
-
58
- def layers=(ordered_list)
59
- ordered_list.each do |layer_name|
60
- @layers << Gruff::Layer.new(@base_dir, layer_name)
61
- end
62
- end
63
-
64
- # Group layers to input values
65
- #
66
- # g.weather_group = ["sky", "sea", "clouds"]
67
- #
68
- # Set input values
69
- #
70
- # g.weather = "cloudy"
71
- #
72
- def method_missing(method_name, *args)
73
- case method_name.to_s
74
- when /^(\w+)_group=$/
75
- add_group Regexp.last_match(1), *args
76
- return
77
- when /^(\w+)=$/
78
- set_input Regexp.last_match(1), args.first
79
- return
80
- end
81
- super
82
- end
83
-
84
- private
85
-
86
- def add_group(input_name, layer_names)
87
- @groups[input_name] = Gruff::Group.new(input_name, @layers.select { |layer| layer_names.include?(layer.name) })
88
- end
89
-
90
- def set_input(input_name, input_value)
91
- if !@groups[input_name].nil?
92
- @groups[input_name].send_updates(input_value)
93
- elsif chosen_layer = @layers.find { |layer| layer.name == input_name }
94
- chosen_layer.update input_value
95
- end
96
- end
97
- end
98
-
99
- # @private
100
- class Gruff::Group
101
- include Observable
102
- attr_reader :name
103
-
104
- def initialize(folder_name, layers)
105
- @name = folder_name
106
- layers.each do |layer|
107
- layer.observe self
108
- end
109
- end
110
-
111
- def send_updates(value)
112
- changed
113
- notify_observers value
114
- end
115
- end
116
-
117
- # @private
118
- class Gruff::Layer
119
- attr_reader :name
120
-
121
- def initialize(base_dir, folder_name)
122
- @base_dir = base_dir.to_s
123
- @name = folder_name.to_s
124
- @filenames = Dir.open(File.join(base_dir, folder_name)).entries.select { |file| file =~ /^[^.]+\.png$/ }.sort
125
- @selected_filename = select_default
126
- end
127
-
128
- # Register this layer so it receives updates from the group
129
- def observe(obj)
130
- obj.add_observer self
131
- end
132
-
133
- # Choose the appropriate filename for this layer, based on the input
134
- def update(value)
135
- @selected_filename = case value.to_s
136
- when /^(true|false)$/
137
- select_boolean value
138
- when /^(\w|\s)+$/
139
- select_string value
140
- when /^-?(\d+\.)?\d+$/
141
- select_numeric value
142
- when /(\d\d):(\d\d):\d\d/
143
- select_time "#{Regexp.last_match(1)}#{Regexp.last_match(2)}"
144
- else
145
- select_default
146
- end
147
- # Finally, try to use 'default' if we're still blank
148
- @selected_filename ||= select_default
149
- end
150
-
151
- # Returns the full path to the selected image, or a blank string
152
- def path
153
- unless @selected_filename.nil? || @selected_filename.empty?
154
- return File.join(@base_dir, @name, @selected_filename)
155
- end
156
-
157
- ''
158
- end
159
-
160
- private
161
-
162
- # Match "true.png" or "false.png"
163
- def select_boolean(value)
164
- file_exists_or_blank value.to_s
165
- end
166
-
167
- # Match -5 to _5.png
168
- def select_numeric(value)
169
- file_exists_or_blank value.to_s.gsub('-', '_')
170
- end
171
-
172
- def select_time(value)
173
- times = @filenames.map { |filename| filename.gsub('.png', '') }
174
- times.each_with_index do |time, index|
175
- if (time > value) && (index > 0)
176
- return "#{times[index - 1]}.png"
177
- end
178
- end
179
-
180
- "#{times.last}.png"
181
- end
182
-
183
- # Match "partly cloudy" to "partly_cloudy.png"
184
- def select_string(value)
185
- file_exists_or_blank value.to_s.gsub(' ', '_')
186
- end
187
-
188
- def select_default
189
- @filenames.include?('default.png') ? 'default.png' : ''
190
- end
191
-
192
- # Returns the string "#{filename}.png", if it exists.
193
- #
194
- # Failing that, it returns default.png, or '' if that doesn't exist.
195
- def file_exists_or_blank(filename)
196
- @filenames.include?("#{filename}.png") ? "#{filename}.png" : select_default
197
- end
198
- end