jekyll-lilypond 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +99 -0
  5. data/LICENSE +21 -0
  6. data/README.md +98 -0
  7. data/files/rite.png +0 -0
  8. data/jekyll-lilypond.gemspec +23 -0
  9. data/lib/jekyll-lilypond/file_processor.rb +67 -0
  10. data/lib/jekyll-lilypond/lilypond_tag.rb +34 -0
  11. data/lib/jekyll-lilypond/tag.rb +17 -0
  12. data/lib/jekyll-lilypond/tag_processor.rb +56 -0
  13. data/lib/jekyll-lilypond/templates/basic.ly +45 -0
  14. data/lib/jekyll-lilypond/templates/empty.ly +1 -0
  15. data/lib/jekyll-lilypond/templates/figure.html +25 -0
  16. data/lib/jekyll-lilypond/templates/img.html +4 -0
  17. data/lib/jekyll-lilypond/templates/raw.html +1 -0
  18. data/lib/jekyll-lilypond/templates/raw.ly +1 -0
  19. data/lib/jekyll-lilypond/templates/showsource.html +36 -0
  20. data/lib/jekyll-lilypond/templates.rb +60 -0
  21. data/lib/jekyll-lilypond/version.rb +5 -0
  22. data/lib/jekyll-lilypond.rb +11 -0
  23. data/spec/file_processor_spec.rb +167 -0
  24. data/spec/fixtures/.gitignore +5 -0
  25. data/spec/fixtures/404.html +25 -0
  26. data/spec/fixtures/Gemfile +31 -0
  27. data/spec/fixtures/_config.yml +56 -0
  28. data/spec/fixtures/_layouts/basic_html.html +1 -0
  29. data/spec/fixtures/_layouts/vacuous_html.html +1 -0
  30. data/spec/fixtures/_layouts/vacuous_ly.ly +1 -0
  31. data/spec/fixtures/_layouts/variables_html.html +1 -0
  32. data/spec/fixtures/_layouts/variables_ly.ly +1 -0
  33. data/spec/fixtures/_posts/2021-01-16-welcome-to-jekyll.markdown +29 -0
  34. data/spec/fixtures/about.markdown +18 -0
  35. data/spec/fixtures/index.markdown +6 -0
  36. data/spec/fixtures/page.md +8 -0
  37. data/spec/integration_spec.rb +115 -0
  38. data/spec/spec_helper.rb +88 -0
  39. data/spec/tag_processor_spec.rb +135 -0
  40. data/spec/tag_spec.rb +51 -0
  41. data/spec/templates_spec.rb +175 -0
  42. metadata +118 -0
