smartgen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/ChangeLog.md +4 -0
  2. data/Gemfile +11 -0
  3. data/Gemfile.lock +61 -0
  4. data/README.md +119 -0
  5. data/Rakefile +8 -0
  6. data/bin/smartgen +25 -0
  7. data/lib/smartgen.rb +21 -0
  8. data/lib/smartgen/configuration.rb +17 -0
  9. data/lib/smartgen/engines.rb +3 -0
  10. data/lib/smartgen/engines/base.rb +45 -0
  11. data/lib/smartgen/engines/markdown.rb +16 -0
  12. data/lib/smartgen/engines/textile.rb +16 -0
  13. data/lib/smartgen/generator.rb +104 -0
  14. data/lib/smartgen/markup_file.rb +47 -0
  15. data/lib/smartgen/object_hash.rb +41 -0
  16. data/lib/smartgen/renderers.rb +1 -0
  17. data/lib/smartgen/renderers/erb.rb +12 -0
  18. data/lib/smartgen/resource.rb +32 -0
  19. data/lib/smartgen/version.rb +4 -0
  20. data/spec/fixtures/expectations/common/another_index.html +13 -0
  21. data/spec/fixtures/expectations/common/index.html +8 -0
  22. data/spec/fixtures/expectations/common/other_index.html +13 -0
  23. data/spec/fixtures/expectations/with_layout/index.html +12 -0
  24. data/spec/fixtures/expectations/with_layout/index_with_metadata.html +43 -0
  25. data/spec/fixtures/expectations/with_layout/index_with_specific_metadata.html +44 -0
  26. data/spec/fixtures/src/assets/images/image.gif +0 -0
  27. data/spec/fixtures/src/assets/javascripts/somelib.js +2 -0
  28. data/spec/fixtures/src/assets/stylesheets/style.css +2 -0
  29. data/spec/fixtures/src/common/another_index.md +12 -0
  30. data/spec/fixtures/src/common/index.textile +10 -0
  31. data/spec/fixtures/src/common/other_index.markdown +12 -0
  32. data/spec/fixtures/src/layout.html.erb +5 -0
  33. data/spec/fixtures/src/layout_with_metadata.html.erb +22 -0
  34. data/spec/fixtures/src/layout_with_specific_metadata.html.erb +23 -0
  35. data/spec/fixtures/src/metadata.yml +43 -0
  36. data/spec/fixtures/src/with_layout/index.textile +10 -0
  37. data/spec/fixtures/src/with_layout/index_with_specific_metadata.textile +10 -0
  38. data/spec/lib/smartgen/configuration_spec.rb +30 -0
  39. data/spec/lib/smartgen/engines/base_spec.rb +71 -0
  40. data/spec/lib/smartgen/engines/markdown_spec.rb +23 -0
  41. data/spec/lib/smartgen/engines/textile_spec.rb +19 -0
  42. data/spec/lib/smartgen/generator_spec.rb +166 -0
  43. data/spec/lib/smartgen/markup_file_spec.rb +128 -0
  44. data/spec/lib/smartgen/object_hash_spec.rb +54 -0
  45. data/spec/lib/smartgen/renderers/erb_spec.rb +32 -0
  46. data/spec/lib/smartgen/resource_spec.rb +60 -0
  47. data/spec/lib/smartgen_spec.rb +18 -0
  48. data/spec/sandbox/.gitkeep +0 -0
  49. data/spec/spec_helper.rb +37 -0
  50. metadata +240 -0
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ 0.1.0
2
+ -----
3
+
4
+ * First release
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ platforms :mri_18 do
6
+ gem "ruby-debug"
7
+ end
8
+
9
+ platforms :mri_19 do
10
+ gem "ruby-debug19" if RUBY_VERSION < "1.9.3"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ smartgen (0.0.1)
5
+ RedCloth (>= 4.2.3)
6
+ activesupport (>= 2.3.5)
7
+ bluecloth (>= 2.0.9)
8
+ i18n (>= 0.5.0)
9
+ thor (>= 0.14.6)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ RedCloth (4.2.3)
15
+ activesupport (2.3.5)
16
+ archive-tar-minitar (0.5.2)
17
+ bluecloth (2.0.9)
18
+ columnize (0.3.2)
19
+ diff-lcs (1.1.2)
20
+ i18n (0.5.0)
21
+ linecache (0.43)
22
+ linecache19 (0.5.11)
23
+ ruby_core_source (>= 0.1.4)
24
+ rspec (2.3.0)
25
+ rspec-core (~> 2.3.0)
26
+ rspec-expectations (~> 2.3.0)
27
+ rspec-mocks (~> 2.3.0)
28
+ rspec-core (2.3.1)
29
+ rspec-expectations (2.3.0)
30
+ diff-lcs (~> 1.1.2)
31
+ rspec-mocks (2.3.0)
32
+ ruby-debug (0.10.4)
33
+ columnize (>= 0.1)
34
+ ruby-debug-base (~> 0.10.4.0)
35
+ ruby-debug-base (0.10.4)
36
+ linecache (>= 0.3)
37
+ ruby-debug-base19 (0.11.24)
38
+ columnize (>= 0.3.1)
39
+ linecache19 (>= 0.5.11)
40
+ ruby_core_source (>= 0.1.4)
41
+ ruby-debug19 (0.11.6)
42
+ columnize (>= 0.3.1)
43
+ linecache19 (>= 0.5.11)
44
+ ruby-debug-base19 (>= 0.11.19)
45
+ ruby_core_source (0.1.4)
46
+ archive-tar-minitar (>= 0.5.2)
47
+ thor (0.14.6)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ RedCloth (>= 4.2.3)
54
+ activesupport (>= 2.3.5)
55
+ bluecloth (>= 2.0.9)
56
+ i18n (>= 0.5.0)
57
+ rspec (>= 2.3.0)
58
+ ruby-debug
59
+ ruby-debug19
60
+ smartgen!
61
+ thor (>= 0.14.6)
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ Smartgen
2
+ ========
3
+
4
+ Smartgen generates static HTML files from markup files, using textile or markdown, and ERB to create layout templates.
5
+
6
+ ## Basic Usage
7
+
8
+ To use you must first add the gem to your Gemfile:
9
+
10
+ gem 'smartgen'
11
+
12
+ Then you can create a resource configuration:
13
+
14
+ Smartgen[:my_doc].configure do |config|
15
+ config.src_files = ['doc/**/*']
16
+ config.output_folder = 'public/docs'
17
+ end
18
+
19
+ Finally you generate it:
20
+
21
+ Smartgen[:my_doc].generate!
22
+
23
+ ## Adding a layout
24
+
25
+ By default Smartgen will only parse your textile (*.textile) or markdown (*.md or *.markdown) files, based on their extension and output it. You can give a layout file that will be used when rendering each file:
26
+
27
+ Smartgen[:my_doc].configure do |config|
28
+ config.src_files = ['doc/**/*']
29
+ config.output_folder = 'public/docs'
30
+ config.layout = 'doc/layout.html.erb'
31
+ end
32
+
33
+ This layout file is an ERB template, that has some methods available, for example this would be a basic layout:
34
+
35
+ <html>
36
+ <body>
37
+ <%= markup_file.contents %>
38
+ </body>
39
+ </html>
40
+
41
+ ## Using metadata in layout
42
+
43
+ You can specify a metadata file and use it in layouts:
44
+
45
+ Smartgen[:my_doc].configure do |config|
46
+ config.src_files = ['doc/**/*']
47
+ config.output_folder = 'public/docs'
48
+ config.layout = 'doc/layout.html.erb'
49
+ config.metadata_file = 'doc/metadata.yml'
50
+ end
51
+
52
+ This would be `doc/metadata.yml` contents
53
+
54
+ title: Common title
55
+ pages:
56
+ - &index
57
+ file: index
58
+ title: Index Page
59
+ description: Description for index page
60
+ - &some_page
61
+ file: some_page
62
+ title: Some Page
63
+ description: Description for some page
64
+ menu: [*index, *some_page]
65
+
66
+ And this could be a layout that makes use of these metadata:
67
+
68
+ <html>
69
+ <head>
70
+ <title><% metadata.current_page.title %> :: <%= metadata.title %></title>
71
+ </head>
72
+ <body>
73
+ <section id="menu">
74
+ <ul>
75
+ <% metadata.menu.each do |entry| %>
76
+ <lu><a href="<%= entry.file %>.html" title="<%= entry.description %>"><%= entry.title %></a></li>
77
+ <% end %>
78
+ </ul>
79
+ </section>
80
+
81
+ <section id="contents">
82
+ <%= markup_file.contents %>
83
+ </section>
84
+ </body>
85
+ </html>
86
+
87
+ As you can see, there is a special metadata method called `current_page`. This is created whenever you declare a pages section in your metadata file with a `file` key that matches the current file being processed. This way you can use this to put specific file metadata when rendering layouts.
88
+
89
+ ## Copying assets
90
+
91
+ When you generate documentation with Smartgen you'll often need to also copy stylesheets, javascript files and images to the output folder. You can easily do so by specifying an array of assets when configuring:
92
+
93
+ Smartgen[:my_doc].configure do |config|
94
+ config.src_files = ['doc/**/*']
95
+ config.output_folder = 'public/docs'
96
+ config.assets = ['doc/stylesheets/*.css', 'doc/javascripts/*.js', 'doc/images/*.{png|jpg|gif}']
97
+ end
98
+
99
+ As you can see you can pass an array of `Dir.glob` to either assets or src_files configuration.
100
+
101
+ ## Using different configurations
102
+
103
+ You may create as many resource configurations as you want. You create a configuration the first time you call `Smartgen[:some_config_name]`, and then, whenever you use it again it will return that configuration. You can change the configuration by calling `configure`:
104
+
105
+ Smartgen[:doc].configure do |config|
106
+ config.src_files = ['doc/**/*']
107
+ config.output_folder = 'public/docs'
108
+ config.layout = 'doc/layout.html.erb'
109
+ config.metadata_file = 'doc/metadata.yml'
110
+ end
111
+
112
+ Smartgen[:some_other_documentation].configure do |config|
113
+ config.src_files = ['other_doc/**/*']
114
+ config.output_folder = 'public/other_docs'
115
+ config.layout = 'other_layout.html.erb'
116
+ end
117
+
118
+ Smartgen[:doc].generate!
119
+ Smartgen[:some_other_documentation].generate!
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require "rspec/core/rake_task"
6
+
7
+ desc "Run all examples"
8
+ RSpec::Core::RakeTask.new(:spec)
data/bin/smartgen ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'smartgen/autorun'
5
+ rescue LoadError
6
+ $stderr.puts <<-EOS
7
+ #{'*'*50}
8
+ Could not find 'smartgen/autorun'
9
+
10
+ This may happen if you're using rubygems as your package manager, but it is not
11
+ being required through some mechanism before executing the rspec command.
12
+
13
+ You may need to do one of the following in your shell:
14
+
15
+ # for bash/zsh
16
+ export RUBYOPT=rubygems
17
+
18
+ # for csh, etc.
19
+ set RUBYOPT=rubygems
20
+
21
+ For background, please see http://gist.github.com/54177.
22
+ #{'*'*50}
23
+ EOS
24
+ exit(1)
25
+ end
data/lib/smartgen.rb ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path(File.join('smartgen', 'object_hash'), File.dirname(__FILE__))
3
+ require File.expand_path(File.join('smartgen', 'resource'), File.dirname(__FILE__))
4
+ require File.expand_path(File.join('smartgen', 'configuration'), File.dirname(__FILE__))
5
+ require File.expand_path(File.join('smartgen', 'markup_file'), File.dirname(__FILE__))
6
+ require File.expand_path(File.join('smartgen', 'renderers'), File.dirname(__FILE__))
7
+ require File.expand_path(File.join('smartgen', 'engines'), File.dirname(__FILE__))
8
+ require File.expand_path(File.join('smartgen', 'generator'), File.dirname(__FILE__))
9
+
10
+ module Smartgen
11
+ class << self
12
+ def [](name)
13
+ resources[name] ||= Smartgen::Resource.new
14
+ end
15
+
16
+ private
17
+ def resources
18
+ @resources ||= {}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Smartgen
2
+ class Configuration
3
+ attr_accessor :src_files
4
+ attr_accessor :output_folder
5
+ attr_accessor :layout
6
+ attr_accessor :assets
7
+ attr_accessor :metadata_file
8
+
9
+ def initialize
10
+ @src_files = []
11
+ @output_folder = 'tmp'
12
+ @layout = nil
13
+ @assets = []
14
+ @metadata_file = 'metadata.yml'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ require File.expand_path('engines/base', File.dirname(__FILE__))
2
+ require File.expand_path('engines/textile', File.dirname(__FILE__))
3
+ require File.expand_path('engines/markdown', File.dirname(__FILE__))
@@ -0,0 +1,45 @@
1
+ require 'active_support/core_ext/class/inheritable_attributes'
2
+ require 'active_support/core_ext/try'
3
+
4
+ module Smartgen
5
+ module Engine
6
+ class Base
7
+ class_inheritable_accessor :pre_processors
8
+
9
+ def initialize
10
+ self.pre_processors ||= []
11
+ end
12
+
13
+ def process(body)
14
+ parse(pre_process(body))
15
+ end
16
+
17
+ def supported?(extension)
18
+ extensions.include?(extension)
19
+ end
20
+
21
+ class << self
22
+ def register(processor)
23
+ self.pre_processors ||= []
24
+ self.pre_processors << processor
25
+ end
26
+ end
27
+
28
+ protected
29
+ def pre_process(body)
30
+ self.pre_processors.inject(body) do |processed_body, pre_processor|
31
+ processed_body = pre_processor.process(processed_body)
32
+ processed_body
33
+ end
34
+ end
35
+
36
+ def parse(body)
37
+ body
38
+ end
39
+
40
+ def extensions
41
+ []
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ require 'bluecloth'
2
+
3
+ module Smartgen
4
+ module Engine
5
+ class Markdown < Base
6
+ protected
7
+ def parse(body)
8
+ BlueCloth.new(body).to_html
9
+ end
10
+
11
+ def extensions
12
+ @extensions ||= ['.md', '.markdown']
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'RedCloth'
2
+
3
+ module Smartgen
4
+ module Engine
5
+ class Textile < Base
6
+ protected
7
+ def parse(body)
8
+ RedCloth.new(body).to_html
9
+ end
10
+
11
+ def extensions
12
+ @extensions ||= ['.textile']
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,104 @@
1
+ require 'thor/group'
2
+
3
+ module Smartgen
4
+ class Generator < Thor::Group
5
+ include Thor::Actions
6
+
7
+ argument :src_files, :type => :array
8
+ argument :output_folder, :type => :string
9
+
10
+ class_option :layout, :type => :string
11
+ class_option :assets, :type => :array, :default => []
12
+ class_option :metadata_file, :type => :string
13
+
14
+ attr_reader :loaded_files
15
+
16
+ def create_output_folder
17
+ destination_root = output_folder
18
+ empty_directory output_folder
19
+ end
20
+
21
+ def generate_files
22
+ markup_files.each do |markup_file|
23
+ create_file output_folder_path("#{markup_file.filename}.html"), process_file(markup_file), :force => true
24
+ end
25
+ end
26
+
27
+ def copy_assets
28
+ options[:assets].each do |dir|
29
+ self.class.source_root File.dirname(dir)
30
+ directory File.basename(dir), output_folder_path(File.basename(dir)), :force => true
31
+ end
32
+ end
33
+
34
+ class << self
35
+ def renderer
36
+ @renderer ||= Smartgen::Renderer::ERB.new
37
+ end
38
+
39
+ def renderer=(value)
40
+ @renderer = value
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def process_file(markup_file)
47
+ if has_layout?
48
+ self.class.renderer.render(layout, markup_file, metadata_for(markup_file))
49
+ else
50
+ markup_file.contents
51
+ end
52
+ end
53
+
54
+ def has_layout?
55
+ options.has_key?("layout")
56
+ end
57
+
58
+ def layout
59
+ File.read(options["layout"])
60
+ end
61
+
62
+ def metadata_for(markup_file)
63
+ metadata = load_metadata
64
+ add_current_page_data_to(metadata, markup_file)
65
+ metadata
66
+ end
67
+
68
+ def load_metadata
69
+ if options["metadata_file"]
70
+ YAML.load(File.read(options["metadata_file"]))
71
+ else
72
+ {}
73
+ end
74
+ end
75
+
76
+ def add_current_page_data_to(metadata, markup_file)
77
+ if metadata.has_key?("pages")
78
+ metadata["current_page"] = page_metadata(metadata, markup_file) || {}
79
+ end
80
+ end
81
+
82
+ def page_metadata(metadata, markup_file)
83
+ metadata["pages"].select { |page| current_page?(page, markup_file) }.first
84
+ end
85
+
86
+ def current_page?(page, markup_file)
87
+ page.has_key?("file") && page["file"] == markup_file.filename
88
+ end
89
+
90
+ def markup_files
91
+ Dir[*src_files].select { |f| supported?(File.extname(f)) }.map do |markup_filename|
92
+ MarkupFile.new markup_filename
93
+ end
94
+ end
95
+
96
+ def supported?(extension)
97
+ MarkupFile.engines.one? { |engine| engine.supported?(extension) }
98
+ end
99
+
100
+ def output_folder_path(path)
101
+ File.join(output_folder, path)
102
+ end
103
+ end
104
+ end