gitara 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +2 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.markdown +5 -0
  4. data/README.markdown +135 -99
  5. data/examples/tab-with-repeats.ly +117 -0
  6. data/examples/tab-with-repeats.rb +19 -0
  7. data/gitara.gemspec +3 -0
  8. data/lib/gitara.rb +5 -3
  9. data/lib/gitara/dsl.rb +13 -0
  10. data/lib/gitara/node/alternative.rb +14 -0
  11. data/lib/gitara/node/bar.rb +3 -1
  12. data/lib/gitara/node/base.rb +22 -24
  13. data/lib/gitara/node/base/chorded_version.rb +1 -4
  14. data/lib/gitara/node/base/node_version.rb +35 -0
  15. data/lib/gitara/node/base/stanza_version.rb +1 -4
  16. data/lib/gitara/node/base/voiced_version.rb +5 -6
  17. data/lib/gitara/node/repeat.rb +10 -0
  18. data/lib/gitara/node/tab.rb +3 -5
  19. data/lib/gitara/utilities.rb +8 -0
  20. data/lib/gitara/version.rb +1 -1
  21. data/lib/gitara/voice.rb +2 -0
  22. data/spec/factories.rb +6 -0
  23. data/spec/lib/gitara/app_spec.rb +12 -28
  24. data/spec/lib/gitara/dsl_spec.rb +218 -173
  25. data/spec/lib/gitara/node/alternative_spec.rb +15 -0
  26. data/spec/lib/gitara/node/bar/chorded_version_spec.rb +2 -2
  27. data/spec/lib/gitara/node/bar/stanza_version_spec.rb +2 -2
  28. data/spec/lib/gitara/node/bar/voiced_version_spec.rb +2 -2
  29. data/spec/lib/gitara/node/bar_spec.rb +7 -7
  30. data/spec/lib/gitara/node/base/chorded_version_spec.rb +1 -1
  31. data/spec/lib/gitara/node/base/node_version_spec.rb +66 -0
  32. data/spec/lib/gitara/node/base/voiced_version_spec.rb +5 -5
  33. data/spec/lib/gitara/node/base_spec.rb +36 -42
  34. data/spec/lib/gitara/node/chord_set/chorded_version_spec.rb +2 -2
  35. data/spec/lib/gitara/node/chord_set_spec.rb +1 -1
  36. data/spec/lib/gitara/node/note_set_spec.rb +1 -1
  37. data/spec/lib/gitara/node/repeat_spec.rb +15 -0
  38. data/spec/lib/gitara/node/stanza_spec.rb +2 -2
  39. data/spec/lib/gitara/node/tab_spec.rb +6 -6
  40. data/spec/lib/gitara/voice_spec.rb +9 -9
  41. data/spec/lib/gitara_spec.rb +3 -1
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/support/app_tester.rb +22 -0
  44. metadata +70 -29
  45. data/lib/gitara/is_node_version.rb +0 -23
  46. data/lib/gitara/pow/base.rb +0 -11
  47. data/spec/lib/gitara/is_node_version_spec.rb +0 -58
@@ -0,0 +1,19 @@
1
+ Gitara.define do
2
+ line do
3
+ repeat 4 do
4
+ bar do
5
+ notes "c4 d e f"
6
+ end
7
+ end
8
+
9
+ alternative do
10
+ bar do
11
+ notes "d2 e"
12
+ end
13
+
14
+ bar do
15
+ notes "f2 g"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_runtime_dependency "erubis"
23
23
  s.add_runtime_dependency "linguistics"
24
24
  s.add_runtime_dependency "pow"
25
+ s.add_runtime_dependency "redwood"
25
26
  s.add_runtime_dependency "thor"
26
27
  s.add_runtime_dependency "valuable"
27
28
 
@@ -31,4 +32,6 @@ Gem::Specification.new do |s|
31
32
  s.add_development_dependency "pry"
32
33
  s.add_development_dependency "rb-inotify"
33
34
  s.add_development_dependency "rspec"
35
+ s.add_development_dependency "yard"
36
+ s.add_development_dependency "yard-rspec"
34
37
  end
@@ -2,16 +2,18 @@ require "active_support/inflector"
2
2
  require "erubis"
3
3
  require "linguistics"
4
4
  require "pow"
5
+ require "redwood"
5
6
  require "thor"
6
7
  require "valuable"
7
8
 
8
9
  require "gitara/app"
9
10
  require "gitara/dsl"
10
- require "gitara/is_node_version"
11
11
  require "gitara/node/base"
12
+ require "gitara/node/base/node_version"
12
13
  require "gitara/node/base/chorded_version"
