barkest_lcd 0.4.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5a99d7096928cf7602e31e972939a1c563943e3e
4
+ data.tar.gz: f0dc18d42e43c626695111307ebec7de8af86431
5
+ SHA512:
6
+ metadata.gz: 0702a57c8942c02aa0bcafd8a1ce43297e41f2f1e067c1f7496853b5326874729a9ed6dce65d88fa01bf1ef29bd5407554de3663fd9c9c8bd1d599a66e974896
7
+ data.tar.gz: 927118633b5bc710774e8077112a84c8b3f64fdfc2c9f18864e57b6bb267bf2cca88b207be20dc3d69a4287cf8688fae5137dd7ec5abb41e51cf0fd1eb24f847
@@ -0,0 +1,11 @@
1
+ .bundle/
2
+ .yardoc
3
+ Gemfile.lock
4
+ _yardoc/
5
+ coverage/
6
+ doc/
7
+ pkg/
8
+ spec/reports/
9
+ tmp/
10
+ .idea/
11
+ /**/.DS_Store
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in barkest_lcd.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Beau Barker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ # BarkestLcd
2
+
3
+ This gem is designed to make it easy to interface with some LCD displays.
4
+
5
+ I wanted to add a front panel LCD to a server I was building, so I started this project to address interacting with
6
+ the LCD panel I picked up.
7
+
8
+ Currently it works with the PicoLCD from [www.mini-box.com](http://www.mini-box.com/picoLCD-256x64-Sideshow-CDROM-Bay).
9
+
10
+
11
+ ## Installation
12
+
13
+ This gem depends on [HID API](http://www.signal11.us/oss/hidapi/).
14
+
15
+ Installation of the dependency is specific to the environment.
16
+
17
+ For OS X (using homebrew):
18
+
19
+ $ brew install hidapi
20
+
21
+ For Ubuntu/Debian:
22
+
23
+ $ sudo apt-get install libhidapi-libusb0
24
+ $ cd /usr/lib/x86_64-linux-gnu
25
+ $ sudo ln -s libhidapi-libusb.so.0.0.0 libhidapi.so
26
+
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'barkest_lcd'
32
+ ```
33
+
34
+ And then execute:
35
+
36
+ $ bundle
37
+
38
+ Or install it yourself as:
39
+
40
+ $ gem install barkest_lcd
41
+
42
+
43
+ ## Usage
44
+
45
+ Let's assume you have one picoLCD 256x64 device attached to your computer. Usage is fairly simple.
46
+
47
+ This example will draw a 32x32 rectangle with an X in it.
48
+
49
+ ```ruby
50
+ my_device = BarkestLcd::PicoLcdGraphic.first
51
+ my_device.open
52
+ my_device.draw_rect(40, 4, 32, 32).draw_line(40, 4, 72, 36).draw_line(40, 36, 72, 4)
53
+ my_device.paint
54
+ ```
55
+
56
+ As you can see, most of the methods are chainable. Unless the method has an explicit return value (like `open?`) then
57
+ the method should be returning the device model. The `open`, `close`, and `paint` methods are all chainable as well.
58
+
59
+ There are currently some very basic drawing methods included by the `SimpleGraphic` module. These include `set_bit`,
60
+ `draw_vline`, `draw_hline`, `draw_line`, and `draw_rect`. The `clear` method will clear the screen.
61
+
62
+ Continuing, we will wait for the user to press the OK button.
63
+
64
+ ```ruby
65
+ OK_BUTTON = 6
66
+ ok_pressed = false
67
+
68
+ # set the callback to process keys when they are released.
69
+ my_device.on_key_up do |key|
70
+ if key == OK_BUTTON
71
+ ok_pressed = true
72
+ end
73
+ end
74
+
75
+ # use the "loop" function to process events with the device and update the screen.
76
+ until ok_pressed
77
+ my_device.loop
78
+ end
79
+ ```
80
+
81
+ In this case we set the `on_key_up` callback to set a flag when the OK button is pressed. If we wanted to do repeating
82
+ keys when a user holds down a button, we may want to set the `on_key_down` or use the `key_state` method within the loop.
83
+
84
+ Finally, I recommend closing the device when you are finished. Ruby will close the device when it exits, but if you
85
+ are handling errors and such, closing the device explicitly in an `ensure` block will help to protect you against being
86
+ unable to open the device again.
87
+
88
+ ```ruby
89
+ my_device.clear.paint
90
+ my_device.close
91
+ ```
92
+
93
+ It is not necessary to clear the screen before closing, but doing so ensures that you do not leave weird information
94
+ or screen artifacts up when your application is done.
95
+
96
+
97
+
98
+ ## Credits
99
+
100
+ * The [picoLCD 256x64 Suite Source](http://resources.mini-box.com/online/picolcd/256x64/1003/PicoLCD256x64_src.zip)
101
+ provided most of the information needed to make this gem interact with the picoLCD 256x64 from
102
+ [mini-box](http://www.mini-box.com).
103
+ * The [picoLCD256x64](https://github.com/itszero/picoLCD256x64) project provided some inspiration, but I ended up going
104
+ a completely different direction.
105
+ * The font came from [Silkscreen](http://www.kottke.org/plus/type/silkscreen/index.html).
106
+
107
+ ## License
108
+
109
+ Copyright (c) 2016 [Beau Barker](mailto:beau@barkerest.com)
110
+
111
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
112
+
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'lib'
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*_test.rb'
8
+ t.verbose = false
9
+ end
10
+
11
+ task :default => :test
12
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'barkest_lcd/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "barkest_lcd"
8
+ spec.version = BarkestLcd::VERSION
9
+ spec.authors = ["Beau Barker"]
10
+ spec.email = ["rtecoder@gmail.com"]
11
+
12
+ spec.summary = "Provides a simple interface to LCD displays."
13
+ spec.description = "This gem was originally created to interface with a PicoLCD 256x64 from www.mini-box.com."
14
+ spec.homepage = "http://www.barkerest.com/"
15
+ spec.license = "MIT"
16
+
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency 'libusb', '~>0.5.1'
24
+ spec.add_dependency 'hidapi', '>=0.1.4'
25
+
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.12'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "barkest_lcd"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
Binary file
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ require 'barkest_lcd/version'
2
+ require 'hidapi'
3
+
4
+ module BarkestLcd
5
+
6
+ end
7
+
8
+ Dir.glob(File.expand_path('../models/*.rb', __FILE__)).each do |file|
9
+ require file
10
+ end
@@ -0,0 +1,3 @@
1
+ module BarkestLcd
2
+ VERSION = "0.4.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ module BarkestLcd
2
+ module ErrorLogger
3
+
4
+ public
5
+
6
+ ##
7
+ # Contains the last 100 errors that have occurred in this instance.
8
+ def error_history
9
+ @error_history ||= []
10
+ end
11
+
12
+ protected
13
+
14
+ def log_error(code, msg)
15
+ HIDAPI.debug "Encountered error (#{code.to_s(16).rjust(8, '0')}) #{msg}"
16
+ error_history << [ code, msg, Time.now ]
17
+ error_history.delete_at(0) while error_history.count > 100
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,404 @@
1
+ module BarkestLcd
2
+ ##
3
+ # A basic bitmap font.
4
+ class Font
5
+
6
+ ##
7
+ # Gets or sets the name of this font.
8
+ attr_accessor :name
9
+
10
+ ##
11
+ # Gets or sets the size of this font.
12
+ attr_accessor :size
13
+
14
+ ##
15
+ # Gets or sets the bold flag.
16
+ attr_accessor :bold
17
+
18
+ ##
19
+ # Creates a new bitmap font from a hash.
20
+ #
21
+ # glyphs = {
22
+ # :name => "Font Name", # optional
23
+ # :size => 8, # optional
24
+ # :bold => false, # optional
25
+ # 97 => { # ASCII character code.
26
+ # :char => "a", # printed character.
27
+ # :width => 8, # width in pixels
28
+ # :height => 8, # height in pixels
29
+ # :data => [ # array containing row data.
30
+ # " ", # rows may be strings or arrays.
31
+ # " ", # when strings, non-space characters are set pixels.
32
+ # " ** ", # when arrays, values of true or 1 are set pixels.
33
+ # " * ",
34
+ # " **** ",
35
+ # " * * ",
36
+ # " **** ",
37
+ # " ",
38
+ # ],
39
+ # },
40
+ # ...
41
+ # }
42
+ def initialize(glyphs)
43
+ raise ArgumentError unless glyphs.is_a?(Hash)
44
+
45
+ @name = glyphs.delete(:name) || 'Font'
46
+ @size = glyphs.delete(:size) || 8
47
+ @bold = glyphs.delete(:bold) || false
48
+ @glyphs = {}
49
+
50
+ glyphs.each do |k,v|
51
+ k = k.to_i
52
+ raise ArgumentError, 'hash must have numeric keys greater than or equal to 32 and less than 127' if k < 32 || k > 127
53
+ raise ArgumentError, 'hash must have hashes as values' unless v.is_a?(Hash)
54
+ raise ArgumentError, 'hash values must have a :char key' unless v[:char]
55
+ raise ArgumentError, 'hash values must have a :width key' unless v[:width]
56
+ raise ArgumentError, 'hash values must have a :height key' unless v[:height]
57
+ raise ArgumentError, 'hash values must have a :data key' unless v[:data]
58
+ raise ArgumentError, 'hash :data key must have an array value' unless v[:data].is_a?(Array)
59
+
60
+ glyph = {
61
+ char: v[:char].to_s.freeze,
62
+ width: v[:width].to_i.freeze,
63
+ height: v[:height].to_i.freeze,
64
+ data: []
65
+ }
66
+
67
+ raise ArgumentError, 'hash :data value must have exactly :height members' unless v[:data].length == glyph[:height]
68
+ v[:data].each_with_index do |row, row_index|
69
+ raise ArgumentError, 'hash :data value members must be arrays or strings' unless row.is_a?(Array) || row.is_a?(String)
70
+ raise ArgumentError, 'hash :data value members must have :width values' unless row.length == glyph[:width]
71
+
72
+ glyph[:data][row_index] = [ false ] * glyph[:width]
73
+ if row.is_a?(String)
74
+ row.chars.each_with_index do |ch, ch_index|
75
+ glyph[:data][row_index][ch_index] = true unless ch == ' ' # spaces are false, everything else is true.
76
+ end
77
+ else
78
+ row.each_with_index do |bit, bit_index|
79
+ glyph[:data][row_index][bit_index] = true if bit == true || bit == 1
80
+ end
81
+ end
82
+ glyph[:data][row_index].freeze
83
+
84
+ end
85
+
86
+ add_glyph_helpers_to glyph
87
+
88
+ glyph[:data].freeze
89
+
90
+ glyph.freeze
91
+
92
+ @glyphs[k] = glyph
93
+ end
94
+
95
+ @height = @glyphs.inject(0) { |h,(key,g)| g.height > h ? g.height : h }
96
+ @nil_glyph = add_glyph_helpers_to(
97
+ {
98
+ char: '',
99
+ width: 0,
100
+ height: 0,
101
+ data: []
102
+ }
103
+ ).freeze
104
+ end
105
+
106
+
107
+ ##
108
+ # Gets the height of the font.
109
+ attr_reader :height
110
+
111
+ ##
112
+ # Gets a glyph for the specified character.
113
+ #
114
+ # +char+ should be a string containing the character.
115
+ #
116
+ # Glyphs are hashes that also include helper methods.
117
+ #
118
+ # g = {
119
+ # char: ' ',
120
+ # width: 2,
121
+ # height: 8,
122
+ # data: [[false,false],[false,false],[false,false],[false,false],[false,false],[false,false],[false,false],[false,false]]
123
+ # }
124
+ # g.char == g[:char]
125
+ # g.width == g[:width]
126
+ # g.height == g[:height]
127
+ # g.data == g[:data]
128
+ #
129
+ def glyph(char)
130
+ char = char.to_s
131
+ ch = char.getbyte(0).to_i
132
+ @glyphs[ch] || @nil_glyph
133
+ end
134
+
135
+ ##
136
+ # Gets the glyphs to draw the specified string with.
137
+ def glyphs(string)
138
+ string.to_s.chars.map { |char| glyph(char) }
139
+ end
140
+
141
+ ##
142
+ # Measures the specified string.
143
+ #
144
+ # If you supply a +max_width+ it will try to fit the string into the specified width.
145
+ #
146
+ # With a +max_width+ it returns [ width, height, lines ].
147
+ # Without a +max_width+ it returns [ width, height ].
148
+ def measure(string, max_width = -1)
149
+
150
+ # handle newlines properly.
151
+ if string.include?("\n")
152
+ w = 0
153
+ h = 0
154
+ lines = []
155
+ string.split("\n").each do |line|
156
+ w2, h2, lines2 = measure(line, max_width)
157
+ w = w2 if w2 > w
158
+ h += h2
159
+ lines += lines2 if lines2
160
+ end
161
+ return [ w, h, lines ] if max_width > 0
162
+ return [ w, h ]
163
+ end
164
+
165
+ # convert to string and replace all whitespace with actual spaces.
166
+ # we don't support tabs or care about carriage returns.
167
+ # we also want to reduce all whitespace sequences to a single space.
168
+ string = string.to_s.gsub(/\s/, ' ').gsub(/\s\s+/, ' ')
169
+
170
+ if max_width > 0
171
+ # we are trying to fit the string into a specific width.
172
+
173
+ # no need to measure an empty string.
174
+ return [ 0, height, [ string ]] if string == ''
175
+
176
+ # measure the initial string.
177
+ w, h = measure(string)
178
+
179
+ # we fit or there are no spaces to wrap on.
180
+ if w <= max_width || !string.include?(' ')
181
+ return [w, h, [ string ]]
182
+ else
183
+
184
+ # prepare to wrap on word boundaries.
185
+ cur_line,_,next_line = string.rpartition(' ')
186
+
187
+ # keep chopping off words until we can't chop any more off or we fit.
188
+ while true
189
+ # measure the current line.
190
+ w, h = measure(cur_line)
191
+
192
+ if w <= max_width || !cur_line.include?(' ')
193
+ # we fit or we can't split anymore.
194
+
195
+ # measure up the rest of the string.
196
+ w2, h2, lines = measure(next_line, max_width)
197
+
198
+ # and adjust the size as needed.
199
+ w = w2 if w2 > w
200
+ h += h2
201
+
202
+ # return the adjusted size and the lines.
203
+ return [ w, h, [ cur_line ] + lines ]
204
+ end
205
+
206
+ # chop off the next word.
207
+ cur_line,_,next_word = cur_line.rpartition(' ')
208
+
209
+ # add the chopped off word to the beginning of the next line.
210
+ next_line = next_word + ' ' + next_line
211
+ end
212
+ end
213
+
214
+
215
+ else
216
+ # we are not trying to fit the string.
217
+
218
+ # no need to measure an empty string.
219
+ return [ 0, height ] if string == ''
220
+
221
+ h = height
222
+ w = glyphs(string).inject(0) { |_w,g| _w + g[:width] }
223
+
224
+ [w, h]
225
+ end
226
+ end
227
+
228
+ ##
229
+ # Generates a hash that can be loaded into a font.
230
+ def inspect(formatted = false)
231
+ ret = '{'
232
+ ret += "\n " if formatted
233
+ ret += ":name => #{name.inspect},"
234
+ ret += "\n " if formatted
235
+ ret += ":size => #{size.inspect},"
236
+ ret += "\n " if formatted
237
+ ret += ":bold => #{bold.inspect},"
238
+ ret += "\n " if formatted
239
+ @glyphs.each do |key, glyph|
240
+ ret += "#{key.inspect} => {"
241
+ ret += "\n " if formatted
242
+ ret += ":char => #{glyph[:char].inspect},"
243
+ ret += "\n " if formatted
244
+ ret += ":width => #{glyph[:width].inspect},"
245
+ ret += "\n " if formatted
246
+ ret += ":height => #{glyph[:height].inspect},"
247
+ ret += "\n " if formatted
248
+ ret += ':data => ['
249
+ ret += "\n " if formatted
250
+ glyph[:data].each do |row|
251
+ ret += "#{row.map{|bit| bit ? '#' : ' '}.join('').inspect},"
252
+ ret += "\n " if formatted
253
+ end
254
+ ret = ret.rstrip + "\n " if formatted
255
+ ret += '],'
256
+ ret += "\n " if formatted
257
+ ret += '},'
258
+ ret += "\n " if formatted
259
+ end
260
+ ret = ret.rstrip + "\n" if formatted
261
+ ret + '}'
262
+ end
263
+
264
+ def to_s # :nodoc:
265
+ "#{name} #{bold ? 'Bold' : 'Regular'} #{size}pt"
266
+ end
267
+
268
+ def freeze # :nodoc
269
+ name.freeze
270
+ size.freeze
271
+ bold.freeze
272
+ super
273
+ end
274
+
275
+ ##
276
+ # Attempts to load and process a font.
277
+ #
278
+ # Returns a Font on success, or nil on failure.
279
+ #
280
+ # There is a possibility that it will load an incorrect font.
281
+ #
282
+ # REQUIRES: rmagick
283
+ #
284
+ # Since the BarkestLcd gem doesn't require 'rmagick', this will always return false by default.
285
+ # If your application includes the 'rmagick' gem, then you can create fonts. Or you can use an
286
+ # IRB console to create fonts.
287
+ #
288
+ # The created fonts should be stored as a ruby object. The constants defined in the BarkestLcd::Font class
289
+ # were created in this manner.
290
+ def self.create(font_name = 'Helvetica', size = 8, bold = false)
291
+ return nil if font_name.to_s == ''
292
+ return nil if size < 4 || size > 144
293
+
294
+ begin
295
+ require 'rmagick'
296
+
297
+ max_black = (Magick::QuantumRange * 0.5).to_i
298
+
299
+ img = Magick::Image.new(200,200)
300
+
301
+ def img.char_width
302
+ get_pixels(0, 0, columns, 1).each_with_index do |px, x|
303
+ return x if px.to_color == 'magenta'
304
+ end
305
+ nil
306
+ end
307
+
308
+ def img.char_height
309
+ get_pixels(0, 0, 1, rows).each_with_index do |px, y|
310
+ return y if px.to_color == 'magenta'
311
+ end
312
+ nil
313
+ end
314
+
315
+ img.background_color = 'magenta'
316
+ img.erase!
317
+
318
+ draw = Magick::Draw.new
319
+ draw.font = font_name
320
+ draw.font_weight bold ? 'bold' : 'normal'
321
+ draw.pointsize = size
322
+ draw.gravity = Magick::NorthWestGravity
323
+ draw.text_antialias = false
324
+ draw.fill = 'black'
325
+ draw.undercolor = 'white'
326
+ draw.stroke = 'transparent'
327
+
328
+ font = {
329
+ name: font_name,
330
+ size: size,
331
+ bold: !!bold,
332
+ }
333
+
334
+ " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(){}[]<>`~!@#$%^&*-_=+\\|;:'\",./?".chars.each do |ch|
335
+
336
+ glyph = {
337
+ char: ch
338
+ }
339
+
340
+ char_code = ch.getbyte(0)
341
+
342
+ ch = "\\\\" if ch == "\\"
343
+ ch = "\\\"" if ch == "\""
344
+
345
+ img.erase!
346
+ draw.annotate img, 0, 0, 0, 0, ch
347
+
348
+ width = img.char_width
349
+ height = img.char_height
350
+
351
+ if width.nil?
352
+ puts "Error on char #{ch.inspect}: no width"
353
+ elsif height.nil?
354
+ puts "Error on char #{ch.inspect}: no height"
355
+ else
356
+ glyph[:width] = width
357
+ glyph[:height] = height
358
+ glyph[:data] = []
359
+
360
+ img.get_pixels(0, 0, width, height).each_with_index do |px, index|
361
+ x = (index % width).to_i
362
+ y = (index / width).to_i
363
+
364
+ glyph[:data][y] ||= []
365
+ glyph[:data][y][x] = px.intensity <= max_black
366
+ end
367
+
368
+ font[char_code] = glyph
369
+ end
370
+
371
+ end
372
+
373
+ Font.new(font)
374
+ rescue LoadError
375
+ nil
376
+ end
377
+ end
378
+
379
+ private
380
+
381
+ def add_glyph_helpers_to(glyph)
382
+ def glyph.char
383
+ self[:char]
384
+ end
385
+ def glyph.width
386
+ self[:width]
387
+ end
388
+ def glyph.height
389
+ self[:height]
390
+ end
391
+ def glyph.data
392
+ self[:data]
393
+ end
394
+ glyph
395
+ end
396
+
397
+ end
398
+ end
399
+
400
+
401
+ # Include the components of the model.
402
+ Dir.glob(File.expand_path('../font/*.rb', __FILE__)).each do |file|
403
+ require file
404
+ end