nanoc-oo 0.0.1

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.
Files changed (54) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +31 -0
  6. data/Rakefile +1 -0
  7. data/bin/nanoc-oo +17 -0
  8. data/cucumber.yml +8 -0
  9. data/data/nanoc-oo/wrapper/Rules +115 -0
  10. data/data/nanoc-oo/wrapper/lib/classes/1_page.rb +198 -0
  11. data/data/nanoc-oo/wrapper/lib/fake_dir.rb +27 -0
  12. data/data/nanoc-oo/wrapper/lib/fake_item.rb +39 -0
  13. data/data/nanoc-oo/wrapper/lib/fake_items.rb +22 -0
  14. data/data/nanoc-oo/wrapper/lib/filter_for.rb +14 -0
  15. data/data/nanoc-oo/wrapper/lib/item_class.rb +22 -0
  16. data/features/_related_tests.feature +32 -0
  17. data/features/composite_file.example +82 -0
  18. data/features/move_children.example +43 -0
  19. data/features/paginate_article.example +86 -0
  20. data/features/step_definitions/complex_steps.rb +23 -0
  21. data/features/step_definitions/nanoc-oo_steps.rb +9 -0
  22. data/features/step_definitions/nanoc_steps.rb +22 -0
  23. data/features/step_definitions/steps.rb +1 -0
  24. data/features/support/env.rb +25 -0
  25. data/features/support/my_extension.rb +16 -0
  26. data/features/support/nice_steps.rb +14 -0
  27. data/lib/nanoc-oo/version.rb +3 -0
  28. data/lib/nanoc-oo.rb +25 -0
  29. data/nanoc-oo.gemspec +32 -0
  30. data/old-README.md +72 -0
  31. data/spec/add_item_spec.rb +97 -0
  32. data/spec/cli/all_spec.rb +57 -0
  33. data/spec/item_children_and_comiling_spec.rb +97 -0
  34. data/spec/items_configuration_spec.rb +239 -0
  35. data/spec/layouts_spec.rb +50 -0
  36. data/spec/rspec/compiler_spec.rb +8 -0
  37. data/spec/rspec/nanoc_spec.rb +13 -0
  38. data/spec/rspec/wrapper_spec.rb +15 -0
  39. data/spec/support/0_coverage.rb +18 -0
  40. data/spec/support/1_config.rb +27 -0
  41. data/spec/support/2_nanoc.rb +27 -0
  42. data/spec/support/3_wrapper.rb +17 -0
  43. data/spec/support/4_compiler.rb +16 -0
  44. data/spec/support/5_temp_files.rb +51 -0
  45. data/spec/support/6_temp_helpers.rb +98 -0
  46. data/spec/support/all.rb +22 -0
  47. data/spec/unit/fake_dir_spec.rb +45 -0
  48. data/spec/unit/fake_item_spec.rb +62 -0
  49. data/spec/unit/fake_items_spec.rb +38 -0
  50. data/spec/unit/filter_for_spec.rb +12 -0
  51. data/spec/unit/item_class_spec.rb +42 -0
  52. data/spec/unit/page_spec.rb +96 -0
  53. data/spec/unit/temp_files_spec.rb +107 -0
  54. metadata +278 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nanoc-oo.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 TODO: Write your name