13
14
  require "gitara/node/base/stanza_version"
14
15
  require "gitara/node/base/voiced_version"
16
+ require "gitara/node/alternative"
15
17
  require "gitara/node/bar"
16
18
  require "gitara/node/bar/chorded_version"
17
19
  require "gitara/node/bar/stanza_version"
@@ -20,10 +22,10 @@ require "gitara/node/chord_set"
20
22
  require "gitara/node/chord_set/chorded_version"
21
23
  require "gitara/node/line"
22
24
  require "gitara/node/note_set"
25
+ require "gitara/node/repeat"
23
26
  require "gitara/node/score"
24
27
  require "gitara/node/stanza"
25
28
  require "gitara/node/tab"
26
- require "gitara/pow/base"
27
29
  require "gitara/utilities"
28
30
  require "gitara/version"
29
31
  require "gitara/voice"
@@ -39,7 +41,7 @@ module Gitara
39
41
 
40
42
  def self.render(path, object)
41
43
  template = (Pow!('gitara/template') / "#{path}.erb")
42
- erb = Erubis::Eruby.new(template.read!)
44
+ erb = Erubis::Eruby.new(Utilities.read!(template))
43
45
  erb.evaluate(object)
44
46
  end
45
47
 
@@ -4,8 +4,13 @@ module Gitara
4
4
  class Dsl < Valuable
5
5
  extend DslMacros
6
6
 
7
+ # @macro [attach] has_value
8
+ # @attribute $1
7
9
  has_value :node
8
10
 
11
+ # @macro [attach] can_add_property
12
+ # @method $1(value)
13
+ # Set value as the $1
9
14
  can_add_property :arranger
10
15
  can_add_property :composer
11
16
  can_add_property :instrument
@@ -30,6 +35,10 @@ module Gitara
30
35
  end
31
36
  end
32
37
 
38
+ def alternative(&block)
39
+ add Node::Alternative.new, &block
40
+ end
41
+
33
42
  def bar(*names, &block)
34
43
  add_names :names => names, :node_class => Node::Bar, &block
35
44
  end
@@ -50,6 +59,10 @@ module Gitara
50
59
  node.specified_duration = duration
51
60
  end
52
61
 
62
+ def repeat(value, &block)
63
+ add Node::Repeat.new(:value => value), &block
64
+ end
65
+
53
66
  def score(*names, &block)
54
67
  add_names :names => names, :node_class => Node::Score, &block
55
68
  end
@@ -0,0 +1,14 @@
1
+ module Gitara
2
+ module Node
3
+ class Alternative < Base
4
+ def call_value(node_version)
5
+ alternatives = node_version.definition_children.map do |child|
6
+ "{ #{child.call_value} }"
7
+ end
8
+
9
+ "\\alternative { #{alternatives.join(' ')} }"
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -1,6 +1,8 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Bar < Base
4
+ # @macro [attach] has_value
5
+ # @attribute $1
4
6
  has_value :specified_duration
5
7
 
6
8
  def duration
@@ -12,7 +14,7 @@ module Gitara
12
14
  end
13
15
 
14
16
  def note_sets
15
- children.select{|child| child.is_a?(Node::NoteSet)}
17
+ definition_children.select{|child| child.is_a?(Node::NoteSet)}
16
18
  end
17
19
 
18
20
  def specified_duration_as_lilypond
@@ -1,31 +1,37 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Base < Valuable
4
+ include Redwood
5
+
6
+ # @macro [attach] has_value
7
+ # @attribute $1
4
8
  has_value :name
5
9
  has_value :id, :default => 1
6
- has_value :parent
7
10
  has_value :value
8
11
 
9
12
  def add(child)
10
- own_children << child
11
- child.id = own_children.select{|c| c.is_a?(child.class)}.size
12
- child.parent = self
13
+ child.id = children.select{|c| c.is_a?(child.class)}.size + 1
14
+ graft child
15
+ end
16
+
17
+ def ancestor(klass)
18
+ ancestors.detect{|ancestor| ancestor.is_a?(klass)}
13
19
  end
14
20
 
15
- def ancestor(node_class)
16
- self.is_a?(node_class) ? self : self.parent && self.parent.ancestor(node_class)
21
+ def call_value(node_version)
22
+ node_version.call_name
17
23
  end
18
24
 
19
- def children
20
- if own_children.empty?
21
- definition ? definition.own_children : []
25
+ def definition_children
26
+ if leaf?
27
+ definition ? definition.children : []
22
28
  else
23
- own_children
29
+ children
24
30
  end
25
31
  end
26
32
 
27
33
  def children=(values)
28
- own_children.clear
34
+ children.clear
29
35
  values.each do |child|
