motion-kramdown 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/lib/kramdown/compatibility.rb +36 -0
  4. data/lib/kramdown/converter/base.rb +259 -0
  5. data/lib/kramdown/converter/html.rb +461 -0
  6. data/lib/kramdown/converter/kramdown.rb +423 -0
  7. data/lib/kramdown/converter/latex.rb +600 -0
  8. data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
  9. data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
  10. data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
  11. data/lib/kramdown/converter/pdf.rb +624 -0
  12. data/lib/kramdown/converter/remove_html_tags.rb +53 -0
  13. data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
  14. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
  15. data/lib/kramdown/converter/toc.rb +69 -0
  16. data/lib/kramdown/converter.rb +69 -0
  17. data/lib/kramdown/document.rb +144 -0
  18. data/lib/kramdown/element.rb +515 -0
  19. data/lib/kramdown/error.rb +17 -0
  20. data/lib/kramdown/options.rb +584 -0
  21. data/lib/kramdown/parser/base.rb +130 -0
  22. data/lib/kramdown/parser/gfm.rb +55 -0
  23. data/lib/kramdown/parser/html.rb +575 -0
  24. data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
  25. data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
  26. data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
  27. data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
  28. data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
  29. data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
  30. data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
  31. data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
  32. data/lib/kramdown/parser/kramdown/eob.rb +26 -0
  33. data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
  34. data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
  35. data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/header.rb +59 -0
  37. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
  38. data/lib/kramdown/parser/kramdown/html.rb +160 -0
  39. data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
  40. data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
  41. data/lib/kramdown/parser/kramdown/link.rb +139 -0
  42. data/lib/kramdown/parser/kramdown/list.rb +256 -0
  43. data/lib/kramdown/parser/kramdown/math.rb +54 -0
  44. data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
  45. data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
  46. data/lib/kramdown/parser/kramdown/table.rb +171 -0
  47. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
  48. data/lib/kramdown/parser/kramdown.rb +359 -0
  49. data/lib/kramdown/parser/markdown.rb +56 -0
  50. data/lib/kramdown/parser.rb +27 -0
  51. data/lib/kramdown/utils/configurable.rb +44 -0
  52. data/lib/kramdown/utils/entities.rb +347 -0
  53. data/lib/kramdown/utils/html.rb +75 -0
  54. data/lib/kramdown/utils/ordered_hash.rb +87 -0
  55. data/lib/kramdown/utils/string_scanner.rb +74 -0
  56. data/lib/kramdown/utils/unidecoder.rb +51 -0
  57. data/lib/kramdown/utils.rb +58 -0
  58. data/lib/kramdown/version.rb +15 -0
  59. data/lib/kramdown.rb +10 -0
  60. data/lib/motion-kramdown.rb +47 -0
  61. data/lib/rubymotion/encodings.rb +37 -0
  62. data/lib/rubymotion/rexml_shim.rb +25 -0
  63. data/lib/rubymotion/set.rb +1349 -0
  64. data/lib/rubymotion/version.rb +6 -0
  65. data/spec/document_tree.rb +48 -0
  66. data/spec/gfm_to_html.rb +95 -0
  67. data/spec/helpers/it_behaves_like.rb +27 -0
  68. data/spec/helpers/option_file.rb +46 -0
  69. data/spec/helpers/spec_options.rb +37 -0
  70. data/spec/helpers/tidy.rb +12 -0
  71. data/spec/html_to_html.rb +40 -0
  72. data/spec/html_to_kramdown_to_html.rb +46 -0
  73. data/spec/kramdown_to_xxx.rb +40 -0
  74. data/spec/test_location.rb +203 -0
  75. data/spec/test_string_scanner_kramdown.rb +19 -0
  76. data/spec/text_to_kramdown_to_html.rb +52 -0
  77. data/spec/text_to_latex.rb +33 -0
  78. metadata +164 -0