@@ -0,0 +1,135 @@
1
+ require "jekyll-lilypond"
2
+ require "spec_helper"
3
+ require "tmpdir"
4
+ include Liquid
5
+
6
+ RSpec.describe(Jekyll::Lilypond::TagProcessor) do
7
+ let(:site) { double("source" => "/some/directory", "static_files" => []) }
8
+ let(:tag) { double("Tag", template_names: { source: "NiftyTemplateName", include: nil},
9
+ template_code: { source: nil, include: "{{ filename }} abcde" },
10
+ attrs: {}) }
11
+ let(:subject) { described_class.new(site, tag) }
12
+
13
+ context "generating source code" do
14
+ it "creates a Template using the tag data when you ask for a source template" do
15
+ expect(Jekyll::Lilypond::Template).to receive(:new).with(site, tag, :source)
16
+ subject.source_template_obj
17
+ end
18
+ end
19
+
20
+ context "generating include code" do
21
+ it "creates a Template using the tag data when you ask for an include template" do
22
+ expect(Jekyll::Lilypond::Template).to receive(:new).with(site, tag, :include)
23
+ subject.include_template_obj
24
+ end
25
+ end
26
+
27
+ context "generating filename" do
28
+ let(:source) { "abcde" }
29
+ let(:hash) { "ab56b4d92b40713acc5af89985d4b786" }
30
+
31
+ it "generates a filename as the hash of the source" do
32
+ allow(subject).to receive(:source).and_return(source)
33
+ expect(subject.hash).to eq(hash)
34
+ end
35
+ it "makes the filename available to the include template" do
36
+ allow(subject).to receive(:source).and_return(source)
37
+ expect(subject.include).to eq("#{hash} abcde")
38
+ end
39
+ it "provides the filename to the file processor" do
40
+ allow(subject).to receive(:source).and_return(source)
41
+ expect(Jekyll::Lilypond::FileProcessor).to \
42
+ receive(:new).with("/some/directory/lilypond_files", hash, source)
43
+ subject.file_processor
44
+ end
45
+ end
46
+
47
+ context "running" do
48
+ let(:source) { "abcde" }
49
+ let(:hash) { "ab56b4d92b40713acc5af89985d4b786" }
50
+ before do
51
+ allow(subject).to receive(:source).and_return(source)
52
+ allow_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:write)
53
+ allow_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:compile)
54
+ allow_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:trim_svg)
55
+ end
56
+
57
+ it "calls write" do
58
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:write)
59
+ subject.run!
60
+ end
61
+ it "calls compile" do
62
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:compile)
63
+ subject.run!
64
+ end
65
+ it "doesn't call trim_svg by default" do
66
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).not_to receive(:trim_svg)
67
+ subject.run!
68
+ end
69
+ it "calls trim_svg if the tag asks it to" do
70
+ tag.attrs["trim"] = "true"
71
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:trim_svg)
72
+ subject.run!
73
+ end
74
+ it "doesn't call make_mp3 by default" do
75
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).not_to receive(:make_mp3)
76
+ subject.run!
77
+ end
78
+ it "calls make_mp3 if the tag asks it to" do
79
+ tag.attrs["mp3"] = "true"
80
+ expect_any_instance_of(Jekyll::Lilypond::FileProcessor).to receive(:make_mp3)
81
+ subject.run!
82
+ end
83
+ end
84
+ end
85
+
86
+ RSpec.describe(Jekyll::Lilypond::TagProcessor) do
87
+ let(:overrides) { {} }
88
+ let(:config) do
89
+ Jekyll.configuration(Jekyll::Utils.deep_merge_hashes({
90
+ "full_rebuild" => true,
91
+ "source" => source_dir,
92
+ "destination" => dest_dir,
93
+ "show_drafts" => false,
94
+ "url" => "http://example.org",
95
+ "name" => "My awesome site",
96
+ "author" => {
97
+ "name" => "Dr. Jekyll",
98
+ },
99
+ "collections" => {
100
+ "my_collection" => { "output" => true },
101
+ "other_things" => { "output" => false },
102
+ },
103
+ }, overrides))
104
+ end
105
+ let(:site) { Jekyll::Site.new(config) }
106
+ let(:context) { make_context(:site => site) }
107
+ before(:each) do
108
+ FileUtils.rm_r Dir.glob(dest_dir("*"))
109
+ site.process
110
+ end
111
+ after(:each) do
112
+ FileUtils.rm_r Dir.glob(source_dir("lilypond_files/*"))
113
+ end
114
+
115
+ context "integration tests" do
116
+ let(:code_tag) { Jekyll::Lilypond::Tag.new({ "source_template_code" => '\version "2.20.0" { {{ content }} }',
117
+ "include_template_code" => '<img src="{{ filename }}.svg" />' },
118
+ "a b c d e") }
119
+ let(:subject) { described_class.new(site, code_tag) }
120
+ let(:target_hash) { "bfcae429ec31cea750c3ab8167526c7c" }
121
+ let(:target_include) { '<img src="bfcae429ec31cea750c3ab8167526c7c.svg" />' }
122
+ let(:target_path) { "#{source_dir}/lilypond_files/bfcae429ec31cea750c3ab8167526c7c.svg" }
123
+
124
+ it "Generates the correct hash from a real tag with explicit code" do
125
+ expect(subject.hash).to eq(target_hash)
126
+ end
127
+ it "Produces an SVG file at the expected location in the source tree" do
128
+ subject.run!
129
+ expect(File).to exist(target_path)
130
+ end
131
+ it "Includes the filename in the include code" do
132
+ expect(subject.include).to eq(target_include)
133
+ end
134
+ end
135
+ end
data/spec/tag_spec.rb ADDED
@@ -0,0 +1,51 @@
1
+ require "jekyll-lilypond"
2
+ require "spec_helper"
3
+ include Liquid
4
+
5
+ RSpec.describe(Jekyll::Lilypond::Tag) do
6
+ context "initialization" do
7
+ it "stores attribute values" do
8
+ t = described_class.new({"foo" => "bar"}, "text")
9
+ expect(t.attrs["foo"]).to eq("bar")
10
+ end
11
+ it "stores content" do
12
+ t = described_class.new({"foo" => "bar"}, "text")
13
+ expect(t.attrs["content"]).to eq("text")
14
+ end
15
+ it "stores source template addresses separately" do
16
+ t = described_class.new({"foo" => "bar", "source_template" => "filename.ly"}, "text")
17
+ expect(t.attrs["source_template"]).to eq(nil)
18
+ end
19
+ it "stores source template addresses in template_names" do
20
+ t = described_class.new({"foo" => "bar", "source_template" => "filename.ly"}, "text")
21
+ expect(t.template_names[:source]).to eq("filename.ly")
22
+ end
23
+ it "stores source template code in template_code" do
24
+ t = described_class.new({"foo" => "bar", "source_template_code" => "foo"}, "text")
25
+ expect(t.template_code[:source]).to eq("foo")
26
+ end
27
+ it "stores include template addresses separately" do
28
+ t = described_class.new({"foo" => "bar", "include_template" => "filename.ly"}, "text")
29
+ expect(t.attrs["include_template"]).to eq(nil)
30
+ end
31
+ it "stores include template addresses in template_names" do
32
+ t = described_class.new({"foo" => "bar", "include_template" => "filename.ly"}, "text")
33
+ expect(t.template_names[:include]).to eq("filename.ly")
34
+ end
35
+ it "stores include template code in template_code" do
36
+ t = described_class.new({"foo" => "bar", "include_template_code" => "foo"}, "text")
37
+ expect(t.template_code[:include]).to eq("foo")
38
+ end
39
+ it "copes with missing text" do
40
+ expect { described_class.new({"foo" => "bar"}, "") }.to_not raise_error()
41
+ end
42
+ it "creates template_names and template_code even when no values are provided" do
43
+ t = described_class.new({"foo" => "bar"}, "")
44
+ expect(t.template_names).to eq({source: nil, include: nil})
45
+ expect(t.template_code).to eq({source: nil, include: nil})
46
+ end
47
+ it "copes with empty attrs" do
48
+ expect { described_class.new({}, "text") }.to_not raise_error()
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,175 @@
1
+ require "jekyll-lilypond"
2
+ require "spec_helper"
3
+ include Liquid
4
+
5
+ long_content =<<-CONTENT
6
+ thing thing thing
7
+ stuff stuff stuff
8
+ (╯°□°)╯︵ ┻━┻)
9
+ ❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙
10
+ stuff stuff stuff
11
+ thing
12
+ CONTENT
13
+
14
+ RSpec.describe(Jekyll::Lilypond::Template) do
15
+ let(:site) { double("Site", layouts: { "name_a" => layout_a,
16
+ "name_b" => layout_b },
17
+ lilypond: double(template_names:
18
+ { source: "name_a",
19
+ include: "name_b" } )) }
20
+ let(:site_without_defaults) { double("Site", layouts: { "name_a" => layout_a,
21
+ "name_b" => layout_b }) }
22
+ let(:template_a) { "{{ content }}" }
23
+ let(:template_b) { "{{ content }}{{ a }}{{ b }}" }
24
+ let(:template_c) { "{{ content }}{{ a }}{{ b }}{{ c }}{{ d }}" }
25
+ let(:layout_a) { double(content: template_a) }
26
+ let(:layout_b) { double(content: template_b) }
27
+ let(:layout_c) { double(content: template_c) }
28
+
29
+ context "getting template names" do
30
+ context "when they are given by the tag" do
31
+ let(:tag) { double("Tag", attrs: {},
32
+ template_code: {source: nil, include: nil },
33
+ template_names: { source: "foo", include: "bar" }) }
34
+ it "returns the given source template name" do
35
+ subject = described_class.new(site, tag, :source)
36
+ expect(subject.template_name).to eq("foo")
37
+ end
38
+ it "returns the given include template name" do
39
+ subject = described_class.new(site, tag, :include)
40
+ expect(subject.template_name).to eq("bar")
41
+ end
42
+ end
43
+ context "when they are not given by the tag" do
44
+ let(:tag) { double("Tag", attrs: {},
45
+ template_code: {source: nil, include: nil },
46
+ template_names: { source: nil, include: nil }) }
47
+ it "returns the sitewide source template name" do
48
+ subject = described_class.new(site, tag, :source)
49
+ expect(subject.template_name).to eq("name_a")
50
+ end
51
+ it "returns the sitewide include template name" do
52
+ subject = described_class.new(site, tag, :include)
53
+ expect(subject.template_name).to eq("name_b")
54
+ end
55
+ it "won't error when there is no sitewide template name" do
56
+ subject = described_class.new(site_without_defaults, tag, :source)
57
+ expect { subject.template_name }.not_to raise_exception
58
+ end
59
+ end
60
+ context "when they are not given by the tag or the site" do
61
+ let(:tag) { double("Tag", attrs: {},
62
+ template_code: {source: nil, include: nil },
63
+ template_names: { source: nil, include: nil }) }
64
+ it "returns the pluginwide source template name" do
65
+ subject = described_class.new(site_without_defaults, tag, :source)
66
+ expect(subject.template_name).to eq("basic")
67
+ end
68
+ it "returns the pluginwide include template name" do
69
+ subject = described_class.new(site_without_defaults, tag, :include)
70
+ expect(subject.template_name).to eq("img")
71
+ end
72
+ end
73
+ end
74
+
75
+ context "when fetching templates" do
76
+ context "literal template code" do
77
+ let(:tag) { double("Tag", attrs: {},
78
+ template_names: { source: nil, include: nil },
79
+ template_code: { source: "foo", include: "bar" }) }
80
+ it "is used verbatim for source" do
81
+ subject = described_class.new(site, tag, :source)
82
+ expect(subject.template_code).to eq("foo")
83
+ end
84
+ it "is used verbatim for includes" do
85
+ subject = described_class.new(site, tag, :include)
86
+ expect(subject.template_code).to eq("bar")
87
+ end
88
+ end
89
+ context "a valid template name" do
90
+ let(:tag) { double("Tag",
91
+ attrs: {},
92
+ template_names: { source: "name_a", include: "name_b" },
93
+ template_code: { source: nil, include: nil }) }
94
+ it "loads the corresponding source template" do
95
+ subject = described_class.new(site, tag, :source)
96
+ expect(subject.template_code).to eq(template_a)
97
+ end
98
+ it "loads the corresponding include template" do
99
+ subject = described_class.new(site, tag, :include)
100
+ expect(subject.template_code).to eq(template_b)
101
+ end
102
+ end
103
+ context "a template name that the plugin defines but the site doesn't" do
104
+ let(:tag) { double("Tag",
105
+ attrs: {},
106
+ template_names: { source: "raw", include: "raw" },
107
+ template_code: { source: nil, include: nil }) }
108
+ it "loads the corresponding plugin source template" do
109
+ subject = described_class.new(site, tag, :source)
110
+ expect(subject.template_code).to eq("{{ content }}")
111
+ end
112
+ it "loads the corresponding plugin include template" do
113
+ subject = described_class.new(site, tag, :include)
114
+ expect(subject.template_code).to eq("{{ content }}")
115
+ end
116
+ end
117
+ context "a template name that the plugin and the site both define" do
118
+ let(:differentsite) { double("Site", layouts: { "raw" => layout_a }) }
119
+ let(:tag) { double("Tag",
120
+ attrs: {},
121
+ template_names: { source: "raw", include: "raw" },
122
+ template_code: { source: nil, include: nil }) }
123
+ it "gives the site version of the source template precedence" do
124
+ subject = described_class.new(differentsite, tag, :source)
125
+ expect(subject.template_code).to eq(template_a)
126
+ end
127
+ it "gives the site version of the include template precedence" do
128
+ subject = described_class.new(differentsite, tag, :include)
129
+ expect(subject.template_code).to eq(template_a)
130
+ end
131
+ end
132
+ context "an invalid template name" do
133
+ let(:tag) { double("Tag",
134
+ attrs: {},
135
+ template_names: { source: "i_do_not_exist", include: "i_do_not_exist" },
136
+ template_code: { source: nil, include: nil }) }
137
+ it "raises an error when it's a source template name" do
138
+ subject = described_class.new(site, tag, :source)
139
+ expect { subject.template_code }.to raise_exception(LoadError)
140
+ end
141
+ it "raises an error when it's an include template name" do
142
+ subject = described_class.new(site, tag, :include)
143
+ expect { subject.template_code }.to raise_exception(LoadError)
144
+ end
145
+ end
146
+ end
147
+
148
+ context "rendering" do
149
+ let(:tag) { double("Tag",
150
+ attrs: {"content" => "text",
151
+ "a" => "1",
152
+ "b" => "2"},
153
+ template_code: { source: template_a, include: template_b },
154
+ template_names: { source: nil, include: nil }) }
155
+ let(:long_tag) { double("Tag",
156
+ attrs: {"content" => long_content,
157
+ "a" => "1",
158
+ "b" => "2"},
159
+ template_code: { source: template_a, include: template_b },
160
+ template_names: { source: nil, include: nil }) }
161
+ it "renders a template without variables" do
162
+ subject = described_class.new(site, tag, :source)
163
+ expect(subject.render(tag)).to eq("text")
164
+ end
165
+ it "renders a template with variables" do
166
+ subject = described_class.new(site, tag, :include)
167
+ expect(subject.render(tag)).to eq("text12")
168
+ end
169
+ it "handles multiline content" do
170
+ subject = described_class.new(site, long_tag, :include)
171
+ expect(subject.render(long_tag)).to eq("#{long_content}12")
172
+ end
173
+ end
174
+ end
175
+
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-lilypond
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Leah Velleman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rspec
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.5'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.5'
47
+ description:
48
+ email:
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - ".gitignore"
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - LICENSE
57
+ - README.md
58
+ - files/rite.png
59
+ - jekyll-lilypond.gemspec
60
+ - lib/jekyll-lilypond.rb
61
+ - lib/jekyll-lilypond/file_processor.rb
62
+ - lib/jekyll-lilypond/lilypond_tag.rb
63
+ - lib/jekyll-lilypond/tag.rb
64
+ - lib/jekyll-lilypond/tag_processor.rb
65
+ - lib/jekyll-lilypond/templates.rb
66
+ - lib/jekyll-lilypond/templates/basic.ly
67
+ - lib/jekyll-lilypond/templates/empty.ly
68
+ - lib/jekyll-lilypond/templates/figure.html
69
+ - lib/jekyll-lilypond/templates/img.html
70
+ - lib/jekyll-lilypond/templates/raw.html
71
+ - lib/jekyll-lilypond/templates/raw.ly
72
+ - lib/jekyll-lilypond/templates/showsource.html
73
+ - lib/jekyll-lilypond/version.rb
74
+ - spec/file_processor_spec.rb
75
+ - spec/fixtures/.gitignore
76
+ - spec/fixtures/404.html
77
+ - spec/fixtures/Gemfile
78
+ - spec/fixtures/_config.yml
79
+ - spec/fixtures/_layouts/basic_html.html
80
+ - spec/fixtures/_layouts/vacuous_html.html
81
+ - spec/fixtures/_layouts/vacuous_ly.ly
82
+ - spec/fixtures/_layouts/variables_html.html
83
+ - spec/fixtures/_layouts/variables_ly.ly
84
+ - spec/fixtures/_posts/2021-01-16-welcome-to-jekyll.markdown
85
+ - spec/fixtures/about.markdown
86
+ - spec/fixtures/index.markdown
87
+ - spec/fixtures/page.md
88
+ - spec/integration_spec.rb
89
+ - spec/spec_helper.rb
90
+ - spec/tag_processor_spec.rb
91
+ - spec/tag_spec.rb
92
+ - spec/templates_spec.rb
93
+ homepage: https://www.velleman.org/jekyll-lilypond.html
94
+ licenses:
95
+ - MIT
96
+ metadata:
97
+ documentation_uri: https://www.velleman.org/jekyll-lilypond.html
98
+ source_code_uri: https://github.com/leahvelleman/jekyll-lilypond
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.0.3
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Lilypond music snippets in Jekyll
118
+ test_files: []