30
36
  add child
31
37
  end
@@ -39,13 +45,13 @@ module Gitara
39
45
  if self.definition_of?(target)
40
46
  self
41
47
  else
42
- result = parent.own_children.detect{|node| node.definition_of?(target) }
48
+ result = parent.children.detect{|node| node.definition_of?(target) }
43
49
  result ? result : parent.definition(target)
44
50
  end
45
51
  end
46
52
 
47
53
  def definition?
48
- ! own_children.empty? || ! value.nil?
54
+ has_children? || ! value.nil?
49
55
  end
50
56
 
51
57
  def definition_name
@@ -60,11 +66,11 @@ module Gitara
60
66
  end
61
67
 
62
68
  def definitions(klass)
63
- self.is_a?(klass) && self.definition? ? [self] : self.own_children.map{|child| child.definitions(klass) }.flatten
69
+ self.is_a?(klass) && self.definition? ? [self] : self.children.map{|child| child.definitions(klass) }.flatten
64
70
  end
65
71
 
66
72
  def descendants(klass)
67
- self.is_a?(klass) ? [self.definition] : self.children.map{|child| child.descendants(klass) }.flatten
73
+ self.is_a?(klass) ? [self.definition] : self.definition_children.map{|child| child.descendants(klass) }.flatten
68
74
  end
69
75
 
70
76
  def id_as_word
@@ -79,20 +85,12 @@ module Gitara
79
85
  attributes[:name] || "#{parent && parent.name}#{self.class.to_s.split('::').last}#{self.id_as_word}"
80
86
  end
81
87
 
82
- def own_children
83
- @children ||= []
84
- end
85
-
86
- def root
87
- parent.nil? ? self : parent.root
88
- end
89
-
90
88
  def stanza_version
91
89
  self.class::StanzaVersion.new(:node => self)
92
90
  end
93
91
 
94
92
  def value
95
- attributes[:value] ? attributes[:value].gsub('/', "\\") : nil
93
+ attributes[:value].respond_to?(:gsub) ? attributes[:value].gsub('/', "\\") : attributes[:value]
96
94
  end
97
95
 
98
96
  def voiced_as(arg)
@@ -1,10 +1,7 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Base
4
- class ChordedVersion < Valuable
5
- include IsNodeVersion
6
-
7
- has_value :node
4
+ class ChordedVersion < NodeVersion
8
5
  end
9
6
  end
10
7
  end
@@ -0,0 +1,35 @@
1
+ module Gitara
2
+ module Node
3
+ class Base
4
+ class NodeVersion < Valuable
5
+ # @macro [attach] has_value
6
+ # @attribute $1
7
+ has_value :node
8
+
9
+ def call_name
10
+ "\\#{definition_name}"
11
+ end
12
+
13
+ def call_value
14
+ node.call_value(self)
15
+ end
16
+
17
+ def definition_children
18
+ node.definition_children.map{|child| self.class.new(:node => child)}
19
+ end
20
+
21
+ def definition_name
22
+ "#{prefix}#{node.class.to_s.split('::').last}#{node.definition_name}"
23
+ end
24
+
25
+ def prefix
26
+ self.class.to_s.split('::').last[0].downcase
27
+ end
28
+
29
+ def value
30
+ definition_children.map(&:call_value).join(' ')
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,10 +1,7 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Base
4
- class StanzaVersion < Valuable
5
- include IsNodeVersion
6
-
7
- has_value :node
4
+ class StanzaVersion < NodeVersion
8
5
  end
9
6
  end
10
7
  end
@@ -1,14 +1,13 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Base
4
- class VoicedVersion < Valuable
5
- include IsNodeVersion
6
-
7
- has_value :node
4
+ class VoicedVersion < NodeVersion
5
+ # @macro [attach] has_value
6
+ # @attribute $1
8
7
  has_value :voice
9
8
 
10
- def children
11
- node.children.map{|child| child.voiced_as(voice) }
9
+ def definition_children
10
+ node.definition_children.map{|child| child.voiced_as(voice) }
12
11
  end
13
12
 
14
13
  def prefix
@@ -0,0 +1,10 @@
1
+ module Gitara
2
+ module Node
3
+ class Repeat < Base
4
+ def call_value(node_version)
5
+ "\\repeat volta #{value} { #{ node_version.value } }"
6
+ end
7
+ end
8
+ end
9
+ end
10
+
@@ -1,6 +1,8 @@
1
1
  module Gitara
2
2
  module Node
3
3
  class Tab < Base
4
+ # @macro [attach] has_value
5
+ # @attribute $1
4
6
  has_value :arranger
