acts_as_markup_extended 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2011-06-29
2
+
3
+ * Initial Release
4
+ * Forked from acts_as_markup
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2008 Brian Landau of Viget Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,198 @@
1
+ = Acts as Markup Extended
2
+
3
+ * by Mitch Marx <mitch.marx@mdpaladin.com>
4
+ * based on acts_as_markup by Brian Landau of Viget Labs <brian.landau@viget.com>
5
+
6
+ GitHub Project: http://github.com/mitchmarx/acts_as_markup_extended
7
+
8
+ RDoc:
9
+ * http://gitrdoc.com/vigetlabs/acts_as_markup/tree/master
10
+ * http://vigetlabs.github.com/acts_as_markup
11
+
12
+
13
+ == NOTE:
14
+
15
+ This is an enhanced version of Brian Landau's acts_as_markup. It includes the
16
+ ":extensions =>" option which allows you to specify methods that extend markup
17
+ languages (see the samples in lib/markup_methods).
18
+
19
+ == DESCRIPTION:
20
+
21
+ Allows you to specify columns of an ActiveRecord model that contain Markdown,
22
+ Textile, Wiki text and RDoc. You may then use +to_s+ to get the original markup
23
+ text or +to_html+ to get the formated HTML.
24
+
25
+ Additionally you can have a model that contains a column that has a column with
26
+ markup text, and another that defines what language to process it as. If the field
27
+ is listed as "markdown" "textile", "wikitext" or "rdoc" (case insensitive) it will
28
+ treat it as such, any other value for markup language will have the value pass
29
+ through as a normal string.
30
+
31
+ You can use 4 different types of Markdown processing backends:
32
+ BlueCloth, RDiscount, Ruby PEG or Maruku. You specify which one you want to use by setting
33
+ a config value in your environment.rb file:
34
+
35
+ ActsAsMarkup.markdown_library = :bluecloth
36
+
37
+ By default RDiscount will be used.
38
+
39
+ You can specify additional options to pass to the markup library by using
40
+ <tt>:markdown_options</tt>, <tt>:textile_options</tt> or <tt>:wikitext_options</tt>.
41
+ RDoc does not support any useful options. The options should be given as an
42
+ array of arguments. You can specify options for multiple languages when
43
+ allowing more than one. See each library's documentation for more details on
44
+ what options are available.
45
+
46
+ == MARKUP EXTENSION METHODS:
47
+
48
+ For any column you can specify markup extension methods by including
49
+ <tt>:extensions =></tt> in any of the forms of acts_as_markup illustrated below.
50
+
51
+ Sample extensions methods are in: <tt>lib/markup_extensions</tt>.
52
+ You can include methods that reside anywhere as long as they are
53
+ in module MarkupExtensionMethods.
54
+
55
+ You can invoke all the methods in module MarkupExtensionMethods with <tt>:extensions => :all</tt>.
56
+
57
+ == EXAMPLES:
58
+
59
+ ==== Using +acts_as_markdown+:
60
+
61
+ class Post < ActiveRecord
62
+ acts_as_markdown :body
63
+ end
64
+
65
+ @post = Post.find(:first)
66
+ @post.body.to_s #=> "## Markdown Headline"
67
+ @post.body.to_html #=> "<h2> Markdown Headline</h2>"
68
+
69
+
70
+ ==== Using +acts_as_textile+:
71
+
72
+ class Post < ActiveRecord
73
+ acts_as_textile :body
74
+ end
75
+
76
+ @post = Post.find(:first)
77
+ @post.body.to_s #=> "h2. Textile Headline"
78
+ @post.body.to_html #=> "<h2>Textile Headline</h2>"
79
+
80
+
81
+ ==== Using +acts_as_wikitext+:
82
+
83
+ class Post < ActiveRecord
84
+ acts_as_wikitext :body
85
+ end
86
+
87
+ @post = Post.find(:first)
88
+ @post.body.to_s #=> "== Wikitext Headline =="
89
+ @post.body.to_html #=> "<h2>Wikitext Headline</h2>"
90
+
91
+
92
+ ==== Using +acts_as_rdoc+:
93
+
94
+ class Post < ActiveRecord
95
+ acts_as_rdoc :body
96
+ end
97
+
98
+ @post = Post.find(:first)
99
+ @post.body.to_s #=> "== RDoc Headline"
100
+ @post.body.to_html #=> "<h2>RDoc Headline</h2>"
101
+
102
+
103
+ ==== Using +acts_as_markup+:
104
+
105
+ class Post < ActiveRecord
106
+ acts_as_markup :language => :markdown, :columns => [:body]
107
+ end
108
+
109
+ @post = Post.find(:first)
110
+ @post.body.to_s #=> "## Markdown Headline"
111
+ @post.body.to_html #=> "<h2> Markdown Headline</h2>"
112
+
113
+
114
+ ==== Using +acts_as_markup+ with <tt>:variable</tt> language:
115
+
116
+ class Post < ActiveRecord
117
+ acts_as_markup :language => :variable, :columns => [:body]
118
+ end
119
+
120
+ @post = Post.find(:first)
121
+ @post.markup_language # => "markdown"
122
+ @post.body.to_s # => "## Markdown Headline"
123
+ @post.body.to_html # => "<h2> Markdown Headline</h2>"
124
+
125
+
126
+ ==== Using options
127
+
128
+ class Post < ActiveRecord
129
+ acts_as_markdown :body, :markdown_options => [ :filter_html ]
130
+ end
131
+
132
+ class Post < ActiveRecord
133
+ acts_as_textile :body, :textile_options => [ [ :filter_html ] ]
134
+ end
135
+
136
+ class Post < ActiveRecord
137
+ acts_as_wikitext :body, :wikitext_options => [ { :space_to_underscore => true } ]
138
+ end
139
+
140
+ ==== With markup extension methods
141
+
142
+ class Post < ActiveRecord
143
+ acts_as_markup :language => :markdown, :columns => [:body],
144
+ :extensions => [:method1, method2, ...].
145
+ end
146
+
147
+ class Post < ActiveRecord
148
+ acts_as_markdown :body, :extensions => :all
149
+ end
150
+
151
+
152
+ == REQUIREMENTS:
153
+
154
+ You will need the RedCloth[http://whytheluckystiff.net/ruby/redcloth/] library
155
+ for processing the Textile text, and the Wikitext[http://wikitext.rubyforge.org/]
156
+ library for processing wikitext.
157
+
158
+ You will also need to install some type of Markdown processor.
159
+ The three options currently supported are:
160
+
161
+ * BlueCloth
162
+ * RDiscount[http://github.com/rtomayko/rdiscount]
163
+ * {Ruby PEG}[http://github.com/rtomayko/rpeg-markdown/tree/master]
164
+ * Maruku[http://maruku.rubyforge.org/]
165
+
166
+ == INSTALL:
167
+
168
+ <tt>sudo gem install acts_as_markup</tt>
169
+
170
+ Add "+acts_as_markup+" to your environment.rb:
171
+
172
+ config.gem "acts_as_markup"
173
+
174
+ == CONTRIBUTING:
175
+
176
+ Make a fork on GitHub, make your changes and do a pull request. Good places to start are adding new Markdown libraries or new markup languages, here's instructions for both:
177
+
178
+ === Instructions for how to add a new Markdown Library:
179
+
180
+ 1. Add another item to the <tt>ActsAsMarkup::MARKDOWN_LIBS</tt> hash in the form of:
181
+ :bluecloth => {:class_name => "BlueCloth",
182
+ :lib_name => "bluecloth"}
183
+ <tt>:lib_name</tt> should be the name needed to require the library, while <tt>:class_name</tt> should be the class that we are making an instance of.
184
+ 2. If you need to modify the object in anyway (e.g. to add a <tt>to_s</tt> or <tt>to_html</tt> method), add a file to the "lib/acts_as_markup/exts/" directory.
185
+ 3. Add appropriate tests (see current tests).
186
+
187
+ === Instructions for how to add a new Markup Language:
188
+
189
+ 1. Add a "<tt>when</tt>" statement to the "<tt>case</tt>" statement in <tt>acts_as_markup</tt>. The "<tt>when</tt>" statement should match with a symbol that represents the language name in some way (e.g. "<tt>:markdown</tt>").
190
+ 2. In the "<tt>when</tt>" block you need to set the "<tt>klass</tt>" local variable and require the library and the extension file if you need one (use the special <tt>require_extensions</tt> method to require extensions).
191
+ 3. Add the same lines you added to the previous "<tt>when</tt>" statement to the "<tt>:variable</tt>" "<tt>when</tt>" statement. But replace "<tt>klass</tt>" with "<tt>language_klass</tt>" (e.g. "<tt>markdown_klass</tt>").
192
+ 4. Add a relevant "<tt>when</tt>" statement to the <tt>class_eval</tt> block for the "<tt>:variable</tt>" language option. This should look something like:
193
+ when /markdown/i
194
+ markup_klasses[:markdown].new self[col].to_s
195
+ 5. Add a convenience method (e.g. "<tt>acts_as_markdown</tt>")
196
+ 6. Add an extension file to the "lib/acts_as_markup/exts/" directory if you need to modify the object in anyway.
197
+ 7. Add appropriate tests (see current tests).
198
+
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+ $:.unshift(File.expand_path('lib'))
5
+
6
+ require 'rake'
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gem|
11
+ gem.name = "acts_as_markup_extended"
12
+ gem.summary = %Q{Represent ActiveRecord Markdown, Textile, Wiki text, RDoc columns as Markdown, Textile Wikitext, RDoc objects using various external libraries to convert to HTML. Includes markup extension methods.}
13
+ gem.description = %Q{Represent ActiveRecord Markdown, Textile, Wiki text, RDoc columns as Markdown, Textile Wikitext, RDoc objects using various external libraries to convert to HTML. Includes markup extension methods.}
14
+ gem.email = "mitch.marx@mdpaladin.com"
15
+ gem.homepage = "https://github.com/mitchmarx/acts_as_markup_extended/"
16
+ gem.authors = ["Mitch Marx"]
17
+ gem.version = '1.0.1'
18
+ gem.add_dependency('activesupport', '>= 2.3.2')
19
+ gem.add_dependency('activerecord', '>= 2.3.2')
20
+ gem.add_dependency('rdiscount', '~> 1.3')
21
+ gem.add_dependency('wikitext', '~> 2.0')
22
+ gem.add_dependency('RedCloth', '~> 4.2')
23
+ gem.files = FileList["[A-Z]*", "{lib,test}/**/*"]
24
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
25
+ end
26
+ Jeweler::GemcutterTasks.new
27
+ rescue LoadError
28
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
29
+ end
30
+
31
+ require 'tasks/test'
32
+ require 'tasks/rdoc'
33
+
34
+ task :test => :check_dependencies
35
+ task :default => :test
@@ -0,0 +1,251 @@
1
+ require 'active_record'
2
+
3
+ module ActiveRecord # :nodoc:
4
+ module Acts # :nodoc:
5
+ module AsMarkup
6
+ def self.included(base) # :nodoc:
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # This allows you to specify columns you want to define as containing
13
+ # Markdown, Textile, Wikitext or RDoc content.
14
+ # Then you can simply call <tt>.to_html</tt> method on the attribute.
15
+ #
16
+ # You can also specify the language as <tt>:variable</tt>. The language used
17
+ # to process the column will be based on another column. By default a column
18
+ # named "<tt>markup_language</tt>" is used, but this can be changed by providing
19
+ # a <tt>:language_column</tt> option. When a value is accessed it will create
20
+ # the correct object (Markdown, Textile, Wikitext or RDoc) based on the value
21
+ # of the language column. If any value besides markdown, textile, wikitext, or
22
+ # RDoc is supplied for the markup language the text will pass through as a string.
23
+ #
24
+ # You can specify additional options to pass to the markup library by using
25
+ # <tt>:markdown_options</tt>, <tt>:textile_options</tt> or <tt>:wikitext_options</tt>.
26
+ # RDoc does not support any useful options. The options should be given as an array
27
+ # of arguments. You can specify options for more than one language when using
28
+ # <tt>:variable</tt>. See each library's documentation for more details on what
29
+ # options are available.
30
+ #
31
+ # For any column you can specify markup extensions by including
32
+ # <tt>:extensions =></tt> in any of the forms of acts_as_markup illustrated below.
33
+ #
34
+ # Sample extensions methods are in: lib/markup_extensions.
35
+ # You can include methods that reside anywhere as long as they are
36
+ # in module MarkupExtensionMethods. (see the samples in lib/markup_methods).
37
+ #
38
+ # You can invoke all the methods in module MarkupExtensionMethods with :extensions => :all.
39
+ #
40
+ # ==== Examples
41
+ #
42
+ # ===== Using Markdown language
43
+ #
44
+ # class Post < ActiveRecord
45
+ # acts_as_markup :language => :markdown, :columns => [:body]
46
+ # end
47
+ #
48
+ # @post = Post.find(:first)
49
+ # @post.body.to_s # => "## Markdown Headline"
50
+ # @post.body.to_html # => "<h2> Markdown Headline</h2>"
51
+ #
52
+ #
53
+ # ===== Using variable language
54
+ #
55
+ # class Post < ActiveRecord
56
+ # acts_as_markup :language => :variable, :columns => [:body], :language_column => 'language_name'
57
+ # end
58
+ #
59
+ # @post = Post.find(:first)
60
+ # @post.language_name # => "markdown"
61
+ # @post.body.to_s # => "## Markdown Headline"
62
+ # @post.body.to_html # => "<h2> Markdown Headline</h2>"
63
+ #
64
+ #
65
+ # ===== Using options
66
+ #
67
+ # class Post < ActiveRecord
68
+ # acts_as_markup :language => :markdown, :columns => [:body], :markdown_options => [ :filter_html ]
69
+ # end
70
+ #
71
+ # class Post < ActiveRecord
72
+ # acts_as_markup :language => :textile, :columns => [:body], :textile_options => [ [ :filter_html ] ]
73
+ # end
74
+ #
75
+ # class Post < ActiveRecord
76
+ # acts_as_markup :language => :wikitext, :columns => [:body], :wikitext_options => [ { :space_to_underscore => true } ]
77
+ # end
78
+ #
79
+ # ===== With markup extension methods
80
+ #
81
+ # class Post < ActiveRecord
82
+ # acts_as_markup :language => :markdown, :columns => [:body],
83
+ # :extensions => [:method1, method2, ...].
84
+ # end
85
+ #
86
+ # class Post < ActiveRecord
87
+ # acts_as_markdown :body, :extensions => :all
88
+ # end
89
+
90
+ def acts_as_markup(options)
91
+ case options[:language].to_sym
92
+ when :markdown, :textile, :wikitext, :rdoc
93
+ klass = require_library_and_get_class(options[:language].to_sym)
94
+ when :variable
95
+ markup_klasses = {}
96
+ [:textile, :wikitext, :rdoc, :markdown].each do |language|
97
+ markup_klasses[language] = require_library_and_get_class(language)
98
+ end
99
+ options[:language_column] ||= :markup_language
100
+ else
101
+ raise ActsAsMarkup::UnsupportedMarkupLanguage, "#{options[:langauge]} is not a currently supported markup language."
102
+ end
103
+
104
+ # create the proc object in current scope
105
+ set_markup_object = markup_object_proc
106
+ unless options[:language].to_sym == :variable
107
+ markup_options = options["#{options[:language]}_options".to_sym] || []
108
+ options[:columns].each do |col|
109
+ define_method col do
110
+ if instance_variable_defined?("@#{col}")
111
+ unless send("#{col}_changed?")
112
+ return instance_variable_get("@#{col}")
113
+ end
114
+ end
115
+ # call the proc to make all 'to_html' methods return an MString instead of a String
116
+ set_markup_object.call("@#{col}",options,
117
+ klass.new(self[col].to_s, *markup_options))
118
+ end
119
+ end
120
+ else
121
+ options[:columns].each do |col|
122
+ define_method col do
123
+ if instance_variable_defined?("@#{col}")
124
+ unless send("#{col}_changed?") || send("#{options[:language_column]}_changed?")
125
+ return instance_variable_get("@#{col}")
126
+ end
127
+ end
128
+ # call the proc to make all 'to_html' methods return an MString instead of a String
129
+ set_markup_object.call("@#{col}",options, case send(options[:language_column])
130
+ when /markdown/i
131
+ markup_klasses[:markdown].new self[col].to_s, *(options[:markdown_options] || [])
132
+ when /textile/i
133
+ markup_klasses[:textile].new self[col].to_s, *(options[:textile_options] || [])
134
+ when /wikitext/i
135
+ markup_klasses[:wikitext].new self[col].to_s, *(options[:wikitext_options] || [])
136
+ when /rdoc/i
137
+ markup_klasses[:rdoc].new self[col].to_s
138
+ else
139
+ self[col]
140
+ end)
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ # This is a convenience method for
147
+ # `<tt>acts_as_markup :language => :markdown, :columns => [:body]</tt>`
148
+ # Additional options can be given at the end, if necessary.
149
+ #
150
+ def acts_as_markdown(*columns)
151
+ options = columns.extract_options!
152
+ acts_as_markup options.merge(:language => :markdown, :columns => columns)
153
+ end
154
+
155
+ # This is a convenience method for
156
+ # `<tt>acts_as_markup :language => :textile, :columns => [:body]</tt>`
157
+ # Additional options can be given at the end, if necessary.
158
+ #
159
+ def acts_as_textile(*columns)
160
+ options = columns.extract_options!
161
+ acts_as_markup options.merge(:language => :textile, :columns => columns)
162
+ end
163
+
164
+ # This is a convenience method for
165
+ # `<tt>acts_as_markup :language => :wikitext, :columns => [:body]</tt>`
166
+ # Additional options can be given at the end, if necessary.
167
+ #
168
+ def acts_as_wikitext(*columns)
169
+ options = columns.extract_options!
170
+ acts_as_markup options.merge(:language => :wikitext, :columns => columns)
171
+ end
172
+
173
+ # This is a convenience method for
174
+ # `<tt>acts_as_markup :language => :rdoc, :columns => [:body]</tt>`
175
+ # Additional options can be given at the end, if necessary.
176
+ #
177
+ def acts_as_rdoc(*columns)
178
+ options = columns.extract_options!
179
+ acts_as_markup options.merge(:language => :rdoc, :columns => columns)
180
+ end
181
+
182
+
183
+ private
184
+ def get_markdown_class
185
+ if ActsAsMarkup::MARKDOWN_LIBS.keys.include? ActsAsMarkup.markdown_library
186
+ markdown_library_names = ActsAsMarkup::MARKDOWN_LIBS[ActsAsMarkup.markdown_library]
187
+ require markdown_library_names[:lib_name]
188
+ require_extensions(markdown_library_names[:lib_name])
189
+ return markdown_library_names[:class_name].constantize
190
+ else
191
+ raise ActsAsMarkup::UnsportedMarkdownLibrary, "#{ActsAsMarkup.markdown_library} is not currently supported."
192
+ end
193
+ end
194
+
195
+ def require_extensions(library)# :nodoc:
196
+ if ActsAsMarkup::LIBRARY_EXTENSIONS.include? library.to_s
197
+ require "#{ActsAsMarkup::LIBPATH}/acts_as_markup/exts/#{library}"
198
+ end
199
+ end
200
+
201
+ def require_library_and_get_class(language)
202
+ case language
203
+ when :markdown
204
+ return get_markdown_class
205
+ when :textile
206
+ require 'redcloth'
207
+ return RedCloth
208
+ when :wikitext
209
+ require 'wikitext'
210
+ require_extensions 'wikitext'
211
+ return WikitextString
212
+ when :rdoc
213
+ require 'rdoc/markup/simple_markup'
214
+ require 'rdoc/markup/simple_markup/to_html'
215
+ require_extensions 'rdoc'
216
+ return RDocText
217
+ else
218
+ return String
219
+ end
220
+ end
221
+
222
+ # This method returns a Proc object that is called
223
+ # when acts_as_markup intercepts ActiveRecord
224
+ # while it is creating a column instance.
225
+ # The Proc creates an Eigenclass for the markup
226
+ # object. The eigenclass overrides the <tt>to_html</tt> method
227
+ # in the markup object (Rdiscount, Textile, etc.) so
228
+ # that it returns an instance of MString instead of String
229
+ # MString includes all the markup extension methods and invokes
230
+ # the ones named in <tt>:extensions =></tt>.
231
+ #
232
+ def markup_object_proc
233
+ Proc.new {|col, options, markup_object |
234
+ if options[:extensions]
235
+ singleton = markup_object.singleton_class
236
+ singleton.send(:alias_method,:old_to_html, :to_html)
237
+ singleton.send(:define_method, :to_html) {
238
+ MarkupExtensions::MString.new(old_to_html).
239
+ run_methods(options[:extensions])
240
+ }
241
+ end
242
+ instance_variable_set(col, markup_object)
243
+ }
244
+ end
245
+
246
+ end
247
+ end
248
+ end
249
+ end
250
+
251
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::AsMarkup