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
@@ -0,0 +1,24 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Package do
4
+
5
+ subject { Henshin::Package }
6
+
7
+ let(:compressor) { mock() }
8
+ let(:site) { test_site }
9
+ let(:package) { subject.new(site, compressor) }
10
+
11
+ describe '#text' do
12
+ it 'returns the compressed text if #enabled?' do
13
+ compressor.expects(:compress)
14
+ package.text
15
+ end
16
+
17
+ it 'returns the joined text when not #enabled?' do
18
+ package.stubs(:enabled?).returns(false)
19
+ compressor.expects(:join)
20
+ package.text
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../../helper'
2
+
3
+ describe Henshin::Package::Script do
4
+
5
+ let(:value) { Object.new }
6
+ let(:site) { stub(:config => {compress: { scripts: value }}) }
7
+
8
+ subject { Henshin::Package::Script }
9
+
10
+ describe '#enabled?' do
11
+ it 'uses value from site config' do
12
+ package = subject.new(site, [])
13
+ package.enabled?.must_equal value
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../../helper'
2
+
3
+ describe Henshin::Package::Style do
4
+
5
+ let(:value) { Object.new }
6
+ let(:site) { stub(:config => {compress: {styles: value}}) }
7
+
8
+ subject { Henshin::Package::Style }
9
+
10
+ describe '#enabled?' do
11
+ it 'uses value from site config' do
12
+ package = subject.new(site, [])
13
+ package.enabled?.must_equal value
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../helper'
2
+ require 'lib/henshin/path'
3
+
4
+ describe Henshin::Path do
5
+
6
+ subject { Henshin::Path }
7
+
8
+ let(:root) { Pathname.new('/') }
9
+ let(:path) { subject.new(root, 'tag', 'code', 'index.html') }
10
+
11
+ it 'has an #extension' do
12
+ path.extension.must_equal '.html'
13
+ end
14
+
15
+ it 'has a #permalink' do
16
+ path.permalink.must_equal '/tag/code/index.html'
17
+ end
18
+
19
+ it 'has a #url' do
20
+ path.url.must_equal '/tag/code/'
21
+ end
22
+
23
+ it 'uses strict equality for #==' do
24
+ other = Henshin::Path(root, 'tag', 'code', 'index.html')
25
+ path.must_equal other
26
+ end
27
+
28
+ describe '#===' do
29
+ it 'matches permalink' do
30
+ path.must_be :===, '/tag/code/index.html'
31
+ end
32
+
33
+ it 'matches url' do
34
+ path.must_be :===, '/tag/code/'
35
+ end
36
+
37
+ it 'matches url without trailing slash' do
38
+ path.must_be :===, '/tag/code'
39
+ end
40
+
41
+ it 'matches equal Path' do
42
+ other = subject.new(root, 'tag', 'code', 'index.html')
43
+ path.must_be :===, other
44
+ end
45
+ end
46
+
47
+ it 'can #<<' do
48
+ path = subject.new(root, 'tag', 'code')
49
+ path << 'test.html'
50
+ path.must_be :===, '/tag/code/test.html'
51
+ end
52
+
53
+ it 'defines #Path()' do
54
+ Path(root, 'test').must_equal subject.new(root, 'test')
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Publisher do
4
+
5
+ subject { Henshin::Publisher }
6
+
7
+ describe '.requires_keys' do
8
+ it 'returns true if the hash contains every key' do
9
+ hash = {a: 1, b: 2, c: 3}
10
+ list = [:a, :b]
11
+ subject.requires_keys(hash, list).must_equal true
12
+ end
13
+ end
14
+
15
+ describe '.get_required_opt' do
16
+ it 'returns the option' do
17
+ subject.get_required_opt({:a => 1}, :a).must_equal 1
18
+ end
19
+
20
+ it 'fails loudly' do
21
+ Henshin::UI.expects(:fail).with("Must give :a option to publish.")
22
+ subject.get_required_opt({}, :a)
23
+ end
24
+ end
25
+
26
+ describe '.get_password' do
27
+ it 'returns the password from the hash' do
28
+ subject.get_password({:pass => 'a'}, :pass).must_equal 'a'
29
+ end
30
+
31
+ it 'executes the command given' do
32
+ subject.get_password({:pass => '$sh echo "hello"'}, :pass).
33
+ must_equal 'hello'
34
+ end
35
+
36
+ it 'prompts for password' do
37
+ m = mock { expects(:ask) }
38
+ HighLine.expects(:new).returns(m)
39
+
40
+ subject.get_password({}, :pass)
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,21 @@
1
+ require_relative '../../helper'
2
+
3
+ describe Henshin::Publisher::Sftp do
4
+
5
+ subject { Henshin::Publisher::Sftp }
6
+
7
+ describe '#write' do
8
+
9
+ let(:path) { Pathname.new('cool/file.txt') }
10
+ let(:file) { mock.tap {|m| m.expects(:open).with("/#{path}", 'w') } }
11
+ let(:conn) { stub(:file => file, :lstat! => false) }
12
+
13
+ it 'writes the contents to the path' do
14
+ with_writing! do
15
+ sftp = subject.new(conn, Pathname.new('/'))
16
+ sftp.write path, 'Hey'
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,55 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Reader do
4
+
5
+ subject { Henshin::Reader.new(Pathname.new('site').realpath) }
6
+
7
+ def path(path)
8
+ Pathname.new('site').realpath + path
9
+ end
10
+
11
+ def paths(*path)
12
+ path.map {|i| path(i) }
13
+ end
14
+
15
+
16
+ describe '#ignore' do
17
+ it 'adds files to the ignored list' do
18
+ subject.ignore 'crazy-file.txt', 'another?.md'
19
+ subject.must_be :ignore?, path('crazy-file.txt')
20
+ end
21
+ end
22
+
23
+ describe '#ignore?' do
24
+ it 'checks if a file is in the ignored list' do
25
+ subject.must_be :ignore?, path('config.yml')
26
+ end
27
+
28
+ it 'checks if the file begins with an underscore' do
29
+ subject.must_be :ignore?, path('folder/folder/_file.txt')
30
+ end
31
+
32
+ it 'checks if a folder above begins with an underscore' do
33
+ subject.must_be :ignore?, path('folder/_folder/file.txt')
34
+ end
35
+ end
36
+
37
+ describe '#read' do
38
+ it 'finds all paths in path given' do
39
+ subject.read('**', '*.sass').must_equal paths('assets/styles/screen.sass')
40
+ end
41
+ end
42
+
43
+ describe '#read_all' do
44
+ it 'finds all paths under path given' do
45
+ subject.read_all('assets', 'styles').must_equal paths('assets/styles/screen.sass')
46
+ end
47
+ end
48
+
49
+ describe '#safe_paths' do
50
+ it 'finds all paths, excepts from reserved directories' do
51
+ subject.safe_paths.wont_include path('assets/styles/screen.sass')
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Safety do
4
+
5
+ let(:klass) {
6
+ Class.new {
7
+ include Henshin::Safety
8
+
9
+ def unsafe_method; "Hey, I was called!"; end
10
+ unsafe :unsafe_method
11
+
12
+ def another(a, b, c); "Yes, called again"; end
13
+ unsafe :another
14
+
15
+ def safe_method; "Ok here"; end
16
+ }
17
+ }
18
+
19
+ subject { klass.new }
20
+
21
+ describe 'a normally created object including Safety' do
22
+ it 'can call marked methods normally' do
23
+ subject.unsafe_method.must_equal "Hey, I was called!"
24
+ end
25
+ end
26
+
27
+ describe '#safe' do
28
+ let(:unsafe) { subject }
29
+ let(:safe) { unsafe.safe }
30
+
31
+ it 'returns a copy of the object' do
32
+ safe.wont_equal unsafe
33
+ safe.must_be_instance_of unsafe.class
34
+ end
35
+
36
+ it 'with the unsafe methods stubbed' do
37
+ safe.unsafe_method.must_equal nil
38
+ end
39
+
40
+ it 'works for methods expecting arguments' do
41
+ safe.another(1, 2, 3).must_equal nil
42
+ end
43
+
44
+ it 'unmarked methods still work' do
45
+ safe.safe_method.must_equal "Ok here"
46
+ end
47
+
48
+ it 'always returns the same safe clone' do
49
+ a = unsafe.safe
50
+ b = unsafe.safe
51
+ c = unsafe.safe
52
+
53
+ a.must_equal b
54
+ b.must_equal c
55
+ end
56
+ end
57
+
58
+ it 'infects inherited classes' do
59
+ subklass = Class.new(klass)
60
+ subsubject = subklass.new
61
+
62
+ subsubject.unsafe_method.must_equal "Hey, I was called!"
63
+ subsubject.safe.unsafe_method.must_equal nil
64
+ end
65
+
66
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Scope do
4
+
5
+ subject { Henshin::Scope }
6
+
7
+ describe '#initialize' do
8
+ it 'creates a method for each key-value' do
9
+ scope = subject.new a: 1, b: 2
10
+ scope.a.must_equal 1
11
+ scope.b.must_equal 2
12
+ end
13
+
14
+ it 'works recursively on hashes' do
15
+ scope = subject.new a: {b: {c: 3}}
16
+ scope.a.b.c.must_equal 3
17
+ end
18
+
19
+ it 'works on arrays of hashes' do
20
+ scope = subject.new a: [{b: 2}, {c: {d: 4}}]
21
+ scope.a[0].b.must_equal 2
22
+ scope.a[1].c.d.must_equal 4
23
+ end
24
+ end
25
+
26
+ describe '#method_missing' do
27
+ it 'returns nil for unknowns' do
28
+ scope = subject.new bye: 1
29
+ scope.hello.must_equal nil
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,142 @@
1
+ require_relative '../helper'
2
+
3
+ describe Henshin::Site do
4
+
5
+ subject { Henshin::Site }
6
+ let(:site) { test_site }
7
+
8
+ describe '.file' do
9
+ it 'adds the file to the list' do
10
+ klass = subject.dup
11
+ klass.file :a, :b
12
+ klass.file_list.must_include :a
13
+ klass.file_list.must_include :b
14
+ end
15
+ end
16
+
17
+ describe '.files' do
18
+ it 'adds the files to the list' do
19
+ klass = subject.dup
20
+ klass.files :a, :b
21
+ klass.files_list.must_include :a
22
+ klass.files_list.must_include :b
23
+ end
24
+ end
25
+
26
+ describe '#initialize' do
27
+ let(:klass) {
28
+ k = subject.dup
29
+ k.any_instance.stubs(:config).returns({:ignore => 'a'})
30
+ k
31
+ }
32
+
33
+ it 'creates a reader' do
34
+ obj = klass.new(test_path)
35
+ obj.instance_variable_get(:@reader).must_be_kind_of Henshin::Reader
36
+ end
37
+
38
+ it 'sets the source' do
39
+ obj = klass.new(test_path.to_s)
40
+ obj.instance_variable_get(:@source).must_equal test_path
41
+ end
42
+
43
+ it 'sets ignored paths' do
44
+ obj = klass.new(test_path)
45
+ obj.instance_variable_get(:@reader).ignore?(test_path + 'a').must_equal true
46
+ end
47
+ end
48
+
49
+ describe '#dest' do
50
+ it 'returns the build path' do
51
+ site.dest.must_equal (Pathname.pwd + 'site' + 'build')
52
+ end
53
+
54
+ it 'uses the destination set in the config' do
55
+ site.stubs(:config).returns({:build => 'different'})
56
+ site.dest.must_equal (Pathname.pwd + 'site' + 'build')
57
+ end
58
+ end
59
+
60
+ describe '#root' do
61
+ it 'returns the url root' do
62
+ site.root.must_equal Pathname.new('/')
63
+ end
64
+ end
65
+
66
+ describe '#config' do
67
+ it 'combines the loaded config.yml with the defaults' do
68
+ site.config.author.must_equal "You"
69
+ site.config.md.superscript.must_equal true
70
+ end
71
+
72
+ it 'is a Hashie::Mash' do
73
+ site.config.must_be_kind_of Hashie::Mash
74
+ end
75
+ end
76
+
77
+ describe '#script' do
78
+ it 'returns the script package' do
79
+ site.script.must_be_kind_of Henshin::Package::Script
80
+ end
81
+ end
82
+
83
+ describe '#style' do
84
+ it 'returns the style package' do
85
+ site.style.must_be_kind_of Henshin::Package::Style
86
+ end
87
+ end
88
+
89
+ describe '#posts' do
90
+ it 'returns the posts' do
91
+ site.posts.all? {|p| p.kind_of?(Henshin::File::Post) }.must_equal true
92
+ end
93
+ end
94
+
95
+ describe '#files' do
96
+ it 'returns all other files' do
97
+ site.files.map(&:permalink).must_include '/index.html', '/feed.xml'
98
+ end
99
+ end
100
+
101
+ describe '#all_files' do
102
+ it 'returns all files' do
103
+ site.all_files.sort.must_equal [site.style, site.script, site.posts, site.files].flatten.sort
104
+ end
105
+ end
106
+
107
+ describe '#templates' do
108
+ it 'returns all templates' do
109
+ site.templates.map(&:name).must_include 'default', 'post'
110
+ end
111
+ end
112
+
113
+ describe '#method_missing' do
114
+ it 'gets the value from the yaml' do
115
+ site.author.must_equal "You"
116
+ end
117
+ end
118
+
119
+ describe '#write' do
120
+ it 'writes all the files' do
121
+ writer = Object.new
122
+ a, b = mock(:write => writer), mock(:write => writer)
123
+ site.expects(:all_files).returns([a, b])
124
+
125
+ site.write(writer)
126
+ end
127
+ end
128
+
129
+ describe '#template' do
130
+ it 'finds the template' do
131
+ default = site.template('default')
132
+ default.must_be_kind_of Henshin::File::Template
133
+ default.name.must_equal 'default'
134
+ end
135
+
136
+ it 'returns the EmptyTemplate if not found' do
137
+ temp = site.template('non-existent')
138
+ temp.must_be_kind_of Henshin::File::EmptyTemplate
139
+ end
140
+ end
141
+
142
+ end