5
7
  has_value :composer
6
8
  has_value :instrument
@@ -11,16 +13,12 @@ module Gitara
11
13
  has_value :title
12
14
  has_value :transposition
13
15
 
14
- def self.parse(text)
15
- Transform.new.apply(Parser.new.parse(text))
16
- end
17
-
18
16
  def max_number_of_voices
19
17
  definitions(Node::Bar).map{|bar| bar.note_sets.size}.max
20
18
  end
21
19
 
22
20
  def playable_child
23
- children.last
21
+ definition_children.last
24
22
  end
25
23
 
26
24
  def voices
@@ -7,6 +7,14 @@ module Gitara
7
7
  def self.inspect_attributes(object, *attributes)
8
8
  "#{object.class}(" + attributes.collect{|a| "#{a}=#{object.send(a).inspect}"}.join(', ') + ")"
9
9
  end
10
+
11
+ def self.read!(pow)
12
+ if pow.exists?
13
+ pow.read
14
+ else
15
+ raise PowError, "#{pow} does not exist"
16
+ end
17
+ end
10
18
  end
11
19
  end
12
20
 
@@ -1,3 +1,3 @@
1
1
  module Gitara
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -1,5 +1,7 @@
1
1
  module Gitara
2
2
  class Voice < Valuable
3
+ # @macro [attach] has_value
4
+ # @attribute $1
3
5
  has_value :id
4
6
  has_value :parent
5
7
 
@@ -2,6 +2,9 @@ FactoryGirl.define do
2
2
  factory :note_set, :class => Node::NoteSet do
3
3
  end
4
4
 
5
+ factory :alternative, :class => Node::Alternative do
6
+ end
7
+
5
8
  factory :app, :class => App do
6
9
  end
7
10
 
@@ -28,6 +31,9 @@ FactoryGirl.define do
28
31
  children [FactoryGirl.build(:bar)]
29
32
  end
30
33
 
34
+ factory :repeat, :class => Node::Repeat do
35
+ end
36
+
31
37
  factory :stanza, :class => Node::Stanza do
32
38
  children [FactoryGirl.build(:line)]
33
39
  end
@@ -1,49 +1,33 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe App do
4
- class AppTest < Valuable
5
- has_value :name
6
-
7
- def run
8
- app = FactoryGirl.build(:app)
9
- app.invoke :export, ["examples/#{name}.rb"], "target-directory" => test_tmp_dir.path, "run-lilypond" => false
10
- (Pow("tmp") / "#{name}.ly").write actual if self.expected != self.actual
11
- end
12
-
13
- def expected
14
- @expected ||= Pow("examples/#{name}.ly").read!.gsub(/\n\s+\n/, "\n")
15
- rescue PowError => e
16
- puts "#{e.message}. Copying actual result..."
17
- Pow("examples/#{name}.ly").write actual
18
- retry
19
- end
20
-
21
- def actual
22
- @actual ||= (test_tmp_dir / "#{name}.ly").read!.gsub(/\n\s+\n/, "\n")
23
- end
24
- end
25
-
26
- describe "export" do
3
+ describe Gitara::App do
4
+ describe "#export" do
27
5
  it "can convert a tab to lilypond" do
28
- app_test = AppTest.new(:name => 'tab')
6
+ app_test = AppTester.new(:name => 'tab')
29
7
  app_test.run
30
8
  app_test.actual.should == app_test.expected
31
9
  end
32
10
 
33
11
  it "can convert a tab with a partial to lilypond" do
34
- app_test = AppTest.new(:name => 'tab-with-partial')
12
+ app_test = AppTester.new(:name => 'tab-with-partial')
35
13
  app_test.run
36
14
  app_test.actual.should == app_test.expected
37
15
  end
38
16
 
39
17
  it "can convert a tab with alternate tuning to lilypond" do
40
- app_test = AppTest.new(:name => 'tab-with-alternate-tuning')
18
+ app_test = AppTester.new(:name => 'tab-with-alternate-tuning')
41
19
  app_test.run
42
20
  app_test.actual.should == app_test.expected
43
21
  end
44
22
 
45
23
  it "can convert a tab with a specified key signature to lilypond" do
46
- app_test = AppTest.new(:name => 'tab-with-key-signature')
24
+ app_test = AppTester.new(:name => 'tab-with-key-signature')
25
+ app_test.run
26
+ app_test.actual.should == app_test.expected
27
+ end
28
+
29
+ it "can convert a tab with repeats to lilypond" do
30
+ app_test = AppTester.new(:name => 'tab-with-repeats')
47
31
  app_test.run
48
32
  app_test.actual.should == app_test.expected
49
33
  end