blaml 1.0.0.pre

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.
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/.gemtest ADDED
File without changes
data/History.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2011-02-02
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ .autotest
2
+ History.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/blaml.rb
7
+ lib/blaml/blamed_io.rb
8
+ lib/blaml/meta.rb
9
+ lib/blaml/to_ruby.rb
10
+ lib/blaml/tree_builder.rb
11
+ test/test_blaml.rb
data/README.rdoc ADDED
@@ -0,0 +1,77 @@
1
+ = Blaml
2
+
3
+ * http://github.com/yaksnrainbows/blaml
4
+
5
+ == DESCRIPTION:
6
+
7
+ Blaml is a parser based on Psych (ruby 1.9 only) for yaml files that have
8
+ been blamed with git.
9
+ Blaml parses the blamed yaml file and makes the blame metadata available on the
10
+ returned ruby object through a 'meta' attribute.
11
+
12
+ == FEATURES:
13
+
14
+ * Load blamed yaml files and access blame data as metadata.
15
+
16
+ * Currently only works on blamed files generated with 'git blame -f file.yml'
17
+
18
+ == SYNOPSIS:
19
+
20
+ b = Blaml.load `git blame -f file.yml`
21
+
22
+ b.meta
23
+ # => Returns metadata hash for most recent change in this data structure.
24
+
25
+ b.keys.first.meta
26
+ # => Returns metadata hash for when this key was changed.
27
+
28
+ b[:key]
29
+ # => "value"
30
+
31
+ b[:key].meta
32
+ # => {:updated_at => 2010-10-28 22:43:33 +0000, :author => 'user', :line => 12, :commit => '25kjpp43', :file => 'file.yml'}
33
+
34
+ b[:key].class
35
+ # => Blaml::MetaNode
36
+
37
+ b[:key] == "value"
38
+ # => true
39
+
40
+ b[:key].value.class
41
+ # => String
42
+
43
+ b.to_value
44
+ # => Returns the full original data structure (not wrapped in meta classes)
45
+ # => {:key => "value"}
46
+
47
+ == INSTALL:
48
+
49
+ $ gem install blaml
50
+
51
+ This task will install any missing dependencies, run the tests/specs,
52
+ and generate the RDoc.
53
+
54
+ == LICENSE:
55
+
56
+ (The MIT License)
57
+
58
+ Copyright (c) 2011 Jeremie Castagna
59
+
60
+ Permission is hereby granted, free of charge, to any person obtaining
61
+ a copy of this software and associated documentation files (the
62
+ 'Software'), to deal in the Software without restriction, including
63
+ without limitation the rights to use, copy, modify, merge, publish,
64
+ distribute, sublicense, and/or sell copies of the Software, and to
65
+ permit persons to whom the Software is furnished to do so, subject to
66
+ the following conditions:
67
+
68
+ The above copyright notice and this permission notice shall be
69
+ included in all copies or substantial portions of the Software.
70
+
71
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
72
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
73
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
74
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
75
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
76
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
77
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'psych'
4
+ require 'rubygems'
5
+ require 'hoe'
6
+
7
+ Hoe.spec 'blaml' do
8
+ developer('Jeremie Castagna', 'yaksnrainbows@gmail.com')
9
+
10
+ self.readme_file = "README.rdoc"
11
+ self.history_file = "History.rdoc"
12
+ self.extra_rdoc_files = FileList['*.rdoc']
13
+ end
14
+
15
+ # vim: syntax=ruby
data/lib/blaml.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'time'
2
+ require 'psych'
3
+ require 'yaml'
4
+
5
+ require 'blaml/blamed_io'
6
+ require 'blaml/tree_builder'
7
+ require 'blaml/meta'
8
+ require 'blaml/to_ruby'
9
+
10
+ class Blaml
11
+
12
+ # This gem's version.
13
+ VERSION = "1.0.0.pre"
14
+
15
+ ###
16
+ # Load +yaml+ in to a Ruby data structure. If multiple documents are
17
+ # provided, the object contained in the first document will be returned.
18
+ #
19
+ # Example:
20
+ #
21
+ # Psych.load("--- a") # => 'a'
22
+ # Psych.load("---\n - a\n - b") # => ['a', 'b']
23
+
24
+ def self.load yaml
25
+ result = parse(yaml)
26
+ result ? result.to_blamed_ruby : result
27
+ end
28
+
29
+
30
+ ###
31
+ # Load multiple documents given in +yaml+. Returns the parsed documents
32
+ # as a list. For example:
33
+ #
34
+ # Psych.load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar']
35
+ #
36
+ def self.load_stream yaml
37
+ parse_stream(yaml).children.map { |child| child.to_blamed_ruby }
38
+ end
39
+
40
+
41
+ ###
42
+ # Load the document contained in +filename+. Returns the yaml contained in
43
+ # +filename+ as a ruby object
44
+
45
+ def self.load_file filename
46
+ self.load File.open(filename)
47
+ end
48
+
49
+
50
+ ###
51
+ # Parse a YAML string in +yaml+. Returns the first object of a YAML AST.
52
+ #
53
+ # Example:
54
+ #
55
+ # Psych.parse("---\n - a\n - b") # => #<Psych::Nodes::Sequence:0x00>
56
+ #
57
+ # See Psych::Nodes for more information about YAML AST.
58
+
59
+ def self.parse yaml
60
+ children = parse_stream(BlamedIO.new(yaml)).children
61
+ children.empty? ? false : children.first.children.first
62
+ end
63
+
64
+
65
+ ###
66
+ # Parse a file at +filename+. Returns the YAML AST.
67
+
68
+ def self.parse_file filename
69
+ File.open filename do |f|
70
+ parse f
71
+ end
72
+ end
73
+
74
+
75
+ ###
76
+ # Returns a default parser
77
+
78
+ def self.parser blaml
79
+ Psych::Parser.new(TreeBuilder.new(blaml))
80
+ end
81
+
82
+
83
+ ###
84
+ # Parse a YAML blame string in +yaml+.
85
+ # Returns the full AST for the YAML document with blame metadata.
86
+ # See Psych::parse_stream for more info.
87
+
88
+ def self.parse_stream blaml
89
+ parser = self.parser blaml
90
+ parser.parse blaml
91
+ parser.handler.root
92
+ end
93
+ end
@@ -0,0 +1,165 @@
1
+ class Blaml
2
+
3
+ ##
4
+ # IO wrapper that removes and parses blame data and makes it
5
+ # available as metadata
6
+
7
+ class BlamedIO
8
+
9
+ DATE_MATCHER =
10
+ %r{(\d+[-\s:]){6}[^\s]+}
11
+
12
+ META_MATCHER =
13
+ %r{([^\s]+)\s+([^\s]+)\s+\(([^\s]+)\s+(#{DATE_MATCHER})\s+(\d+)\)\s}
14
+
15
+ # Accessor for currently available metadata.
16
+ attr_reader :metadata
17
+
18
+
19
+ ##
20
+ # Create new BlamedIO with a string or IO object.
21
+
22
+ def initialize io
23
+ io = StringIO.new io if String === io
24
+
25
+ @io = io
26
+ @metadata = []
27
+ @meta_mode = true
28
+ end
29
+
30
+
31
+ %w{close closed? eof eof? rewind}.each do |meth|
32
+ class_eval "def #{meth}; @io.#{meth}; end"
33
+ end
34
+
35
+
36
+ ##
37
+ # Retrieves the metadata for a given string that matches
38
+ # the next line in the IO stream and deletes it permanently.
39
+
40
+ def shift_metadata_for str
41
+ meta, line = @metadata.first
42
+
43
+ if line.nil? || line.empty? || line[0..0] == '#'
44
+ @metadata.shift
45
+ return meta if str.empty?
46
+ end
47
+
48
+ meta, line = @metadata.first
49
+
50
+ if str.length > line.length
51
+
52
+ while str.include? @metadata.first[1] do
53
+ tmp_meta, line = @metadata.shift
54
+ meta = tmp_meta if
55
+ !meta || !meta[:updated_at] ||
56
+ meta && meta[:updated_at] &&
57
+ tmp_meta && tmp_meta[:updated_at] &&
58
+ tmp_meta[:updated_at] > meta[:updated_at]
59
+ end
60
+
61
+ else
62
+ @metadata.first[1] = line.split(str, 2).last.strip
63
+ @metadata.first[1].gsub!(%r{^:(\s+|$)}, "")
64
+ end
65
+
66
+ #puts "#{str} -> #{meta.inspect}"
67
+ meta
68
+ end
69
+
70
+
71
+ ##
72
+ # Removes leading spaces and dashes from a line of yaml data.
73
+
74
+ def sanitize_data str
75
+ str.to_s.strip.gsub(%r{^(-\s)+}, "")
76
+ end
77
+
78
+
79
+ ##
80
+ # Read single char as an integer.
81
+
82
+ def getc
83
+ read(1).unpack('c')[0]
84
+ end
85
+
86
+
87
+ ##
88
+ # Read from the IO instance and parse the blame data.
89
+
90
+ def read length=nil, buffer=nil
91
+ buffer ||= ""
92
+ meta_line = ""
93
+
94
+ until buffer.length == length || @io.eof?
95
+ if @meta_mode
96
+ read_meta
97
+ @meta_mode = false
98
+ end
99
+
100
+ char = @io.getc
101
+ buffer << char
102
+ meta_line << char
103
+
104
+ if buffer[-1..-1] == $/
105
+ @meta_mode = true
106
+ @metadata.last << sanitize_data(meta_line)
107
+ meta_line = ""
108
+ end
109
+ end
110
+
111
+ #puts @metadata.map{|i| i.inspect}.join("\n")
112
+ buffer
113
+ end
114
+
115
+
116
+ ##
117
+ # Read a single line.
118
+
119
+ def readline sep_string=$/
120
+ buffer = ""
121
+ until buffer[-1..-1] == sep_string || @io.eof?
122
+ buffer << read(1)
123
+ end
124
+
125
+ buffer
126
+ end
127
+
128
+ alias gets readline
129
+
130
+
131
+ ##
132
+ # Reads blame metadata.
133
+
134
+ def read_meta
135
+ buffer = ""
136
+
137
+ start_pos = @io.pos
138
+
139
+ until buffer =~ META_MATCHER do
140
+
141
+ # Got to the end of line with no metadata.
142
+ # Assume we're reading a regular yml IO.
143
+ if @io.eof? || buffer =~ %r{#{$/}$}
144
+ @io.pos = start_pos
145
+ @metadata << [nil]
146
+ return
147
+ end
148
+
149
+ buffer << @io.getc
150
+ end
151
+
152
+ meta_key = {
153
+ :file => $2,
154
+ :line => $6.to_i,
155
+ :author => $3,
156
+ :commit => $1,
157
+ :updated_at => Time.parse($4)
158
+ }
159
+
160
+ @metadata << [meta_key]
161
+
162
+ true
163
+ end
164
+ end
165
+ end
data/lib/blaml/meta.rb ADDED
@@ -0,0 +1,190 @@
1
+ class Blaml
2
+
3
+
4
+ ##
5
+ # Simple wrapper class to assign metadata to object instances.
6
+
7
+ class MetaNode
8
+
9
+
10
+ # The object to assign metadata to.
11
+ attr_reader :value
12
+
13
+ # The metadata assigned to the wrapped object.
14
+ attr_writer :meta
15
+
16
+ ##
17
+ # Create a new MetaNode with the value to wrap and optional metadata.
18
+
19
+ def initialize value, meta=nil
20
+ @value = value
21
+ @meta = meta
22
+ end
23
+
24
+
25
+ ##
26
+ # Checks for equality against the value attribute.
27
+
28
+ def == obj
29
+ case obj
30
+ when MetaNode then obj.value == @value
31
+ else
32
+ @value == obj
33
+ end
34
+ end
35
+
36
+
37
+ ##
38
+ # Accessor for the meta attribute.
39
+ # Overridden in MetaArray and MetaHash classes.
40
+
41
+ def meta
42
+ @meta
43
+ end
44
+
45
+
46
+ %w{to_s to_i to_f to_sym inspect}.each do |meth|
47
+ class_eval "def #{meth}; @value.#{meth}; end"
48
+ end
49
+
50
+
51
+ ##
52
+ # Sends non-defined methods to the value attribute
53
+
54
+ def method_missing name, *args, &block
55
+ @value.send name, *args, &block
56
+ end
57
+
58
+
59
+ ##
60
+ # Accessor for the value attribute.
61
+ # Overridden in MetaArray and MetaHash classes.
62
+
63
+ def to_value
64
+ @value
65
+ end
66
+ end
67
+
68
+
69
+ ##
70
+ # Wraps Array instances with metadata.
71
+
72
+ class MetaArray < MetaNode
73
+
74
+ ##
75
+ # Returns the child metadata with the most recent change.
76
+
77
+ def meta
78
+ meta = nil
79
+
80
+ @value.each do |val|
81
+ next unless val.respond_to? :meta
82
+
83
+ meta = val.meta if !meta ||
84
+ meta && val.meta && val.meta[:updated_at] > meta[:updated_at]
85
+ end
86
+
87
+ meta
88
+ end
89
+
90
+
91
+ ##
92
+ # Strips MetaNode wrapper from the value and calls to_value
93
+ # on all array elements.
94
+
95
+ def to_value
96
+ @value.map{|v| v.respond_to?(:to_value) ? v.to_value : v }
97
+ end
98
+ end
99
+
100
+
101
+ ##
102
+ # Wraps Hash instances with metadata.
103
+
104
+ class MetaHash < MetaNode
105
+
106
+ ##
107
+ # Access a value of the wrapped hash.
108
+
109
+ def [] key
110
+ @value.each{|k,v| return v if k == key}
111
+ end
112
+
113
+
114
+ ##
115
+ # Assign a value of the wrapped hash.
116
+
117
+ def []= key, val
118
+ @value.each{|k,v| @value[k] = val and return if k == key}
119
+ end
120
+
121
+
122
+ ##
123
+ # Merge with and modify the wrapped hash.
124
+
125
+ def merge! hash
126
+ hash.each do |k,v|
127
+ key = @value.keys.find{|vk| vk == k || k == vk } || k
128
+ @value.delete key
129
+ @value[k] = v
130
+ end
131
+
132
+ self
133
+ end
134
+
135
+
136
+ ##
137
+ # Create a new MetaHash merged with the given hash or metahash.
138
+
139
+ def merge hash
140
+ clone = @value.dup
141
+ hash.each do |k,v|
142
+ key = clone.keys.find{|vk| vk == k || k == vk } || k
143
+ clone.delete key
144
+ clone[k] = v
145
+ end
146
+
147
+ clone
148
+ end
149
+
150
+
151
+ ##
152
+ # Returns the child metadata with the most recent change.
153
+
154
+ def meta
155
+ meta = nil
156
+
157
+ @value.each do |key, val|
158
+ if val.respond_to? :meta
159
+ meta = val.meta if !meta ||
160
+ meta && val.meta && val.meta[:updated_at] > meta[:updated_at]
161
+ end
162
+
163
+ if key.respond_to? :meta
164
+ meta = key.meta if !meta ||
165
+ meta && val.meta && key.meta[:updated_at] > meta[:updated_at]
166
+ end
167
+ end
168
+
169
+ meta
170
+ end
171
+
172
+
173
+ ##
174
+ # Strips MetaNode wrapper from the value and calls to_value
175
+ # on all hash elements.
176
+
177
+ def to_value
178
+ clone = Hash.new
179
+
180
+ @value.each do |k, v|
181
+ key = k.respond_to?(:to_value) ? k.to_value : k
182
+ val = v.respond_to?(:to_value) ? v.to_value : v
183
+
184
+ clone[key] = val
185
+ end
186
+
187
+ clone
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,55 @@
1
+ class Blaml
2
+
3
+ ##
4
+ # Subclass Psych's to_ruby to build node values with metadata.
5
+
6
+ class ToRuby < Psych::Visitors::ToRuby
7
+
8
+ ##
9
+ # Wraps ruby object with MetaNode.
10
+ # See Psych::Visitors::ToRuby
11
+
12
+ def visit_Psych_Nodes_Scalar o
13
+ MetaNode.new super, o.meta
14
+ end
15
+
16
+
17
+ ##
18
+ # Wraps ruby Arrays with MetaArray.
19
+ # See Psych::Visitors::ToRuby
20
+
21
+ def visit_Psych_Nodes_Sequence o
22
+ seq = super
23
+ return seq unless Array === seq
24
+
25
+ MetaArray.new seq
26
+ end
27
+
28
+
29
+
30
+ ##
31
+ # Wraps ruby Arrays with MetaHash.
32
+ # See Psych::Visitors::ToRuby
33
+
34
+ def visit_Psych_Nodes_Mapping o
35
+ mapp = super
36
+ return mapp unless Hash === mapp
37
+
38
+ MetaHash.new mapp
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ class Psych::Nodes::Node
45
+
46
+ # Metadata accessor.
47
+ attr_accessor :meta
48
+
49
+ ##
50
+ # Tells Psych nodes to use Blaml::ToRuby.
51
+
52
+ def to_blamed_ruby
53
+ Blaml::ToRuby.new.accept self
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ class Blaml
2
+
3
+ class TreeBuilder < Psych::TreeBuilder
4
+
5
+ def initialize meta_binding
6
+ @meta_binding = meta_binding
7
+ super()
8
+ end
9
+
10
+
11
+ ##
12
+ # Sets the @meta instance variable to the metadata
13
+ # matched for the string object.
14
+ # Returns the passed instance.
15
+
16
+ def add_meta obj
17
+ obj.meta = @meta_binding.shift_metadata_for obj.value
18
+ obj
19
+ end
20
+
21
+
22
+ ##
23
+ # Calls parent and applies metadata to return value.
24
+
25
+ def scalar value, anchor, tag, plain, quoted, style
26
+ super
27
+ add_meta @last.children.last
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "blaml"
3
+
4
+ class TestBlaml < Test::Unit::TestCase
5
+ def test_sanity
6
+ assert true
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blaml
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Jeremie Castagna
9
+ autorequire: !!null
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-02-03 00:00:00.000000000 -08:00
13
+ default_executable: !!null
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ requirement: &2156751600 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.9.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2156751600
26
+ description: ! 'Blaml is a parser based on Psych (ruby 1.9 only) for yaml files that
27
+ have
28
+
29
+ been blamed with git.
30
+
31
+ Blaml parses the blamed yaml file and makes the blame metadata available on the
32
+
33
+ returned ruby object through a ''meta'' attribute.'
34
+ email:
35
+ - yaksnrainbows@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files:
39
+ - Manifest.txt
40
+ - History.rdoc
41
+ - README.rdoc
42
+ files:
43
+ - .autotest
44
+ - History.rdoc
45
+ - Manifest.txt
46
+ - README.rdoc
47
+ - Rakefile
48
+ - lib/blaml.rb
49
+ - lib/blaml/blamed_io.rb
50
+ - lib/blaml/meta.rb
51
+ - lib/blaml/to_ruby.rb
52
+ - lib/blaml/tree_builder.rb
53
+ - test/test_blaml.rb
54
+ - .gemtest
55
+ has_rdoc: true
56
+ homepage: http://github.com/yaksnrainbows/blaml
57
+ licenses: []
58
+ post_install_message: !!null
59
+ rdoc_options:
60
+ - --main
61
+ - README.rdoc
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>'
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.1
76
+ requirements: []
77
+ rubyforge_project: blaml
78
+ rubygems_version: 1.5.0
79
+ signing_key: !!null
80
+ specification_version: 3
81
+ summary: Blaml is a parser based on Psych (ruby 1.9 only) for yaml files that have
82
+ been blamed with git
83
+ test_files:
84
+ - test/test_blaml.rb