henshin 0.4.2 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/LICENCE +18 -0
  2. data/README.md +133 -0
  3. data/Rakefile +6 -51
  4. data/bin/henshin +127 -134
  5. data/lib/henshin.rb +163 -38
  6. data/lib/henshin/compressor.rb +31 -0
  7. data/lib/henshin/compressors/css.rb +20 -0
  8. data/lib/henshin/compressors/js.rb +20 -0
  9. data/lib/henshin/core_ext.rb +69 -0
  10. data/lib/henshin/error.rb +28 -0
  11. data/lib/henshin/file.rb +67 -0
  12. data/lib/henshin/files/abstract.rb +102 -0
  13. data/lib/henshin/files/attributes.rb +31 -0
  14. data/lib/henshin/files/empty_template.rb +24 -0
  15. data/lib/henshin/files/physical.rb +117 -0
  16. data/lib/henshin/files/post.rb +64 -0
  17. data/lib/henshin/files/templatable.rb +29 -0
  18. data/lib/henshin/files/template.rb +45 -0
  19. data/lib/henshin/files/tilt.rb +50 -0
  20. data/lib/henshin/files/tilt_template.rb +45 -0
  21. data/lib/henshin/package.rb +27 -0
  22. data/lib/henshin/packages/script.rb +23 -0
  23. data/lib/henshin/packages/style.rb +20 -0
  24. data/lib/henshin/path.rb +143 -0
  25. data/lib/henshin/publisher.rb +42 -0
  26. data/lib/henshin/publishers/sftp.rb +79 -0
  27. data/lib/henshin/reader.rb +68 -0
  28. data/lib/henshin/safety.rb +74 -0
  29. data/lib/henshin/scope.rb +55 -0
  30. data/lib/henshin/site.rb +291 -228
  31. data/lib/henshin/ui.rb +35 -0
  32. data/lib/henshin/version.rb +3 -0
  33. data/lib/henshin/writer.rb +43 -0
  34. data/lib/rack/henshin.rb +113 -0
  35. data/site/assets/scripts/config.js +11 -0
  36. data/site/assets/styles/_mixins.sass +16 -0
  37. data/site/assets/styles/screen.sass +198 -0
  38. data/site/config.yml +13 -0
  39. data/site/drafts/a-work-in-progress.md +5 -0
  40. data/site/feed.xml.slim +19 -0
  41. data/site/index.html.slim +28 -0
  42. data/site/init.rb +33 -0
  43. data/site/posts/1-hello-world.md +46 -0
  44. data/site/posts/2-code-testing.md +36 -0
  45. data/site/posts/3-style-test.md +80 -0
  46. data/site/templates/default.slim +11 -0
  47. data/site/templates/post.slim +30 -0
  48. data/site/test.html.md +7 -0
  49. data/spec/helper.rb +70 -0
  50. data/spec/henshin/compressor_spec.rb +25 -0
  51. data/spec/henshin/compressors/css_spec.rb +22 -0
  52. data/spec/henshin/compressors/js_spec.rb +22 -0
  53. data/spec/henshin/core_ext_spec.rb +59 -0
  54. data/spec/henshin/error_spec.rb +22 -0
  55. data/spec/henshin/file_spec.rb +28 -0
  56. data/spec/henshin/files/abstract_spec.rb +98 -0
  57. data/spec/henshin/files/attributes_spec.rb +25 -0
  58. data/spec/henshin/files/empty_template_spec.rb +11 -0
  59. data/spec/henshin/files/physical_spec.rb +55 -0
  60. data/spec/henshin/files/post_spec.rb +66 -0
  61. data/spec/henshin/files/template_spec.rb +53 -0
  62. data/spec/henshin/files/tilt_spec.rb +59 -0
  63. data/spec/henshin/package_spec.rb +24 -0
  64. data/spec/henshin/packages/script_spec.rb +17 -0
  65. data/spec/henshin/packages/style_spec.rb +17 -0
  66. data/spec/henshin/path_spec.rb +56 -0
  67. data/spec/henshin/publisher_spec.rb +44 -0
  68. data/spec/henshin/publishers/sftp_spec.rb +21 -0
  69. data/spec/henshin/reader_spec.rb +55 -0
  70. data/spec/henshin/safety_spec.rb +66 -0
  71. data/spec/henshin/scope_spec.rb +33 -0
  72. data/spec/henshin/site_spec.rb +142 -0
  73. data/spec/henshin/ui_spec.rb +26 -0
  74. data/spec/henshin/writer_spec.rb +26 -0
  75. metadata +352 -197
  76. data/.gitignore +0 -27
  77. data/LICENSE +0 -20
  78. data/README.markdown +0 -35
  79. data/VERSION +0 -1
  80. data/henshin.gemspec +0 -121
  81. data/lib/henshin/archive.rb +0 -133
  82. data/lib/henshin/exec/files.rb +0 -46
  83. data/lib/henshin/ext.rb +0 -55
  84. data/lib/henshin/gen.rb +0 -154
  85. data/lib/henshin/labels.rb +0 -144
  86. data/lib/henshin/plugin.rb +0 -100
  87. data/lib/henshin/plugins/highlight.rb +0 -24
  88. data/lib/henshin/plugins/liquid.rb +0 -61
  89. data/lib/henshin/plugins/maruku.rb +0 -18
  90. data/lib/henshin/plugins/sass.rb +0 -24
  91. data/lib/henshin/plugins/textile.rb +0 -18
  92. data/lib/henshin/post.rb +0 -156
  93. data/lib/henshin/static.rb +0 -33
  94. data/test/helper.rb +0 -44
  95. data/test/site/css/_reset.sass +0 -34
  96. data/test/site/css/print.css +0 -12
  97. data/test/site/css/screen.sass +0 -70
  98. data/test/site/includes/head.html +0 -1
  99. data/test/site/index.html +0 -23
  100. data/test/site/layouts/archive_date.html +0 -20
  101. data/test/site/layouts/archive_month.html +0 -24
  102. data/test/site/layouts/archive_year.html +0 -26
  103. data/test/site/layouts/category_index.html +0 -27
  104. data/test/site/layouts/category_page.html +0 -20
  105. data/test/site/layouts/main.html +0 -13
  106. data/test/site/layouts/post.html +0 -36
  107. data/test/site/layouts/tag_index.html +0 -27
  108. data/test/site/layouts/tag_page.html +0 -20
  109. data/test/site/options.yaml +0 -17
  110. data/test/site/plugins/test.rb +0 -3
  111. data/test/site/posts/Testing-Stuff.markdown +0 -14
  112. data/test/site/posts/Textile-Test.textile +0 -7
  113. data/test/site/posts/cat/test.markdown +0 -6
  114. data/test/site/posts/lorem-ipsum.markdown +0 -7
  115. data/test/site/posts/same-date.markdown +0 -7
  116. data/test/site/static.html +0 -19
  117. data/test/suite.rb +0 -4
  118. data/test/test_gen.rb +0 -98
  119. data/test/test_options.rb +0 -73
  120. data/test/test_post.rb +0 -67
  121. data/test/test_site.rb +0 -197
  122. data/test/test_static.rb +0 -13