@@ -0,0 +1,359 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'strscan'
11
+ # RM require 'stringio'
12
+
13
+ #TODO: use [[:alpha:]] in all regexp to allow parsing of international values in 1.9.1
14
+ #NOTE: use @src.pre_match only before other check/match?/... operations, otherwise the content is changed
15
+
16
+ module Kramdown
17
+
18
+ module Parser
19
+
20
+ # Used for parsing a document in kramdown format.
21
+ #
22
+ # If you want to extend the functionality of the parser, you need to do the following:
23
+ #
24
+ # * Create a new subclass
25
+ # * add the needed parser methods
26
+ # * modify the @block_parsers and @span_parsers variables and add the names of your parser
27
+ # methods
28
+ #
29
+ # Here is a small example for an extended parser class that parses ERB style tags as raw text if
30
+ # they are used as span-level elements (an equivalent block-level parser should probably also be
31
+ # made to handle the block case):
32
+ #
33
+ # require 'kramdown/parser/kramdown'
34
+ #
35
+ # class Kramdown::Parser::ERBKramdown < Kramdown::Parser::Kramdown
36
+ #
37
+ # def initialize(source, options)
38
+ # super
39
+ # @span_parsers.unshift(:erb_tags)
40
+ # end
41
+ #
42
+ # ERB_TAGS_START = /<%.*?%>/
43
+ #
44
+ # def parse_erb_tags
45
+ # @src.pos += @src.matched_size
46
+ # @tree.children << Element.new(:raw, @src.matched)
47
+ # end
48
+ # define_parser(:erb_tags, ERB_TAGS_START, '<%')
49
+ #
50
+ # end
51
+ #
52
+ # The new parser can be used like this:
53
+ #
54
+ # require 'kramdown/document'
55
+ # # require the file with the above parser class
56
+ #
57
+ # Kramdown::Document.new(input_text, :input => 'ERBKramdown').to_html
58
+ #
59
+ class Kramdown < Base
60
+
61
+ include ::Kramdown
62
+
63
+ # Create a new Kramdown parser object with the given +options+.
64
+ def initialize(source, options)
65
+ super
66
+
67
+ reset_env
68
+
69
+ @root.options[:abbrev_defs] = {}
70
+ @alds = {}
71
+ @footnotes = {}
72
+ @link_defs = {}
73
+ update_link_definitions(@options[:link_defs])
74
+
75
+ @block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :atx_header,
76
+ :horizontal_rule, :list, :definition_list, :block_html, :setext_header,
77
+ :block_math, :table, :footnote_definition, :link_definition, :abbrev_definition,
78
+ :block_extensions, :eob_marker, :paragraph]
79
+ @span_parsers = [:emphasis, :codespan, :autolink, :span_html, :footnote_marker, :link, :smart_quotes, :inline_math,
80
+ :span_extensions, :html_entity, :typographic_syms, :line_break, :escaped_chars]
81
+
82
+ end
83
+ private_class_method(:new, :allocate)
84
+
85
+
86
+ # The source string provided on initialization is parsed into the @root element.
87
+ def parse
88
+ configure_parser
89
+ parse_blocks(@root, adapt_source(source))
90
+ update_tree(@root)
91
+ replace_abbreviations(@root)
92
+ @footnotes.each {|name,data| update_tree(data[:content])}
93
+ end
94
+
95
+ #######
96
+ protected
97
+ #######
98
+
99
+ # :doc:
100
+ #
101
+ # Update the parser specific link definitions with the data from +link_defs+ (the value of the
102
+ # :link_defs option).
103
+ #
104
+ # The parameter +link_defs+ is a hash where the keys are possibly unnormalized link IDs and
105
+ # the values are two element arrays consisting of the link target and a title (can be +nil+).
106
+ def update_link_definitions(link_defs)
107
+ link_defs.each {|k,v| @link_defs[normalize_link_id(k)] = v}
108
+ end
109
+
110
+ # Adapt the object to allow parsing like specified in the options.
111
+ def configure_parser
112
+ @parsers = {}
113
+ (@block_parsers + @span_parsers).each do |name|
114
+ if self.class.has_parser?(name)
115
+ @parsers[name] = self.class.parser(name)
116
+ else
117
+ raise Kramdown::Error, "Unknown parser: #{name}"
118
+ end
119
+ end
120
+ @span_start, @span_start_re = span_parser_regexps
121
+ end
122
+
123
+ # Create the needed span parser regexps.
124
+ def span_parser_regexps(parsers = @span_parsers)
125
+ span_start = /#{parsers.map {|name| @parsers[name].span_start}.join('|')}/
126
+ [span_start, /(?=#{span_start})/]
127
+ end
128
+
129
+ # Parse all block-level elements in +text+ into the element +el+.
130
+ def parse_blocks(el, text = nil)
131
+ @stack.push([@tree, @src, @block_ial])
132
+ @tree, @block_ial = el, nil
133
+ @src = (text.nil? ? @src : ::Kramdown::Utils::StringScanner.new(text, el.options[:location]))
134
+
135
+ status = catch(:stop_block_parsing) do
136
+ while !@src.eos?
137
+ block_ial_set = @block_ial
138
+ @block_parsers.any? do |name|
139
+ if @src.check(@parsers[name].start_re)
140
+ send(@parsers[name].method)
141
+ else
142
+ false
143
+ end
144
+ end || begin
145
+ warning('Warning: this should not occur - no block parser handled the line')
146
+ add_text(@src.scan(/.*\n/))
147
+ end
148
+ @block_ial = nil if block_ial_set
149
+ end
150
+ end
151
+
152
+ @tree, @src, @block_ial = *@stack.pop
153
+ status
154
+ end
155
+
156
+ # Update the tree by parsing all :+raw_text+ elements with the span-level parser (resets the
157
+ # environment) and by updating the attributes from the IALs.
158
+ def update_tree(element)
159
+ last_blank = nil
160
+ element.children.map! do |child|
161
+ if child.type == :raw_text
162
+ last_blank = nil
163
+ reset_env(:src => ::Kramdown::Utils::StringScanner.new(child.value, element.options[:location]),
164
+ :text_type => :text)
165
+ parse_spans(child)
166
+ child.children
167
+ elsif child.type == :eob
168
+ []
169
+ elsif child.type == :blank
170
+ if last_blank
171
+ last_blank.value << child.value
172
+ []
173
+ else
174
+ last_blank = child
175
+ child
176
+ end
177
+ else
178
+ last_blank = nil
179
+ update_tree(child)
180
+ update_attr_with_ial(child.attr, child.options[:ial]) if child.options[:ial]
181
+ update_raw_header_text(child) if child.type == :header
182
+ child
183
+ end
184
+ end.flatten!
185
+ end
186
+
187
+ # Parse all span-level elements in the source string of @src into +el+.
188
+ #
189
+ # If the parameter +stop_re+ (a regexp) is used, parsing is immediately stopped if the regexp
190
+ # matches and if no block is given or if a block is given and it returns +true+.
191
+ #
192
+ # The parameter +parsers+ can be used to specify the (span-level) parsing methods that should
193
+ # be used for parsing.
194
+ #
195
+ # The parameter +text_type+ specifies the type which should be used for created text nodes.
196
+ def parse_spans(el, stop_re = nil, parsers = nil, text_type = @text_type)
197
+ @stack.push([@tree, @text_type]) unless @tree.nil?
198
+ @tree, @text_type = el, text_type
199
+
200
+ span_start = @span_start
201
+ span_start_re = @span_start_re
202
+ span_start, span_start_re = span_parser_regexps(parsers) if parsers
203
+ parsers = parsers || @span_parsers
204
+
205
+ used_re = (stop_re.nil? ? span_start_re : /(?=#{Regexp.union(stop_re, span_start)})/)
206
+ stop_re_found = false
207
+ while !@src.eos? && !stop_re_found
208
+ if result = @src.scan_until(used_re)
209
+ add_text(result)
210
+ if stop_re && @src.check(stop_re)
211
+ stop_re_found = (block_given? ? yield : true)
212
+ end
213
+ processed = parsers.any? do |name|
214
+ if @src.check(@parsers[name].start_re)
215
+ send(@parsers[name].method)
216
+ true
217
+ else
218
+ false
219
+ end
220
+ end unless stop_re_found
221
+ add_text(@src.getch) if !processed && !stop_re_found
222
+ else
223
+ (add_text(@src.rest); @src.terminate) unless stop_re
224
+ break
225
+ end
226
+ end
227
+
228
+ @tree, @text_type = @stack.pop
229
+
230
+ stop_re_found
231
+ end
232
+
233
+ # Reset the current parsing environment. The parameter +env+ can be used to set initial
234
+ # values for one or more environment variables.
235
+ def reset_env(opts = {})
236
+ opts = {:text_type => :raw_text, :stack => []}.merge(opts)
237
+ @src = opts[:src]
238
+ @tree = opts[:tree]
239
+ @block_ial = opts[:block_ial]
240
+ @stack = opts[:stack]
241
+ @text_type = opts[:text_type]
242
+ end
243
+
244
+ # Return the current parsing environment.
245
+ def save_env
246
+ [@src, @tree, @block_ial, @stack, @text_type]
247
+ end
248
+
249
+ # Restore the current parsing environment.
250
+ def restore_env(env)
251
+ @src, @tree, @block_ial, @stack, @text_type = *env
252
+ end
253
+
254
+ # Update the given attributes hash +attr+ with the information from the inline attribute list
255
+ # +ial+ and all referenced ALDs.
256
+ def update_attr_with_ial(attr, ial)
257
+ ial[:refs].each do |ref|
258
+ update_attr_with_ial(attr, ref) if ref = @alds[ref]
259
+ end if ial[:refs]
260
+ ial.each do |k,v|
261
+ if k == IAL_CLASS_ATTR
262
+ attr[k] = (attr[k] || '') << " #{v}"
263
+ attr[k].lstrip!
264
+ elsif k.kind_of?(String)
265
+ attr[k] = v
266
+ end
267
+ end
268
+ end
269
+
270
+ # Update the raw header text for automatic ID generation.
271
+ def update_raw_header_text(header)
272
+ # DEPRECATED: option auto_id_stripping will be removed in 2.0 because then this will be the
273
+ # default behaviour
274
+ return unless @options[:auto_id_stripping]
275
+ raw_text = ''
276
+
277
+ append_text = lambda do |child|
278
+ if child.type == :text
279
+ raw_text << child.value
280
+ else
281
+ child.children.each {|c| append_text.call(c)}
282
+ end
283
+ end
284
+
285
+ append_text.call(header)
286
+ header.options[:raw_text] = raw_text
287
+ end
288
+
289
+ # Create a new block-level element, taking care of applying a preceding block IAL if it
290
+ # exists. This method should always be used for creating a block-level element!
291
+ def new_block_el(*args)
292
+ el = Element.new(*args)
293
+ el.options[:ial] = @block_ial if @block_ial && el.type != :blank && el.type != :eob
294
+ el
295
+ end
296
+
297
+ @@parsers = {}
298
+
299
+ # Struct class holding all the needed data for one block/span-level parser method.
300
+ Data = Struct.new(:name, :start_re, :span_start, :method)
301
+
302
+ # Add a parser method
303
+ #
304
+ # * with the given +name+,
305
+ # * using +start_re+ as start regexp
306
+ # * and, for span parsers, +span_start+ as a String that can be used in a regexp and
307
+ # which identifies the starting character(s)
308
+ #
309
+ # to the registry. The method name is automatically derived from the +name+ or can explicitly
310
+ # be set by using the +meth_name+ parameter.
311
+ def self.define_parser(name, start_re, span_start = nil, meth_name = "parse_#{name}")
312
+ raise "A parser with the name #{name} already exists!" if @@parsers.has_key?(name)
313
+ @@parsers[name] = Data.new(name, start_re, span_start, meth_name)
314
+ end
315
+
316
+ # Return the Data structure for the parser +name+.
317
+ def self.parser(name = nil)
318
+ @@parsers[name]
319
+ end
320
+
321
+ # Return +true+ if there is a parser called +name+.
322
+ def self.has_parser?(name)
323
+ @@parsers.has_key?(name)
324
+ end
325
+
326
+ # Regexp for matching indentation (one tab or four spaces)
327
+ INDENT = /^(?:\t| {4})/m # RM Oniguruma -> ICU
328
+ # Regexp for matching the optional space (zero or up to three spaces)
329
+ OPT_SPACE = / {0,3}/
330
+
331
+ # RM require 'kramdown/parser/kramdown/blank_line'
332
+ # RM require 'kramdown/parser/kramdown/eob'
333
+ # RM require 'kramdown/parser/kramdown/paragraph'
334
+ # RM require 'kramdown/parser/kramdown/header'
335
+ # RM require 'kramdown/parser/kramdown/blockquote'
336
+ # RM require 'kramdown/parser/kramdown/table'
337
+ # RM require 'kramdown/parser/kramdown/codeblock'
338
+ # RM require 'kramdown/parser/kramdown/horizontal_rule'
339
+ # RM require 'kramdown/parser/kramdown/list'
340
+ # RM require 'kramdown/parser/kramdown/link'
341
+ # RM require 'kramdown/parser/kramdown/extensions'
342
+ # RM require 'kramdown/parser/kramdown/footnote'
343
+ # RM require 'kramdown/parser/kramdown/html'
344
+ # RM require 'kramdown/parser/kramdown/escaped_chars'
345
+ # RM require 'kramdown/parser/kramdown/html_entity'
346
+ # RM require 'kramdown/parser/kramdown/line_break'
347
+ # RM require 'kramdown/parser/kramdown/typographic_symbol'
348
+ # RM require 'kramdown/parser/kramdown/autolink'
349
+ # RM require 'kramdown/parser/kramdown/codespan'
350
+ # RM require 'kramdown/parser/kramdown/emphasis'
351
+ # RM require 'kramdown/parser/kramdown/smart_quotes'
352
+ # RM require 'kramdown/parser/kramdown/math'
353
+ # RM require 'kramdown/parser/kramdown/abbreviation'
354
+
355
+ end
356
+
357
+ end
358
+
359
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/kramdown'
11
+
12
+ module Kramdown
13
+
14
+ module Parser
15
+
16
+ # Used for parsing a document in Markdown format.
17
+ #
18
+ # This parser is based on the kramdown parser and removes the parser methods for the additional
19
+ # non-Markdown features. However, since some things are handled differently by the kramdown
20
+ # parser methods (like deciding when a list item contains just text), this parser differs from
21
+ # real Markdown parsers in some respects.
22
+ #
23
+ # Note, though, that the parser basically fails just one of the Markdown test cases (some others
24
+ # also fail but those failures are negligible).
25
+ class Markdown < Kramdown
26
+
27
+ # Array with all the parsing methods that should be removed from the standard kramdown parser.
28
+ EXTENDED = [:codeblock_fenced, :table, :definition_list, :footnote_definition, :abbrev_definition, :block_math,
29
+ :block_extensions,
30
+ :footnote_marker, :smart_quotes, :inline_math, :span_extensions, :typographic_syms]
31
+
32
+ def initialize(source, options) # :nodoc:
33
+ super
34
+ @block_parsers.delete_if {|i| EXTENDED.include?(i)}
35
+ @span_parsers.delete_if {|i| EXTENDED.include?(i)}
36
+ end
37
+
38
+ # :stopdoc:
39
+
40
+ BLOCK_BOUNDARY = /#{BLANK_LINE}|#{EOB_MARKER}|\Z/
41
+ LAZY_END = /#{BLANK_LINE}|#{EOB_MARKER}|^#{OPT_SPACE}#{LAZY_END_HTML_STOP}|^#{OPT_SPACE}#{LAZY_END_HTML_START}|\Z/
42
+ CODEBLOCK_MATCH = /(?:#{BLANK_LINE}?(?:#{INDENT}[ \t]*\S.*\n)+)*/
43
+ PARAGRAPH_END = LAZY_END
44
+
45
+ IAL_RAND_CHARS = (('a'..'z').to_a + ('0'..'9').to_a)
46
+ IAL_RAND_STRING = (1..20).collect {|a| IAL_RAND_CHARS[rand(IAL_RAND_CHARS.size)]}.join
47
+ LIST_ITEM_IAL = /^\s*(#{IAL_RAND_STRING})?\s*\n/
48
+ IAL_SPAN_START = LIST_ITEM_IAL
49
+
50
+ # :startdoc:
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+
12
+ # This module contains all available parsers. A parser takes an input string and converts the
13
+ # string to an element tree.
14
+ #
15
+ # New parsers should be derived from the Base class which provides common functionality - see its
16
+ # API documentation for how to create a custom converter class.
17
+ module Parser
18
+
19
+ # RM autoload :Base, 'kramdown/parser/base'
20
+ # RM autoload :Kramdown, 'kramdown/parser/kramdown'
21
+ # RM autoload :Html, 'kramdown/parser/html'
22
+ # RM autoload :Markdown, 'kramdown/parser/markdown'
23
+ # RM autoload :GFM, 'kramdown/parser/gfm'
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+ module Utils
12
+
13
+ # Methods for registering configurable extensions.
14
+ module Configurable
15
+
16
+ # Create a new configurable extension called +name+.
17
+ #
18
+ # Three methods will be defined on the calling object which allow to use this configurable
19
+ # extension:
20
+ #
21
+ # configurables:: Returns a hash of hashes that is used to store all configurables of the
22
+ # object.
23
+ #
24
+ # <name>(ext_name):: Return the configured extension +ext_name+.
25
+ #
26
+ # add_<name>(ext_name, data=nil, &block):: Define an extension +ext_name+ by specifying either
27
+ # the data as argument or by using a block.
28
+ def configurable(name)
29
+ singleton_class = (class << self; self; end)
30
+ singleton_class.send(:define_method, :configurables) do
31
+ @_configurables ||= Hash.new {|h, k| h[k] = {}}
32
+ end
33
+ singleton_class.send(:define_method, name) do |data|
34
+ configurables[name][data]
35
+ end
36
+ singleton_class.send(:define_method, "add_#{name}".intern) do |data, *args, &block|
37
+ configurables[name][data] = args.first || block
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end