omniboard 1.1.1
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.
- checksums.yaml +7 -0
- data/HISTORY.md +71 -0
- data/README.md +404 -0
- data/Rakefile +3 -0
- data/bin/omniboard +43 -0
- data/lib/omniboard.rb +25 -0
- data/lib/omniboard/colour.rb +15 -0
- data/lib/omniboard/column.rb +368 -0
- data/lib/omniboard/columns/active.rb +17 -0
- data/lib/omniboard/columns/backburner.rb +5 -0
- data/lib/omniboard/columns/completed.rb +5 -0
- data/lib/omniboard/columns/config.rb +11 -0
- data/lib/omniboard/core_ext.rb +22 -0
- data/lib/omniboard/group.rb +104 -0
- data/lib/omniboard/omniboard.rb +149 -0
- data/lib/omniboard/project_wrapper.rb +119 -0
- data/lib/omniboard/property.rb +53 -0
- data/lib/omniboard/renderer.rb +50 -0
- data/lib/omniboard/styled_text.rb +63 -0
- data/lib/omniboard/styled_text_element.rb +40 -0
- data/lib/omniboard/templates/column.erb +25 -0
- data/lib/omniboard/templates/postamble.erb +226 -0
- data/lib/omniboard/templates/preamble.erb +346 -0
- data/lib/omniboard/templates/project.erb +15 -0
- data/omniboard.gemspec +23 -0
- data/version.txt +1 -0
- metadata +100 -0
data/Rakefile
ADDED
data/bin/omniboard
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "omniboard"
|
4
|
+
|
5
|
+
# Command line options
|
6
|
+
require "trollop"
|
7
|
+
|
8
|
+
opts = Trollop::options do
|
9
|
+
opt :configure, "Location of configuration files. (Default: #{File.join(ENV["HOME"], ".omniboard")})", type: String
|
10
|
+
opt :output, "Where should the html go? If not specified, outputs to STDOUT.", type: String
|
11
|
+
opt :reset, "Wipe cache and start again from the source."
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Set configuration location
|
16
|
+
Omniboard::config_location = opts[:configure] if opts[:configure]
|
17
|
+
|
18
|
+
# If this is a new location, populate appropriately
|
19
|
+
Omniboard::populate if !Omniboard::config_exists?
|
20
|
+
|
21
|
+
# Create or load + update
|
22
|
+
if Omniboard::document_exists? && !opts[:reset]
|
23
|
+
Omniboard::load_document
|
24
|
+
else
|
25
|
+
Omniboard::create_document
|
26
|
+
end
|
27
|
+
|
28
|
+
Omniboard::update_document
|
29
|
+
|
30
|
+
renderer = Omniboard::Renderer.new
|
31
|
+
|
32
|
+
Omniboard::columns.each do |column|
|
33
|
+
column.add Omniboard::projects
|
34
|
+
renderer.add_column(column)
|
35
|
+
end
|
36
|
+
|
37
|
+
if opts[:output]
|
38
|
+
File.open(opts[:output], "w"){ |io| io.puts renderer.to_s }
|
39
|
+
else
|
40
|
+
puts renderer.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
Omniboard::save_document
|
data/lib/omniboard.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Helps iron out some problems
|
2
|
+
Encoding.default_external = "UTF-8"
|
3
|
+
|
4
|
+
module Omniboard; end
|
5
|
+
|
6
|
+
# Rubyfocus library
|
7
|
+
require "rubyfocus"
|
8
|
+
|
9
|
+
# FileUtils for making directories
|
10
|
+
require "fileutils"
|
11
|
+
|
12
|
+
# ERB for rendering web
|
13
|
+
require "erb"
|
14
|
+
|
15
|
+
# Require library files
|
16
|
+
require "omniboard/core_ext"
|
17
|
+
require "omniboard/project_wrapper"
|
18
|
+
require "omniboard/property"
|
19
|
+
require "omniboard/omniboard"
|
20
|
+
require "omniboard/column"
|
21
|
+
require "omniboard/colour"
|
22
|
+
require "omniboard/group"
|
23
|
+
require "omniboard/renderer"
|
24
|
+
require "omniboard/styled_text_element"
|
25
|
+
require "omniboard/styled_text"
|
@@ -0,0 +1,368 @@
|
|
1
|
+
# The column. Each column represents either:
|
2
|
+
# * A grouped column, if Column#group_by or Column.group_by are set
|
3
|
+
# * An ungrouped column, if they aren't
|
4
|
+
class Omniboard::Column
|
5
|
+
include Omniboard::Property
|
6
|
+
|
7
|
+
# Column name, used to display
|
8
|
+
attr_accessor :name
|
9
|
+
def to_s; @name; end
|
10
|
+
|
11
|
+
# All projects contained by the column
|
12
|
+
attr_accessor :projects
|
13
|
+
|
14
|
+
# Blocks governing which projects are let in and how they are displayed
|
15
|
+
block_property :conditions
|
16
|
+
block_property :sort
|
17
|
+
block_property :mark_when
|
18
|
+
block_property :dim_when
|
19
|
+
block_property :icon
|
20
|
+
|
21
|
+
# Blocks governing how to group and how groups are displayed
|
22
|
+
block_property :group_by
|
23
|
+
block_property :sort_groups
|
24
|
+
block_property :group_name
|
25
|
+
|
26
|
+
INHERITED_PROPERTIES = %i(sort mark_when dim_when icon group_by sort_groups group_name hide_dimmed display_project_counts)
|
27
|
+
ALLOWED_PROJECT_COUNTS = %i(all active marked inherit)
|
28
|
+
ALLOWED_PROJECT_DISPLAYS = %i(full compact)
|
29
|
+
|
30
|
+
# Order in the kanban board. Lower numbers are further left. Default 0
|
31
|
+
property :order
|
32
|
+
|
33
|
+
# Relative width of the column. Defaults to 1.
|
34
|
+
property :width
|
35
|
+
|
36
|
+
# Display - compact or full? Full includes task info.
|
37
|
+
property :display, allowed_values: ALLOWED_PROJECT_DISPLAYS
|
38
|
+
|
39
|
+
# Display a heading with the total number of projects in this column?
|
40
|
+
# Allowed values: "all", "active", "marked", nil
|
41
|
+
property :display_project_counts, allowed_values: ALLOWED_PROJECT_COUNTS
|
42
|
+
|
43
|
+
# Display total projects in red if this project limit is breached
|
44
|
+
property :project_limit
|
45
|
+
|
46
|
+
# Do we show a button allowing us to filter our dimmed projects?
|
47
|
+
property :filter_button
|
48
|
+
|
49
|
+
# Do we automatically hide dimmed projects?
|
50
|
+
property :hide_dimmed
|
51
|
+
|
52
|
+
# How many projects to show per line? Defaults to 1.
|
53
|
+
property :columns
|
54
|
+
|
55
|
+
# Intializer. Provide name and block for instance evaluation (optional)
|
56
|
+
def initialize(name, &blck)
|
57
|
+
# Set defaults
|
58
|
+
self.name = name
|
59
|
+
self.projects = []
|
60
|
+
self.order(0)
|
61
|
+
self.width(1)
|
62
|
+
self.columns(1)
|
63
|
+
self.display(:full)
|
64
|
+
self.display_project_counts(nil)
|
65
|
+
self.project_limit(nil)
|
66
|
+
self.filter_button(false)
|
67
|
+
|
68
|
+
INHERITED_PROPERTIES.each{ |s| self.send(s, :inherit) }
|
69
|
+
|
70
|
+
instance_exec(&blck) if blck
|
71
|
+
Omniboard::Column.add(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Fetch the appropriate property value. If the value is :inherit, will fetch the appropriate value from Omniboard::Column
|
75
|
+
def property(sym)
|
76
|
+
raise(ArgumentError, "Unrecognised property #{sym}: allowed values are #{INHERITED_PROPERTIES.join(", ")}.") unless INHERITED_PROPERTIES.include?(sym)
|
77
|
+
v = self.send(sym)
|
78
|
+
v = Omniboard::Column.send(sym) if v == :inherit
|
79
|
+
v
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add an array of projects to the column. If +conditions+ is set, each project will be run through it.
|
83
|
+
# Only projects that return +true+ will be allowed in.
|
84
|
+
def add(arr)
|
85
|
+
# Reset cache
|
86
|
+
@grouped_projects = nil
|
87
|
+
|
88
|
+
# Make sure it's an array
|
89
|
+
arr = [arr] unless arr.is_a?(Array) || arr.is_a?(Rubyfocus::SearchableArray)
|
90
|
+
|
91
|
+
# Run through individual conditions block
|
92
|
+
arr = arr.select{ |p| self.conditions[p] } if self.conditions
|
93
|
+
|
94
|
+
# Run through global conditions block
|
95
|
+
arr = arr.select{ |p| self.class.conditions[p] } if self.class.conditions
|
96
|
+
|
97
|
+
# Wrap in ProjectWrappers
|
98
|
+
arr = arr.map{ |p| p.is_a?(Omniboard::ProjectWrapper) ? p : Omniboard::ProjectWrapper.new(p, column: self) }
|
99
|
+
|
100
|
+
# Tasks performed upon adding project to column. Add to group, mark & dim appropriately
|
101
|
+
arr.each do |pw|
|
102
|
+
pw.column = self
|
103
|
+
|
104
|
+
pw.group = self.group_for(pw)
|
105
|
+
pw.marked = self.should_mark(pw)
|
106
|
+
pw.dimmed = self.should_dim(pw)
|
107
|
+
|
108
|
+
# Icon methods
|
109
|
+
icon_attrs = self.icon_for(pw)
|
110
|
+
if icon_attrs.is_a?(Array)
|
111
|
+
pw.icon, pw.icon_alt = *icon_attrs
|
112
|
+
else
|
113
|
+
pw.icon = icon_attrs
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
@projects += arr
|
118
|
+
end
|
119
|
+
alias_method :<<, :add
|
120
|
+
|
121
|
+
|
122
|
+
# Return an array of projects, sorted according to the sort block (or not, if no sort block supplied).
|
123
|
+
# If group string is provided, only fetched projects for that group
|
124
|
+
def projects(group=nil)
|
125
|
+
p = if group
|
126
|
+
group = Omniboard::Group[group] unless group.is_a?(Omniboard::Group)
|
127
|
+
grouped_projects[group]
|
128
|
+
else
|
129
|
+
@projects
|
130
|
+
end
|
131
|
+
|
132
|
+
sort_block = property(:sort)
|
133
|
+
|
134
|
+
if sort_block.nil?
|
135
|
+
p.sort_by(&:to_s)
|
136
|
+
elsif sort_block.arity == 1
|
137
|
+
p.sort_by(&self.sort)
|
138
|
+
elsif sort_block.arity == 2
|
139
|
+
p.sort(&self.sort)
|
140
|
+
else
|
141
|
+
raise ArgumentError, "Omniboard::Column.sort has an arity of #{sort.arity}, must take either 1 or 2 arguments."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns true if column or global group_by supplied
|
146
|
+
def can_be_grouped?
|
147
|
+
!!property(:group_by)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns a sorted array of groups. Returned as strings
|
151
|
+
def groups
|
152
|
+
keys = grouped_projects.keys.map(&:identifier)
|
153
|
+
|
154
|
+
group_sort_block = property(:sort_groups)
|
155
|
+
if group_sort_block.nil?
|
156
|
+
keys.sort
|
157
|
+
elsif group_sort_block.arity == 1
|
158
|
+
keys.sort_by(&group_sort_block)
|
159
|
+
elsif group_sort_block.arity == 2
|
160
|
+
keys.sort(&group_sort_block)
|
161
|
+
else
|
162
|
+
raise ArgumentError, "Omniboard::Column.group_sort has an arity of #{group_sort_block.arity}, must take either 1 or 2 arguments."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Return a hash of arrays of sorted projects, grouped using the group_by lambda.
|
167
|
+
# Note: Unsorted
|
168
|
+
def grouped_projects
|
169
|
+
raise(RuntimeError, "Attempted to return grouped projects from column #{self.name}, but no group_by method defined.") unless can_be_grouped?
|
170
|
+
@grouped_projects ||= self.projects.group_by(&:group)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return the group a project should fall into
|
174
|
+
def group_for(project)
|
175
|
+
gby = property(:group_by)
|
176
|
+
if gby
|
177
|
+
Omniboard::Group[gby[project]]
|
178
|
+
else
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Return the group name for a given group
|
184
|
+
def group_name_for(group)
|
185
|
+
gname = property(:group_name)
|
186
|
+
gname ? gname[group] : group.to_s
|
187
|
+
end
|
188
|
+
|
189
|
+
# Return the marked status of a given project, based on mark_when blocks
|
190
|
+
def should_mark(project)
|
191
|
+
mark = property(:mark_when)
|
192
|
+
if mark
|
193
|
+
mark[project]
|
194
|
+
else
|
195
|
+
false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Return the dimmed status of a given project, based on mark_when blocks
|
200
|
+
def should_dim(project)
|
201
|
+
dim = property(:dim_when)
|
202
|
+
if dim
|
203
|
+
dim[project]
|
204
|
+
else
|
205
|
+
false
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Return the icon for a given project, based on icon blocks
|
210
|
+
def icon_for(project)
|
211
|
+
ic = property(:icon)
|
212
|
+
if ic
|
213
|
+
ic[project]
|
214
|
+
else
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
#---------------------------------------
|
220
|
+
# Presentation methods
|
221
|
+
def count_div
|
222
|
+
total = case property(:display_project_counts)
|
223
|
+
when :all
|
224
|
+
self.projects.count
|
225
|
+
when :active
|
226
|
+
self.projects.select{ |p| !p.dimmed? }.count
|
227
|
+
when :marked
|
228
|
+
self.projects.select{ |p| p.marked? }.count
|
229
|
+
else
|
230
|
+
0
|
231
|
+
end
|
232
|
+
css_class = "column-total"
|
233
|
+
css_class << " limit-breached" if project_limit && project_limit < total
|
234
|
+
%|<div class="#{css_class}">#{total}</div>|
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
#---------------------------------------
|
239
|
+
# Class-level methods
|
240
|
+
|
241
|
+
# Columns
|
242
|
+
@columns = []
|
243
|
+
|
244
|
+
# Any group colour assignments
|
245
|
+
@colour_groups = []
|
246
|
+
|
247
|
+
# Default values for global config
|
248
|
+
@hide_dimmed = false
|
249
|
+
|
250
|
+
class << self
|
251
|
+
include Omniboard::Property
|
252
|
+
|
253
|
+
attr_reader :columns
|
254
|
+
|
255
|
+
# Add a column to the global columns register
|
256
|
+
def add(c)
|
257
|
+
@columns << c
|
258
|
+
@columns = @columns.sort_by(&:order)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Wipe the global register of columns. Useful when resetting Omniboard to
|
262
|
+
# a default state
|
263
|
+
def reset_columns
|
264
|
+
@columns = []
|
265
|
+
end
|
266
|
+
|
267
|
+
# Configuration
|
268
|
+
|
269
|
+
#---------------------------------------
|
270
|
+
# Font config values
|
271
|
+
property :heading_font
|
272
|
+
property :body_font
|
273
|
+
|
274
|
+
# If set, will provide a link in the heading allowing you to refresh the page
|
275
|
+
property :refresh_link
|
276
|
+
|
277
|
+
# Fallback default for displaying project counts
|
278
|
+
# Allowed values: "all", "active", "marked", nil
|
279
|
+
property :display_project_counts, allowed_values: ALLOWED_PROJECT_COUNTS
|
280
|
+
|
281
|
+
# Global conditions, apply to all columns
|
282
|
+
block_property :conditions
|
283
|
+
|
284
|
+
# Fallback sort method
|
285
|
+
block_property :sort
|
286
|
+
|
287
|
+
# Fallback mark method, apply only if individual mark method is blank
|
288
|
+
block_property :mark_when
|
289
|
+
|
290
|
+
# Fallback mark method, apply only if individual dim method is blank
|
291
|
+
block_property :dim_when
|
292
|
+
|
293
|
+
# Fallback property for hiding dimmed project
|
294
|
+
property :hide_dimmed
|
295
|
+
|
296
|
+
# Fallback group method, apply only if individual column group is blank
|
297
|
+
block_property :group_by
|
298
|
+
|
299
|
+
# Fallback group name method, apply only if individual column group name is blank
|
300
|
+
block_property :group_name
|
301
|
+
|
302
|
+
# Fallback group sort method
|
303
|
+
block_property :sort_groups
|
304
|
+
|
305
|
+
# Fallback icon method
|
306
|
+
block_property :icon
|
307
|
+
|
308
|
+
# Assign a colour to a group, given it fits a block
|
309
|
+
def colour_group(hue, &blck)
|
310
|
+
@colour_groups << {hue: hue, block: blck}
|
311
|
+
end
|
312
|
+
|
313
|
+
# Returns the appropriate hue for a given group, if it matches any of the colour groups provided by @colour_groups
|
314
|
+
def colour_for_group(group)
|
315
|
+
colour_group = @colour_groups.find{ |cgp| cgp[:block][group] }
|
316
|
+
return colour_group && colour_group[:hue]
|
317
|
+
end
|
318
|
+
|
319
|
+
# Config method
|
320
|
+
def config &blck
|
321
|
+
self.instance_exec(&blck)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Clear configuration option. You can always pass :all to clear all configuration options
|
325
|
+
def clear_config config
|
326
|
+
case config
|
327
|
+
when :conditions
|
328
|
+
@conditions = nil
|
329
|
+
when :sort
|
330
|
+
@sort = nil
|
331
|
+
when :group_by
|
332
|
+
@group_by = nil
|
333
|
+
when :mark_when
|
334
|
+
@mark_when = nil
|
335
|
+
when :dim_when
|
336
|
+
@dim_when = nil
|
337
|
+
when :icon
|
338
|
+
@icon = nil
|
339
|
+
when :sort_groups
|
340
|
+
@sort_groups = nil
|
341
|
+
when :group_name
|
342
|
+
@group_name = nil
|
343
|
+
when :hide_dimmed
|
344
|
+
@hide_dimmed = false
|
345
|
+
when :display_project_counts
|
346
|
+
@display_project_counts = nil
|
347
|
+
when :all
|
348
|
+
@conditions = nil
|
349
|
+
@sort = nil
|
350
|
+
@group_by = nil
|
351
|
+
@mark_when = nil
|
352
|
+
@dim_when = nil
|
353
|
+
@icon = nil
|
354
|
+
@sort_groups = nil
|
355
|
+
@group_name = nil
|
356
|
+
@hide_dimmed = false
|
357
|
+
@display_project_counts = nil
|
358
|
+
else
|
359
|
+
raise ArgumentError, "Do not know how to clear config: #{config}"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
#---------------------------------------
|
365
|
+
# Default values
|
366
|
+
heading_font "Helvetica, Arial, sans-serif"
|
367
|
+
body_font "Helvetica, Arial, sans-serif"
|
368
|
+
end
|