2
+
3
+ MIT License
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
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # NanocOO
2
+
3
+ TODO: Write a gem description
4
+ TODO: Write a gem description
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'nanoc-oo'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install nanoc-oo
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/nanoc-oo ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'nanoc-oo'
3
+ require 'thor/group'
4
+
5
+ class CLI < Thor::Group
6
+ argument :name, type: :string, desc: 'directory name for new site'
7
+ class_option :blank, type: :boolean, default: false, desc: 'without content files'
8
+ desc 'creates nanoc site with object-oriented wrapper'
9
+
10
+ def create_it
11
+ NanocOO.create_site name, options[:blank]
12
+ end
13
+
14
+ def self.banner; super.sub('cli ','') end
15
+ end
16
+
17
+ ARGV.empty?? CLI.usage : CLI.start
data/cucumber.yml ADDED
@@ -0,0 +1,8 @@
1
+ <%
2
+ rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
3
+ rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
4
+ std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
5
+ %>
6
+ default: <%= std_opts %> features
7
+ wip: --tags @wip:3 --wip features
8
+ rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ ###############
5
+ # Odd #
6
+ ###############
7
+
8
+ class Nanoc::Item
9
+ def +@
10
+ self[:object]
11
+ end
12
+ end
13
+
14
+ #compile('/'){ layout 'default' }; route('/'){'/index.html'}
15
+ #compile('/stylesheet/'){}; route('/stylesheet/'){'/style.css'}
16
+
17
+ @file = '../log.txt'
18
+ def log?
19
+ File.exist?(@file) ? File.read(@file) : ''
20
+ end
21
+ def log data
22
+ File.write @file, log? + "\n#{ data }"
23
+ end
24
+
25
+ module Preprocessor # one .preprocess per item!
26
+
27
+ def self.preprocess! context, classes
28
+
29
+ while item = context.items.find { |item| !(+item) || !(+item).processed? }
30
+ prepare item, classes
31
+ (+item).do_preprocess context
32
+ end
33
+
34
+ fix_children context # after preprocessing finished
35
+ end
36
+
37
+ def self.prepare item, classes
38
+ item[:object] = classes[item].new(item) unless +item
39
+ item
40
+ end
41
+ def self.fix_children context
42
+ context.items.each do |item|
43
+ (+item).fix_children context
44
+ end
45
+ end
46
+ end
47
+
48
+ module ::Builder
49
+
50
+ def self.build! item, context, items_context
51
+
52
+ unless item.is_a? FakeDir
53
+
54
+ context.compile (+item).identifier do
55
+ (+item).compile self
56
+ end
57
+
58
+ context.route (+item).identifier do
59
+ (+item).route
60
+ end
61
+ end
62
+
63
+ (+item).compile_children context, items_context
64
+ end
65
+ end
66
+
67
+ def root items, classes
68
+ items.find { |item| item.identifier == '/' } or Preprocessor.prepare Nanoc::Item.new('', { hidden: true }, '/'), classes
69
+ end
70
+
71
+
72
+ #################
73
+ # Layouts #
74
+ #################
75
+ #
76
+ # The first matching rule is used:
77
+ # layout '*', :erb
78
+ # layout '/default/', :slim
79
+ # layout '/default/', :haml, format: :html5
80
+ # layout %r{^/_.+/$}, :erb
81
+ # /default/ may be layouts/default.with-any-extension
82
+ # all unmatched layouts will be compiled using filter determined by extension:
83
+ #
84
+
85
+ FakeItems.new('layouts').items.each do |item|
86
+ layout item.identifier, FilterFor[item.file]
87
+ end
88
+
89
+
90
+ #################
91
+ # Items #
92
+ #################
93
+
94
+ fake = FakeItems.new('content')
95
+ classes = ItemClass.new
96
+
97
+
98
+ ##################
99
+ # Preprocess #
100
+ ##################
101
+
102
+ Preprocessor.preprocess! fake, classes
103
+
104
+ preprocess do
105
+ Preprocessor.preprocess! self, classes
106
+ end
107
+
108
+
109
+ ###################
110
+ # Compile/Route #
111
+ ###################
112
+ #
113
+ # FIXME "if" just for specs, to prevent crashes
114
+ #
115
+ Builder.build! root(fake.items, classes), self, fake if fake.items.count > 0
@@ -0,0 +1,198 @@
1
+ def is_binary_file? file
2
+ require 'ptools'
3
+ File.binary? file
4
+ end
5
+
6
+ # module Hidable
7
+ # def route *a
8
+ # return nil if hidden?
9
+ # super
10
+ # end
11
+
12
+ # # def compile *a
13
+ # # return nil if hidden?
14
+ # # super
15
+ # # end
16
+
17
+ # private
18
+ # def hidden?
19
+ # # item[:hidden] == true
20
+ # end
21
+ # end
22
+
23
+ module PageDefaults
24
+ def route
25
+ return nil if self.class::ROUTE == false
26
+ self.class::ROUTE or identifier.chop + (html?? '/index.html' : ".#{extension}")
27
+ end
28
+
29
+ #def item_path
30
+ # identifier[%r|(/.*/).*?/|]
31
+ #end
32
+ #def item_name
33
+ # identifier[%r|/.*(/.*?/)|]
34
+ #end
35
+
36
+ def compile_children context, items_context
37
+ children(items_context).each do |child|
38
+ ::Builder.build! child, context, items_context
39
+ end
40
+ end
41
+
42
+ def children context=nil
43
+ return @fixed_children if @fixed_children
44
+ raise 'context is needed to get children before preprocessing is finished!' unless context
45
+ direct_children context
46
+ end
47
+
48
+
49
+ def fix_children context
50
+ @fixed_children = children context
51
+ end
52
+
53
+
54
+ def direct_children context
55
+ context.items.select { |items| items.identifier =~ %r|^#{ Regexp.escape identifier }[^/]+/$| }
56
+ end
57
+
58
+ def compile context
59
+ apply_filter context
60
+ apply_layout context
61
+ after_compile context
62
+ end
63
+
64
+ def apply_filter context
65
+ context.filter filter if filter
66
+ end
67
+ def filter
68
+ return self.class::FILTER if self.class::FILTER
69
+ return @@filters[last_extension] if @@filters[last_extension]
70
+ end
71
+ def apply_layout context
72
+ context.layout layout if layout
73
+ end
74
+ def layout
75
+ self.class::LAYOUT if defined? self.class::LAYOUT
76
+ end
77
+ def after_compile context
78
+ context.filter :relativize_paths, type: kind if kind
79
+ end
80
+
81
+ def kind
82
+ case
83
+ when html? then :html
84
+ when css? then :css
85
+ end
86
+ end
87
+
88
+
89
+ def css?
90
+ really_css?
91
+ end
92
+
93
+ def really_css?
94
+ extension.split('.').find { |ext| ext =~ /^(#{ CSS_EXTENSIONS })$/ } ? true : false
95
+ end
96
+
97
+
98
+
99
+ # uses FakeItem#file or NanocItem#[:binary]
100
+ #
101
+ def binary?
102
+ item.respond_to?(:file) ? is_binary_file?(item.file) : item[:binary]
103
+ end
104
+
105
+ def html?
106
+ really_html?
107
+ end
108
+
109
+ def really_html?
110
+ extension.split('.').find { |ext| ext =~ /^(#{ HTML_EXTENSIONS })$/ } ? true : false
111
+ end
112
+
113
+ def extension
114
+ self.class::EXT or item[:extension] or ''
115
+ end
116
+
117
+ def last_extension
118
+ extension[/[.](.+?)$/, 1] or extension
119
+ end
120
+
121
+ FILTER = nil
122
+ ROUTE = nil
123
+ EXT = nil
124
+ CSS_EXTENSIONS = 'css|sass|scss'
125
+ HTML_EXTENSIONS = 'html|htm|slim|haml|md'
126
+ PRIORITY = 0
127
+ @@filters = {'md' => :kramdown, 'slim' => :slim, 'erb' => :erb}
128
+ end
129
+
130
+ class Page
131
+ include PageDefaults
132
+ # include Hidable
133
+ attr_reader :item
134
+
135
+ def initialize item
136
+ @item = item
137
+ end
138
+
139
+ def self.create context, *params
140
+ context.items << item = Nanoc::Item.new(*params)
141
+ item[:object] = self.new item
142
+ end
143
+
144
+ def preprocess context
145
+ end
146
+
147
+ def do_preprocess context
148
+ preprocess context
149
+ ensure
150
+ @processed = true
151
+ end
152
+
153
+ def processed?
154
+ @processed
155
+ end
156
+
157
+ def identifier
158
+ item.identifier
159
+ end
160
+ def identifier= value
161
+ item.identifier = value
162
+ end
163
+
164
+ def self.accept? identifier
165
+ return false unless mask = good_id
166
+ identifier =~ mask ? true : false
167
+ end
168
+
169
+ def self.good_id
170
+ id = self::GOOD_ID
171
+ case id
172
+ when String
173
+ id.gsub!('**','.+') # any level
174
+ id.gsub!('*','[^/]+') # one level
175
+ id = "/#{id}" unless id[0] == '/'
176
+ id = "#{id}/" unless id[-1] == '/'
177
+ /^#{id}$/
178
+ else
179
+ id
180
+ end
181
+ end
182
+
183
+ def representation
184
+ rep
185
+ end
186
+ def rep
187
+ :default
188
+ end
189
+ end
190
+
191
+ class UndefinedPage < Page
192
+ PRIORITY = fixnum_min = -(2**(0.size * 8 -2))
193
+ def self.accept? *a; true end
194
+
195
+ def route *a; end
196
+ def compile *a; end
197
+ def preprocess *a; end
198
+ end
@@ -0,0 +1,27 @@
1
+ class FakeDir
2
+ attr_reader :file
3
+ attr_accessor :identifier
4
+
5
+ def initialize file
6
+ @file = file
7
+ @values = {}
8
+ @identifier = get_identifier
9
+ end
10
+
11
+ def +@
12
+ @values[:object]
13
+ end
14
+ def []= key, value
15
+ @values[key] = value
16
+ end
17
+ def [] key
18
+ @values[key]
19
+ end
20
+
21
+ private
22
+ def get_identifier
23
+ dir = @file.sub(/.+?\//, '/')
24
+ dir << '/' if dir[-1] != '/'
25
+ dir
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ class FakeItem
2
+ attr_reader :file
3
+ attr_accessor :identifier
4
+
5
+ def initialize file
6
+ @file = file
7
+ @identifier = get_identifier
8
+ @values = {}
9
+ end
10
+
11
+ def binary?
12
+ require 'ptools'
13
+ File.binary?(file)
14
+ end
15
+
16
+ def raw_content
17
+ File.read file
18
+ end
19
+
20
+ def +@
21
+ @values[:object]
22
+ end
23
+ def []= key, value
24
+ @values[key] = value
25
+ end
26
+ def [] key
27
+ return extension if key == :extension
28
+ @values[key]
29
+ end
30
+
31
+ private
32
+ def get_identifier
33
+ (@file.sub(/.+?\//, '/').sub(/\.[^\/]+/, '') + '/').sub(%r[/index/$], '/')
34
+ end
35
+
36
+ def extension
37
+ @file[/\.([^\/]+)$/, 1].to_s
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ class FakeItems
2
+ attr_reader :items
3
+
4
+ def initialize where, params={nanoc:false}
5
+ @params = params
6
+ all = Dir["#{where}/**/*"]
7
+ files = all.reject { |those| File.directory? those }.map { |item| create item } # FakeItem.new item }
8
+ dirs = all.select { |those| File.directory? those }.map { |item| FakeDir.new item }
9
+ dirs.reject! { |dir| files.find { |file| file.identifier == dir.identifier } }
10
+ @items = files + dirs
11
+ end
12
+
13
+ def create item
14
+ fake = FakeItem.new item
15
+ if @params[:nanoc] && defined? Nanoc
16
+ content = fake.binary?? fake.file : File.read(fake.file)
17
+ Nanoc::Item.new content, {}, fake.identifier
18
+ else
19
+ fake
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module FilterFor
2
+ extend self
3
+ KNOWN_FILTERS = { 'html' => :erb, 'htm' => :erb }
4
+
5
+ def [] file
6
+ filter_ext File.extname file
7
+ end
8
+
9
+ private
10
+ def filter_ext ext
11
+ ext = ext.sub(/^\./,'')
12
+ KNOWN_FILTERS[ext] or ext.to_sym
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ class Class
2
+ def subclasses
3
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
4
+ end
5
+ end
6
+
7
+ class ItemClass
8
+ attr_reader :classes
9
+
10
+ def initialize
11
+ @classes = find_classes
12
+ end
13
+
14
+ def [] item
15
+ @classes.find { |that| that.accept? item.identifier }
16
+ end
17
+
18
+ private
19
+ def find_classes
20
+ Page.subclasses.sort_by { |x| x::PRIORITY }.reverse
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ Feature: related tests
2
+
3
+ Scenario Outline: nanoc is ok
4
+ When I successfully run `ruby -S nanoc <command>`
5
+ Then the output should contain "<success>"
6
+
7
+ Examples:
8
+ | command | success |
9
+ | -v | 2007 |
10
+ | cs temp | Enjoy! |
11
+
12
+
13
+ Scenario: wrapper is ok
14
+ Given I have a blank wrapped nanoc site
15
+ And a directory named "lib/classes" should exist
16
+ And a file named "lib/classes/hide_all.rb" with:
17
+ """
18
+ class HideAll < Page
19
+ GOOD_ID = // # any
20
+ def route; nil end
21
+ end
22
+ """
23
+ When I successfully compile it
24
+ Then the file "output/index.html" should not exist
25
+ Then the file "output/style.css" should not exist
26
+
27
+
28
+ Scenario: steps are ok
29
+ Given I have a default nanoc site
30
+ When I successfully compile it
31
+ Then a file named "output/index.html" should exist
32
+ Then a file named "output/style.css" should exist
@@ -0,0 +1,82 @@
1
+ Feature: composite file example
2
+
3
+ Background:
4
+ Given I have a blank wrapped nanoc site
5
+ And the folowing are input files:
6
+ """
7
+ content/style/one.sass
8
+ content/style/two.sass
9
+ content/style/three.sass
10
+ content/style/index.erb
11
+ """
12
+ And the folowing are expected output files:
13
+ """
14
+ output/style.css
15
+ """
16
+
17
+ Scenario:
18
+ Given a file named "lib/classes/style.rb" with:
19
+ """
20
+ # different items with same routes???
21
+ # INDEX = ...
22
+ # create item?
23
+ # compile_ext / route_ext ???
24
+ # EXT = 'css' filter an route
25
+ # NAME = 'style.css' # used by routing and ext ??
26
+ # INDEX = ...
27
+ # LAYOUT = ...
28
+ #%r|^/style/.| # '/style/.'
29
+ # '*/style' < 1 level deep
30
+ # '**/style' < n levels deep
31
+
32
+ #%r|^/style/.| # <<<<<<<<<<< get rid of regexp! '*/style/*'
33
+
34
+
35
+ class Style < Page
36
+ GOOD_ID = '/style/'
37
+ ROUTE = '/style.css'
38
+ end
39
+
40
+ class SubStyle < Page
41
+ GOOD_ID = 'style/**' # == '/style/**/' (boundary slashes are optional: se == /se == se/ == /se/ )
42
+ ROUTE = false # nil - default action, false - dont create output file
43
+ FILTER = :sass # or @@filters['sass'] = :sass # filter by ext
44
+ end
45
+ """
46
+ And a file named "content/style/index.erb" with:
47
+ """
48
+ <%= (+item).children.map(&:compiled_content).join "\n" %>
49
+ """
50
+ And a file named "content/style/one.sass" with:
51
+ """
52
+ html
53
+ color: red
54
+ """
55
+ And a file named "content/style/two.sass" with:
56
+ """
57
+ html
58
+ color: green
59
+ """
60
+ And a file named "content/style/three.sass" with:
61
+ """
62
+ html
63
+ color: blue
64
+ """
65
+ When I successfully compile it
66
+ Then input are the only files in "content" directory
67
+ And expected output are the only files in "output" directory
68
+ Then the file "output/style.css" should contain:
69
+ """
70
+ html {
71
+ color: red; }
72
+ """
73
+ Then the file "output/style.css" should contain:
74
+ """
75
+ html {
76
+ color: green; }
77
+ """
78
+ Then the file "output/style.css" should contain:
79
+ """
80
+ html {
81
+ color: blue; }
82
+ """