@@ -1,154 +0,0 @@
1
- module Henshin
2
-
3
- # This is the main class for files which need to be rendered with plugins
4
- class Gen
5
-
6
- attr_accessor :path, :data, :content, :site, :to_inject, :generators
7
-
8
- # Creates a new instance of Gen
9
- #
10
- # @param [Pathname] path to the file
11
- # @param [Site] the site the gen belongs to
12
- # @param [Hash] an optional payload to add when rendered
13
- def initialize(path, site, to_inject=nil)
14
- @path = path
15
- @site = site
16
- @data = {}
17
- @content = ''
18
- @to_inject = to_inject
19
- @generators = []
20
-
21
- @data['input'] = @path.extension
22
- end
23
-
24
-
25
- ##
26
- # Reads the file if it exists, if not gets generators and layout, then cleans up @data
27
- def read
28
- self.read_file if @path.exist?
29
- self.get_generators
30
- self.get_layout
31
-
32
- # tidy up data
33
- @data['output'] ||= @data['input']
34
- self
35
- end
36
-
37
- # Opens the file and reads the yaml frontmatter if any exists, and
38
- # also gets the contents of the file.
39
- def read_file
40
- file = @path.read
41
-
42
- if file =~ /^(---\s*\n.*?\n?^---\s*$\n?)/m
43
- override = YAML.load_file(@path)
44
- @data = @data.merge(override)
45
- @content = file[$1.size..-1]
46
- else
47
- @content = file
48
- end
49
- end
50
-
51
- # Finds the correct plugins to render this gen and sets output
52
- def get_generators
53
- @site.plugins[:generators].each do |k, v|
54
- if k == @data['input'] || k == '*'
55
- @generators << v
56
- @data['output'] ||= v.extensions[:output]
57
- @data['ignore_layout'] ||= (v.config['ignore_layouts'] ? true : false)
58
- end
59
- end
60
- @generators.sort!
61
- end
62
-
63
- # Gets the correct layout for the gen, or the default if none exists.
64
- # It gets the default layout from options.yaml or looks for one called
65
- # 'main' or 'default'.
66
- def get_layout
67
- if @data['layout']
68
- @data['layout'] = site.layouts[ @data['layout'] ]
69
- else
70
- # get default layout
71
- @data['layout'] = site.layouts[(site.config['layout'] || 'main' || 'default')]
72
- end
73
- end
74
-
75
- ##
76
- # Renders the files content using the generators from #get_generators and all layout parsers.
77
- # Passed through layout parser twice so that markup in the gen is processed.
78
- def render
79
- @generators.each do |plugin|
80
- @content = plugin.generate(@content)
81
- end
82
-
83
- unless @data['ignore_layout'] || @data['layout'].nil?
84
- @site.plugins[:layoutors].each do |plugin|
85
- @content = plugin.generate(@data['layout'], self.payload)
86
- @content = plugin.generate(@content, self.payload)
87
- end
88
- end
89
-
90
- end
91
-
92
- # Creates the data to be sent to the layout engine. Adds optional data if available.
93
- #
94
- # @return [Hash] the payload for the layout engine
95
- def payload
96
- hash = {
97
- 'yield' => @content,
98
- 'gen' => self.to_hash,
99
- 'site' => @site.payload['site']
100
- }
101
- hash[ @to_inject[:name] ] = @to_inject[:payload] if @to_inject
102
- hash
103
- end
104
-
105
- # Turns all of the gens data into a hash.
106
- #
107
- # @return [Hash]
108
- def to_hash
109
- @data['content'] = @content
110
- @data['url'] = self.url
111
- @data['permalink'] = self.permalink
112
- @data
113
- end
114
-
115
-
116
- ##
117
- # Writes the file to the correct place
118
- def write
119
- FileUtils.mkdir_p(self.write_path.dirname)
120
- file = File.new(self.write_path, "w")
121
- file.puts(@content)
122
- end
123
-
124
- # @return [String] the permalink of the gen
125
- def permalink
126
- rel = @path.relative_path_from(@site.root).to_s
127
- rel.gsub!(".#{@data['input']}", ".#{@data['output']}")
128
- File.join(@site.base, rel)
129
- end
130
-
131
- # @return [String] the pretty url for the gen
132
- def url
133
- if @site.config['permalink'].include?("/index.html") && @data['output'] == 'html'
134
- self.permalink.to_p.dirname.to_s
135
- else
136
- self.permalink
137
- end
138
- end
139
-
140
- # @return [Pathname] path to write the file to
141
- def write_path
142
- @site.target + self.permalink[1..-1]
143
- end
144
-
145
- # Sorts gens based on permalink only
146
- def <=>( other )
147
- self.permalink <=> other.permalink
148
- end
149
-
150
- def inspect
151
- "#<Gen:#{@path}>"
152
- end
153
- end
154
- end
@@ -1,144 +0,0 @@
1
- module Henshin
2
-
3
- # This is basically a front for tags and categories, because they are so similar
4
- # it makes sense to condense them into one class!
5
- #
6
- class Labels < Array
7
-
8
- attr_accessor :base, :site
9
-
10
- # Creates a new instance of labels
11
- #
12
- # @param [String] base the base part of the urls, eg. category, tag
13
- # @param [Site] site that the labels belong to
14
- def initialize(base, site)
15
- @base = base
16
- @site = site
17
- end
18
-
19
- # Adds the given post to the correct category object in the array
20
- # or creates the category and adds the post to that
21
- #
22
- # @param [Post] post to be added
23
- # @param [String, Array] k label(s) to be added to
24
- #
25
- # @todo Make it a bit more abstract, actually hard coding stuff in will
26
- # lead to problems!
27
- def <<(post)
28
- k = nil
29
- if base == 'tag'
30
- k = post.data['tags']
31
- elsif base == 'category'
32
- k = [post.data['category']]
33
- end
34
-
35
- k.each do |j|
36
- unless self.map{|i| i.name}.include?(j)
37
- super Henshin::Label.new(j, @base, @site)
38
- end
39
- i = self.find_index {|i| i.name == j}
40
- self[i].posts << post
41
- end
42
- end
43
-
44
- # Converts the labels to a hash for use in a layout parser
45
- def to_hash
46
- r = []
47
- self.each do |i|
48
- r << i.to_hash
49
- end
50
- r
51
- end
52
-
53
- # @return [String] permalink for label index
54
- def permalink
55
- File.join(@site.base, @base, "index.html")
56
- end
57
-
58
- # @return [String] base url for label
59
- def url
60
- File.join(@site.base, @base)
61
- end
62
-
63
- # Need a fake path where the file would have been so as to
64
- # trick the gen into constructing the correct paths
65
- #
66
- # @return [Pathname] the path for the gen
67
- def fake_write_path
68
- @site.root + self.permalink[1..-1]
69
- end
70
-
71
- # Writes the category index, then writes the individual
72
- # category pages
73
- def write
74
- if @site.layouts["#{@base}_index"]
75
- page = Gen.new(self.fake_write_path, @site)
76
- page.read
77
- page.data['layout'] = @site.layouts["#{@base}_index"]
78
-
79
- page.render
80
- page.write
81
- end
82
- if @site.layouts["#{@base}_page"]
83
- self.each {|label| label.write }
84
- end
85
- end
86
-
87
- end
88
-
89
- class Label
90
- attr_accessor :name, :posts, :site
91
-
92
- # Creates a new instance of label
93
- #
94
- # @param [String] name of the label
95
- # @param [String] base of the url for the label (see Labels#initialize)
96
- # @param [Site] site that the label belongs to
97
- def initialize(name, base, site)
98
- @name = name
99
- @base = base
100
- @site = site
101
- @posts = []
102
- end
103
-
104
- # Converts the label to a hash
105
- def to_hash
106
- hash = {
107
- 'name' => @name,
108
- 'posts' => @posts.sort.collect {|i| i.to_hash},
109
- 'url' => self.url
110
- }
111
- end
112
-
113
- # @return [String] permalink for the label
114
- def permalink
115
- File.join(@site.base, @base, "#{@name.slugify}/index.html")
116
- end
117
-
118
- # @return [String] url for the label
119
- def url
120
- File.join(@site.base, @base, "#{@name.slugify}")
121
- end
122
-
123
- # @see Labels#fake_write_path
124
- def fake_write_path
125
- @site.root + self.permalink[1..-1]
126
- end
127
-
128
- # Writes the label page
129
- def write
130
- payload = {:name => @base, :payload => self.to_hash}
131
- page = Gen.new(self.fake_write_path, @site, payload)
132
- page.read
133
- page.data['layout'] = @site.layouts["#{@base}_page"]
134
-
135
- page.render
136
- page.write
137
- end
138
-
139
- def inspect
140
- "#<Label:#{@base}/#{@name}>"
141
- end
142
- end
143
-
144
- end
@@ -1,100 +0,0 @@
1
- module Henshin
2
-
3
- class Plugin
4
-
5
- # @return [Hash{:input, :output => Array, String}]
6
- # the file extensions that can be read by the plugin and the extension
7
- # of the output
8
- #
9
- # @example
10
- #
11
- # @extensions = {:input => ['md', 'markdown'],
12
- # :output => 'html'}
13
- attr_accessor :extensions
14
-
15
- # @return [Hash{Symbol => Object}]
16
- # the config for the plugin
17
- attr_accessor :config
18
-
19
- # @return [Integer]
20
- # The plugins are sorted on priority, high priority plugins are called first.
21
- # You could really use any number, but stick to 1 to 5.
22
- attr_accessor :priority
23
-
24
- # Create a new instance of Plugin
25
- #
26
- # @param [Site] site that the plugin belongs to
27
- def initialize(site)
28
- @extensions = {:input => [],
29
- :output => ''}
30
- @config = {}
31
- @priority = 3
32
- end
33
-
34
- # Finds all classes that subclass this particular class
35
- #
36
- # @return [Array] an array of class objects
37
- # @see http://www.ruby-forum.com/topic/163430
38
- # modified from the answer given on ruby-forum by black eyes
39
- def self.subclasses
40
- r = Henshin.constants.find_all do |c_klass|
41
- if (c_klass != c_klass.upcase) && (Henshin.const_get(c_klass).is_a?(Class))
42
- self > Henshin.const_get(c_klass)
43
- else
44
- nil
45
- end
46
- end
47
- r.collect {|k| Henshin.const_get(k)}
48
- end
49
-
50
- # Plugins are sorted by priority
51
- def <=>(other)
52
- self.priority <=> other.priority
53
- end
54
-
55
- end
56
-
57
- # Generator is the plugin type for processing things like markdown
58
- #
59
- # @example
60
- #
61
- # class MyMarkupPlugin < Henshin::Generator
62
- # def generate(content)
63
- # MyMarkup.do_stuff(content)
64
- # end
65
- # end
66
- #
67
- class Generator < Plugin
68
-
69
- # This is the method that is called when rendering content
70
- #
71
- # @param [String] content to be rendered
72
- # @return [String]
73
- def generate( content )
74
- end
75
-
76
- end
77
-
78
- # Layoutor is the plugin type for things like liquid
79
- #
80
- # @example
81
- #
82
- # class MyLayoutPlugin < Henshin::Layoutor
83
- # def generate(content, data)
84
- # MyLayout.do_stuff(content).render(data)
85
- # end
86
- # end
87
- #
88
- class Layoutor < Plugin
89
-
90
- # This is the method called when rendering content
91
- #
92
- # @param [String] content to be rendered
93
- # @param [Hash] data to be put into the content
94
- # @return [String]
95
- def generate( content, data )
96
- end
97
-
98
- end
99
-
100
- end
@@ -1,24 +0,0 @@
1
- require 'simplabs/highlight'
2
-
3
- module Henshin
4
- class HighlightPlugin < Generator
5
-
6
- def initialize(site)
7
- @extensions = {:input => ['*']}
8
- @config = {}
9
- @priority = 1
10
- end
11
-
12
- def generate( content )
13
- content =~ /(\$highlight)\s+(.+)((\n.*)+)(\$end)/
14
- if $1
15
- lang = $2.to_sym
16
- code = $3[1..-1] # removes first new line
17
- insert = '<pre><code>' + Simplabs::Highlight.highlight(lang, code) + '</code></pre>'
18
- content.gsub(/(\$highlight.*\$end)/m, insert)
19
- else
20
- content
21
- end
22
- end
23
- end
24
- end
@@ -1,61 +0,0 @@
1
- require 'liquid'
2
-
3
- module Henshin
4
- class LiquidPlugin < Layoutor
5
-
6
- def initialize(site)
7
- @config = {}
8
-
9
- if site.config['liquid']
10
- @config = site.config['liquid']
11
- @config['include_dir'] = File.join(site.root, @config['include_dir'])
12
- end
13
- end
14
-
15
- def generate( content, data )
16
- reg = {:include_dir => @config['include_dir']}
17
- Liquid::Template.parse(content).render(data, :registers => reg)
18
- end
19
-
20
- module Filters
21
- def date_to_string(dt)
22
- dt.strftime "%d %b %Y"
23
- end
24
-
25
- def date_to_long(dt)
26
- dt.strftime "%d %B %Y at %H:%M"
27
- end
28
-
29
- def time_to_string(dt)
30
- dt.strtime "%H:%M"
31
- end
32
-
33
- def titlecase(str)
34
- str.upcase
35
- end
36
-
37
- def escape(str)
38
- CGI::escape str
39
- end
40
-
41
- def escape_html(str)
42
- CGI::escapeHTML str
43
- end
44
- end
45
- Liquid::Template.register_filter(Filters)
46
-
47
- class Include < Liquid::Tag
48
- def initialize(tag_name, file, tokens)
49
- super
50
- @file = file.strip
51
- end
52
-
53
- def render(context)
54
- include = File.join(context.registers[:include_dir], @file)
55
- File.open(include, 'r') {|f| f.read}
56
- end
57
- end
58
- Liquid::Template.register_tag('include', Include)
59
-
60
- end
61
- end