chordpro 0.0.1 → 1.0.0
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.
- checksums.yaml +5 -5
- data/Gemfile +3 -2
- data/Guardfile +3 -3
- data/README.md +32 -2
- data/Rakefile +2 -2
- data/chordpro.gemspec +17 -17
- data/lib/chordpro/chord.rb +27 -0
- data/lib/chordpro/directive.rb +37 -0
- data/lib/chordpro/html.rb +36 -34
- data/lib/chordpro/line.rb +18 -0
- data/lib/chordpro/linebreak.rb +11 -0
- data/lib/chordpro/lyric.rb +11 -0
- data/lib/chordpro/metadata.rb +29 -0
- data/lib/chordpro/parser.rb +14 -13
- data/lib/chordpro/setup.rb +63 -0
- data/lib/chordpro/song.rb +26 -0
- data/lib/chordpro/transform.rb +17 -0
- data/lib/chordpro/version.rb +1 -1
- data/lib/chordpro.rb +17 -3
- data/spec/chordpro/chord_spec.rb +37 -0
- data/spec/chordpro/directive_spec.rb +53 -0
- data/spec/chordpro/html_spec.rb +23 -6
- data/spec/chordpro/line_spec.rb +17 -0
- data/spec/chordpro/parser_spec.rb +61 -55
- data/spec/chordpro/song_spec.rb +56 -0
- data/spec/chordpro/transform_spec.rb +65 -0
- data/spec/example_spec.rb +14 -9
- data/spec/fixtures/sunshine.crd +1 -0
- data/spec/fixtures/sunshine.html +1 -27
- data/spec/spec_helper.rb +2 -1
- metadata +52 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3edc27bf8140ea8f823be328ea4bbb0445c8043bff80bb7a96282bc1620d6fc
|
4
|
+
data.tar.gz: c226e03871b162324c61f852b2b19b90abc7e238a2cb4e3bc8526d28b4388093
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5411d453a8a82e400c4b89f4f5f5010d03cb921c426b52957c06d81c3e9bf6129e8f642483bb3274f921232f4cd516cc2d369640479599cea3280b142d5b22a8
|
7
|
+
data.tar.gz: aaa2e883a32b1942c000d2b8db0654a0078a5822ecdf592123566096bb100a67078ab74e71139dad48c6100909f5e43333562c121e1ba2144ca2f9fd1049d88d
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
guard
|
1
|
+
guard "rspec", cmd: "bundle exec rspec" do
|
2
2
|
watch(%r{^spec/.+_spec\.rb$})
|
3
|
-
watch(%r{^spec/spec_helper.rb$})
|
4
|
-
watch(%r{^lib/(.+)\.rb$})
|
3
|
+
watch(%r{^spec/spec_helper.rb$}) { "spec" }
|
4
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
5
5
|
end
|
data/README.md
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
# Chordpro
|
2
2
|
|
3
|
-
A ruby parser for the [chordpro](http://tenbyten.com/software/songsgen/help/HtmlHelp/files_reference.htm) song file format.
|
3
|
+
A ruby parser for the [chordpro](http://tenbyten.com/software/songsgen/help/HtmlHelp/files_reference.htm) song file format. It converts songs in the chordpro format like this…
|
4
|
+
|
5
|
+
```chordpro
|
6
|
+
{title: You Are My Sunshine}
|
7
|
+
|
8
|
+
{c:Verse 1}
|
9
|
+
[G]The other night dear as I lay sleeping
|
10
|
+
[G7]I dreamed I [C]held you in my [G]arms
|
11
|
+
[G7]But when I a[C]woke dear I was mis[G]taken
|
12
|
+
So I hung my [D7]head and [G]cried
|
13
|
+
|
14
|
+
{c:Chorus}
|
15
|
+
{soc}
|
16
|
+
You are my sunshine my only sunshine
|
17
|
+
[G7]You make me [C]happy when skies are [G]gray
|
18
|
+
[G7]You'll never [C]know dear how much I [G]love you
|
19
|
+
Please don't take [D7]my sunshine a[G]way
|
20
|
+
{eoc}
|
21
|
+
```
|
22
|
+
|
23
|
+
…to HTML, which can be styled to look something like this…
|
24
|
+
|
25
|
+

|
4
26
|
|
5
27
|
## Installation
|
6
28
|
|
@@ -19,7 +41,15 @@ Or install it yourself as:
|
|
19
41
|
## Usage
|
20
42
|
|
21
43
|
```ruby
|
22
|
-
|
44
|
+
contents = File.read('spec/fixtures/sunshine.crd')
|
45
|
+
|
46
|
+
# Generate HTML from the chordpro song
|
47
|
+
File.write('sunshine.html', Chordpro.html(contents))
|
48
|
+
|
49
|
+
# Parse and inspect the chordpro song
|
50
|
+
song = Chordpro.parse(contents)
|
51
|
+
song.title # => "You Are My Sunshine"
|
52
|
+
song.metadata.to_h # => {"title" => "You Are My Sunshine", "key" => "G"}
|
23
53
|
```
|
24
54
|
|
25
55
|
## Contributing
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
-
require
|
3
|
+
require "rspec/core/rake_task"
|
4
4
|
|
5
5
|
desc "Run all specs"
|
6
6
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
@@ -8,4 +8,4 @@ RSpec::Core::RakeTask.new(:spec) do |t|
|
|
8
8
|
t.verbose = false
|
9
9
|
end
|
10
10
|
|
11
|
-
task :
|
11
|
+
task default: :spec
|
data/chordpro.gemspec
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require "chordpro/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
11
|
-
spec.summary
|
12
|
-
spec.description
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
6
|
+
spec.name = "chordpro"
|
7
|
+
spec.version = Chordpro::VERSION
|
8
|
+
spec.authors = ["Brandon Keepers"]
|
9
|
+
spec.email = ["brandon@opensoul.org"]
|
10
|
+
spec.summary = "A ruby parser for the chordpro song file format."
|
11
|
+
spec.description = "A ruby parser for the chordpro song file format."
|
12
|
+
spec.homepage = "https://github.com/bkeepers/chordpro"
|
13
|
+
spec.license = "MIT"
|
15
14
|
|
16
|
-
spec.files
|
17
|
-
spec.executables
|
18
|
-
spec.test_files
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
18
|
spec.require_paths = ["lib"]
|
20
19
|
|
21
|
-
spec.add_dependency
|
20
|
+
spec.add_dependency "parslet"
|
21
|
+
spec.add_dependency "builder"
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "guard-rspec"
|
27
27
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Chord < Struct.new(:name)
|
3
|
+
SUBSTITUTIONS = {
|
4
|
+
"b" => "♭",
|
5
|
+
"#" => "♯",
|
6
|
+
"aug" => "+",
|
7
|
+
"dim" => "°",
|
8
|
+
"2" => "²",
|
9
|
+
"4" => "⁴",
|
10
|
+
"5" => "⁵",
|
11
|
+
"6" => "⁶",
|
12
|
+
"7" => "⁷",
|
13
|
+
"9" => "⁹",
|
14
|
+
"sus" => "ˢᵘˢ"
|
15
|
+
}
|
16
|
+
|
17
|
+
REGEX = /(#{SUBSTITUTIONS.keys.join('|')})/
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
name.gsub(REGEX) { |match| SUBSTITUTIONS[match] }
|
21
|
+
end
|
22
|
+
|
23
|
+
def accept(visitor)
|
24
|
+
visitor.respond_to?(:chord) ? visitor.chord(self) : self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Directive < Struct.new(:name, :value)
|
3
|
+
class Name
|
4
|
+
attr_reader :aka, :meta
|
5
|
+
|
6
|
+
def initialize(name, aka: nil, meta: false)
|
7
|
+
@name, @aka, @meta = name, aka, meta
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@name
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
to_s == other.to_s || (aka && aka == other.to_s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
BY_NAME = {}
|
20
|
+
BY_ALIAS = {}
|
21
|
+
|
22
|
+
def self.register(name, **options)
|
23
|
+
directive = Name.new(name, **options)
|
24
|
+
|
25
|
+
BY_NAME[directive.to_s] = directive
|
26
|
+
BY_ALIAS[directive.aka] = directive if directive.aka
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find(name)
|
30
|
+
BY_NAME[name.to_s] || BY_ALIAS[name.to_s]
|
31
|
+
end
|
32
|
+
|
33
|
+
def accept(visitor)
|
34
|
+
visitor.respond_to?(name.to_s) ? visitor.send(name.to_s, value) : self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/chordpro/html.rb
CHANGED
@@ -1,49 +1,41 @@
|
|
1
|
-
require
|
1
|
+
require "builder"
|
2
2
|
|
3
3
|
module Chordpro
|
4
4
|
class HTML
|
5
|
-
def initialize(
|
6
|
-
@song =
|
5
|
+
def initialize(song)
|
6
|
+
@song = song
|
7
|
+
@html = Builder::XmlMarkup.new
|
8
|
+
@song.accept(self)
|
7
9
|
end
|
8
10
|
|
9
11
|
def to_s
|
10
|
-
@
|
12
|
+
@html.target!
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
send key, value
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def directive(directive)
|
20
|
-
send directive[:name], directive if respond_to?(directive[:name])
|
21
|
-
end
|
22
|
-
|
23
|
-
def title(directive)
|
24
|
-
%Q|<h1 class="title">#{directive[:value]}</h1>|
|
15
|
+
def title(title)
|
16
|
+
@html.h1(class: "title") { |h1| h1.text! title }
|
25
17
|
end
|
26
18
|
alias_method :t, :title
|
27
19
|
|
28
|
-
def subtitle(
|
29
|
-
|
20
|
+
def subtitle(subtitle)
|
21
|
+
@html.h2(class: "subtitle") { |h2| h2.text! subtitle }
|
30
22
|
end
|
31
23
|
alias_method :st, :subtitle
|
32
24
|
alias_method :su, :subtitle
|
33
25
|
|
34
|
-
def line(line)
|
26
|
+
def line(line, parts)
|
35
27
|
chords = []
|
36
28
|
lyrics = []
|
37
29
|
|
38
30
|
line.each do |element|
|
39
|
-
if
|
40
|
-
lyrics <<
|
41
|
-
elsif
|
31
|
+
if element.is_a?(Lyric)
|
32
|
+
lyrics << element
|
33
|
+
elsif element.is_a?(Chord)
|
42
34
|
if chords[lyrics.size]
|
43
|
-
|
35
|
+
chords << element
|
44
36
|
lyrics << nil
|
45
37
|
else
|
46
|
-
chords[lyrics.size] =
|
38
|
+
chords[lyrics.size] = element
|
47
39
|
end
|
48
40
|
end
|
49
41
|
end
|
@@ -51,21 +43,31 @@ module Chordpro
|
|
51
43
|
# ensure chords has same number of cells as lyrics
|
52
44
|
chords[lyrics.size - 1] ||= nil if lyrics.size > 0
|
53
45
|
|
54
|
-
|
55
|
-
chords.
|
56
|
-
|
57
|
-
|
58
|
-
|
46
|
+
@html.table do |table|
|
47
|
+
if chords.any?
|
48
|
+
table.tr(class: "chords") do |tr|
|
49
|
+
chords.each do |chord|
|
50
|
+
tr.td { |td| td.text! chord.to_s }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
if lyrics.any?
|
55
|
+
table.tr do |tr|
|
56
|
+
lyrics.each do |lyric|
|
57
|
+
tr.td { |td| td.text! lyric.to_s }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
59
62
|
end
|
60
63
|
|
61
|
-
def
|
62
|
-
|
64
|
+
def linebreak(_)
|
65
|
+
@html.br
|
63
66
|
end
|
64
67
|
|
65
|
-
def comment(
|
66
|
-
|
68
|
+
def comment(text)
|
69
|
+
@html.span(text, class: "comment")
|
67
70
|
end
|
68
71
|
alias_method :c, :comment
|
69
|
-
|
70
72
|
end
|
71
73
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Line < Struct.new(:parts)
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def each(&block)
|
6
|
+
parts.each(&block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def accept(visitor)
|
10
|
+
parts = map { |part| part.accept(visitor) }
|
11
|
+
if visitor.respond_to?(:line)
|
12
|
+
visitor.line(self, parts)
|
13
|
+
else
|
14
|
+
Line.new(parts)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Metadata
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(elements)
|
6
|
+
@elements = elements
|
7
|
+
end
|
8
|
+
|
9
|
+
def directives
|
10
|
+
@elements.lazy.select { |e| e.is_a?(Directive) && e.name.meta }
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
directives.each(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
values = select { |directive| directive.name == key }.map(&:value)
|
19
|
+
values.length > 1 ? values : values[0]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
each_with_object({}) do |d, h|
|
24
|
+
key = d.name.to_s
|
25
|
+
h[key] = h.has_key?(key) ? Array(h[key]).push(d.value) : d.value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/chordpro/parser.rb
CHANGED
@@ -1,26 +1,27 @@
|
|
1
|
-
require
|
1
|
+
require "parslet"
|
2
2
|
|
3
3
|
module Chordpro
|
4
4
|
class Parser < Parslet::Parser
|
5
5
|
# Characters
|
6
|
-
rule(:space)
|
7
|
-
rule(:colon)
|
8
|
-
rule(:newline)
|
9
|
-
rule(:lbrace)
|
10
|
-
rule(:rbrace)
|
11
|
-
rule(:lbracket) { str(
|
12
|
-
rule(:rbracket) { str(
|
6
|
+
rule(:space) { match('\s').repeat }
|
7
|
+
rule(:colon) { space >> str(":") >> space }
|
8
|
+
rule(:newline) { str("\n") }
|
9
|
+
rule(:lbrace) { str("{") }
|
10
|
+
rule(:rbrace) { str("}") }
|
11
|
+
rule(:lbracket) { str("[") }
|
12
|
+
rule(:rbracket) { str("]") }
|
13
13
|
|
14
|
-
|
15
|
-
rule(:identifier) { match['a-z'].repeat(1) }
|
14
|
+
rule(:identifier) { match["a-z"].repeat(1) }
|
16
15
|
rule(:value) { (rbrace.absent? >> any).repeat }
|
17
16
|
|
17
|
+
rule(:comment) { str("#") >> (newline.absent? >> any).repeat >> newline.maybe }
|
18
|
+
|
18
19
|
rule(:directive) do
|
19
20
|
(
|
20
21
|
lbrace >> space >>
|
21
22
|
identifier.as(:name) >>
|
22
23
|
(
|
23
|
-
|
24
|
+
colon >>
|
24
25
|
value.as(:value)
|
25
26
|
).maybe >>
|
26
27
|
rbrace
|
@@ -28,9 +29,9 @@ module Chordpro
|
|
28
29
|
end
|
29
30
|
rule(:chord) { lbracket >> (rbracket.absent? >> any).repeat.as(:chord) >> rbracket }
|
30
31
|
rule(:lyric) { (lbracket.absent? >> newline.absent? >> any).repeat(1).as(:lyric) }
|
31
|
-
rule(:line)
|
32
|
+
rule(:line) { (chord | lyric).repeat(1).as(:line) >> newline.maybe }
|
32
33
|
|
33
|
-
rule(:song)
|
34
|
+
rule(:song) { (comment | directive | newline.as(:linebreak) | line).repeat.as(:song) }
|
34
35
|
|
35
36
|
root(:song)
|
36
37
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Register documented directives
|
2
|
+
# https://www.chordpro.org/chordpro/chordpro-directives/
|
3
|
+
Chordpro::Directive.class_eval do
|
4
|
+
# preamble
|
5
|
+
register "new_song", aka: "ns"
|
6
|
+
|
7
|
+
# metadata
|
8
|
+
register "title", aka: "t", meta: true
|
9
|
+
register "sorttitle", meta: true
|
10
|
+
register "subtitle", aka: "st", meta: true
|
11
|
+
register "artist", meta: true
|
12
|
+
register "composer", meta: true
|
13
|
+
register "lyricist", meta: true
|
14
|
+
register "copyright", meta: true
|
15
|
+
register "album", meta: true
|
16
|
+
register "year", meta: true
|
17
|
+
register "key", meta: true
|
18
|
+
register "time", meta: true
|
19
|
+
register "tempo", meta: true
|
20
|
+
register "duration", meta: true
|
21
|
+
register "capo", meta: true
|
22
|
+
register "meta", meta: true
|
23
|
+
|
24
|
+
# Environment directives
|
25
|
+
%w[chorus verse bridge tab grid].each do |directive|
|
26
|
+
letter = directive[0]
|
27
|
+
register "start_of_#{directive}", aka: "so#{letter}"
|
28
|
+
register "end_of_#{directive}", aka: "eo#{letter}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Chord diagrams
|
32
|
+
register "define"
|
33
|
+
register "chord"
|
34
|
+
register "transpose"
|
35
|
+
|
36
|
+
register "chordfont", aka: "cf"
|
37
|
+
register "chordsize", aka: "cs"
|
38
|
+
register "chordcolour"
|
39
|
+
register "footerfont"
|
40
|
+
register "footersize"
|
41
|
+
register "footercolour"
|
42
|
+
register "gridfont"
|
43
|
+
register "gridsize"
|
44
|
+
register "gridcolour"
|
45
|
+
register "tabfont"
|
46
|
+
register "tabsize"
|
47
|
+
register "tabcolour"
|
48
|
+
register "tocfont"
|
49
|
+
register "tocsize"
|
50
|
+
register "toccolour"
|
51
|
+
register "textfont", aka: "tf"
|
52
|
+
register "textsize", aka: "ts"
|
53
|
+
register "textcolour"
|
54
|
+
register "titlefont"
|
55
|
+
register "titlesize"
|
56
|
+
register "titlecolour"
|
57
|
+
|
58
|
+
# Output related directives
|
59
|
+
register "new_page", aka: "np"
|
60
|
+
register "new_physical_page", aka: "npp"
|
61
|
+
register "column_break", aka: "cb"
|
62
|
+
register "pagetype"
|
63
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Song
|
3
|
+
attr_reader :elements, :metadata
|
4
|
+
|
5
|
+
def initialize(elements = [])
|
6
|
+
@elements = elements
|
7
|
+
@metadata = Metadata.new(@elements)
|
8
|
+
end
|
9
|
+
|
10
|
+
def accept(visitor)
|
11
|
+
elements.map { |element| element.accept(visitor) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method, *args)
|
15
|
+
if respond_to_missing?(method)
|
16
|
+
metadata[method]
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(method, include_all = false)
|
23
|
+
super || !!Directive.find(method)&.meta
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Chordpro
|
2
|
+
class Transform < Parslet::Transform
|
3
|
+
rule(directive: {name: simple(:name), value: simple(:value)}) do
|
4
|
+
directive_name = Directive.find(name) || Directive::Name.new(name.to_s)
|
5
|
+
Chordpro::Directive.new(directive_name, value.to_s)
|
6
|
+
end
|
7
|
+
rule(directive: {name: simple(:name)}) do
|
8
|
+
Chordpro::Directive.new(name.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
rule(linebreak: simple(:x)) { Chordpro::Linebreak.new }
|
12
|
+
rule(chord: simple(:name)) { Chordpro::Chord.new(name.to_s) }
|
13
|
+
rule(lyric: simple(:text)) { Chordpro::Lyric.new(text.to_s) }
|
14
|
+
rule(line: subtree(:parts)) { Chordpro::Line.new(parts) }
|
15
|
+
rule(song: subtree(:elements)) { Chordpro::Song.new(elements) }
|
16
|
+
end
|
17
|
+
end
|
data/lib/chordpro/version.rb
CHANGED
data/lib/chordpro.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
|
-
require "chordpro/
|
2
|
-
require "chordpro/
|
1
|
+
require "chordpro/chord"
|
2
|
+
require "chordpro/directive"
|
3
3
|
require "chordpro/html"
|
4
|
+
require "chordpro/line"
|
5
|
+
require "chordpro/linebreak"
|
6
|
+
require "chordpro/lyric"
|
7
|
+
require "chordpro/metadata"
|
8
|
+
require "chordpro/parser"
|
9
|
+
require "chordpro/song"
|
10
|
+
require "chordpro/transform"
|
11
|
+
require "chordpro/version"
|
12
|
+
|
13
|
+
require "chordpro/setup"
|
4
14
|
|
5
15
|
module Chordpro
|
16
|
+
def self.parse(string)
|
17
|
+
Transform.new.apply(Parser.new.parse(string))
|
18
|
+
end
|
19
|
+
|
6
20
|
def self.html(string)
|
7
|
-
HTML.new(
|
21
|
+
HTML.new(parse(string)).to_s
|
8
22
|
end
|
9
23
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Chordpro::Chord do
|
4
|
+
describe "to_s" do
|
5
|
+
{
|
6
|
+
"Bb" => "B♭",
|
7
|
+
"F#m" => "F♯m",
|
8
|
+
"Gaug7" => "G+⁷",
|
9
|
+
"Ddim7" => "D°⁷",
|
10
|
+
"Csus9" => "Cˢᵘˢ⁹",
|
11
|
+
"Asus2" => "Aˢᵘˢ²",
|
12
|
+
"Esus4" => "Eˢᵘˢ⁴",
|
13
|
+
"E5" => "E⁵",
|
14
|
+
"Cm6" => "Cm⁶",
|
15
|
+
"G7" => "G⁷",
|
16
|
+
"Asus9" => "Aˢᵘˢ⁹"
|
17
|
+
}.each do |input, output|
|
18
|
+
it "replaces #{input.inspect} with #{output.inspect}" do
|
19
|
+
expect(Chordpro::Chord.new(input).to_s).to eq(output)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "accept" do
|
25
|
+
let(:chord) { Chordpro::Chord.new("C") }
|
26
|
+
let(:visitor) { double(:visitor) }
|
27
|
+
|
28
|
+
it "calls chord with chord" do
|
29
|
+
expect(visitor).to receive(:chord).with(chord).and_return("result")
|
30
|
+
expect(chord.accept(visitor)).to eq("result")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "does not blow up if object does not respond to chord" do
|
34
|
+
expect(chord.accept(visitor)).to be(chord)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Chordpro::Directive do
|
4
|
+
describe "accept" do
|
5
|
+
let(:visitor) { double(:visitor) }
|
6
|
+
let(:directive) { Chordpro::Directive.new("title", "The Visitor") }
|
7
|
+
|
8
|
+
it "calls method on visitor" do
|
9
|
+
expect(visitor).to receive(:title).and_return("title")
|
10
|
+
expect(directive.accept(visitor)).to eq("title")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns self when visitor does not respond to method" do
|
14
|
+
expect(directive.accept(visitor)).to be(directive)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Chordpro::Directive::Name do
|
20
|
+
describe "==" do
|
21
|
+
context "with alias" do
|
22
|
+
let(:name) { Chordpro::Directive::Name.new("title", aka: "t") }
|
23
|
+
|
24
|
+
it "equals string matching name" do
|
25
|
+
expect(name).to eq("title")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "equals symbol matching name" do
|
29
|
+
expect(name).to eq(:title)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "equals string matching alias" do
|
33
|
+
expect(name).to eq("t")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "equals symbol matching alias" do
|
37
|
+
expect(name).to eq(:t)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "equals Name matching name" do
|
41
|
+
expect(name).to eq(described_class.new("title"))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not equal other string" do
|
45
|
+
expect(name).not_to eq("nope")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "does not equal other Name" do
|
49
|
+
expect(name).not_to eq(described_class.new("comment"))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|