amp-core 0.1.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.
Files changed (46) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +67 -0
  7. data/VERSION +1 -0
  8. data/features/amp-core.feature +9 -0
  9. data/features/step_definitions/amp-core_steps.rb +0 -0
  10. data/features/support/env.rb +4 -0
  11. data/lib/amp-core.rb +53 -0
  12. data/lib/amp-core/command_ext/repository_loading.rb +31 -0
  13. data/lib/amp-core/repository/abstract/abstract_changeset.rb +113 -0
  14. data/lib/amp-core/repository/abstract/abstract_local_repo.rb +208 -0
  15. data/lib/amp-core/repository/abstract/abstract_staging_area.rb +202 -0
  16. data/lib/amp-core/repository/abstract/abstract_versioned_file.rb +116 -0
  17. data/lib/amp-core/repository/abstract/common_methods/changeset.rb +185 -0
  18. data/lib/amp-core/repository/abstract/common_methods/local_repo.rb +293 -0
  19. data/lib/amp-core/repository/abstract/common_methods/staging_area.rb +248 -0
  20. data/lib/amp-core/repository/abstract/common_methods/versioned_file.rb +87 -0
  21. data/lib/amp-core/repository/generic_repo_picker.rb +94 -0
  22. data/lib/amp-core/repository/repository.rb +41 -0
  23. data/lib/amp-core/support/encoding_utils.rb +46 -0
  24. data/lib/amp-core/support/platform_utils.rb +92 -0
  25. data/lib/amp-core/support/rooted_opener.rb +143 -0
  26. data/lib/amp-core/support/string_utils.rb +86 -0
  27. data/lib/amp-core/templates/git/blank.log.erb +18 -0
  28. data/lib/amp-core/templates/git/default.log.erb +18 -0
  29. data/lib/amp-core/templates/mercurial/blank.commit.erb +23 -0
  30. data/lib/amp-core/templates/mercurial/blank.log.erb +18 -0
  31. data/lib/amp-core/templates/mercurial/default.commit.erb +23 -0
  32. data/lib/amp-core/templates/mercurial/default.log.erb +26 -0
  33. data/lib/amp-core/templates/template.rb +202 -0
  34. data/spec/amp-core_spec.rb +11 -0
  35. data/spec/command_ext_specs/repository_loading_spec.rb +64 -0
  36. data/spec/command_ext_specs/spec_helper.rb +1 -0
  37. data/spec/repository_specs/repository_spec.rb +41 -0
  38. data/spec/repository_specs/spec_helper.rb +1 -0
  39. data/spec/spec.opts +1 -0
  40. data/spec/spec_helper.rb +14 -0
  41. data/spec/support_specs/encoding_utils_spec.rb +69 -0
  42. data/spec/support_specs/platform_utils_spec.rb +33 -0
  43. data/spec/support_specs/spec_helper.rb +1 -0
  44. data/spec/support_specs/string_utils_spec.rb +44 -0
  45. data/test/test_templates.rb +81 -0
  46. metadata +157 -0
@@ -0,0 +1,86 @@
1
+ ##################################################################
2
+ # Licensing Information #
3
+ # #
4
+ # The following code is licensed, as standalone code, under #
5
+ # the Ruby License, unless otherwise directed within the code. #
6
+ # #
7
+ # For information on the license of this code when distributed #
8
+ # with and used in conjunction with the other modules in the #
9
+ # Amp project, please see the root-level LICENSE file. #
10
+ # #
11
+ # © Michael J. Edgar and Ari Brown, 2009-2010 #
12
+ # #
13
+ ##################################################################
14
+ require 'digest'
15
+
16
+ module Amp
17
+ module Core
18
+ module Support
19
+ # This module is a set of string functions that we use frequently.
20
+ # They sued to be monkey-patched onto the String class, but we don't
21
+ # do that anymore.
22
+ module StringUtils
23
+ module_function
24
+
25
+ # Converts this text into hex. each letter is replaced with
26
+ # it's hex counterpart
27
+ def hexlify(src)
28
+ str = ""
29
+ src.each_byte do |i|
30
+ str << i.to_s(16).rjust(2, "0")
31
+ end
32
+ str
33
+ end
34
+
35
+ if RUBY_VERSION < "1.9"
36
+ # Returns the value of the first byte of the string.
37
+ #
38
+ # @return [Fixnum, 0 <= x < 256] The value of the first byte.
39
+ def ord(str)
40
+ raise ArgumentError.new('empty string') if str.empty?
41
+ str[0]
42
+ end
43
+ ##
44
+ # Converts a string of hex into the binary values it represents. This is used for
45
+ # when we store a node ID in a human-readable format, and need to convert it back.
46
+ #
47
+ # @example StringUtils.unhexlify("DEADBEEF") #=> "\336\255\276\357"
48
+ # @return [String] the string decoded from hex form
49
+ def unhexlify(src)
50
+ str = "\000" * (src.size/2)
51
+ c = 0
52
+ (0..src.size-2).step(2) do |i|
53
+ hex = src[i,2].to_i(16)
54
+ str[c] = hex
55
+ c += 1
56
+ end
57
+ str
58
+ end
59
+ else
60
+ # Returns the value of the first byte of the string.
61
+ #
62
+ # @return [Fixnum, 0 <= x < 256] The value of the first byte.
63
+ def ord(str)
64
+ raise ArgumentError.new('empty string') if str.empty?
65
+ str.ord
66
+ end
67
+
68
+ def unhexlify(str)
69
+ str = "\000" * (src.size/2)
70
+ c = 0
71
+ (0..src.size-2).step(2) do |i|
72
+ hex = src[i,2].to_i(16)
73
+ str[c] = hex.chr
74
+ c += 1
75
+ end
76
+ str
77
+ end
78
+ end
79
+
80
+ def sha1(str)
81
+ Digest::SHA1.new.update(str)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,18 @@
1
+ <%# This is the blank template file. Here's some information about the
2
+ variables you have access to in your ERb file!
3
+
4
+ Note: This template is for when you want to print changesets.
5
+
6
+ revision: the revision number of the changeset you're printing
7
+ change_node: the node ID of the changeset. This will be in binary,
8
+ so you'll probably want to use hexlify to make it hexy.
9
+ branch: the name of the branch this changeset belongs to
10
+ parents: parents of the revision, as [index, node (as hex)]
11
+ username: the user who committed this changeset
12
+ date: the date this was committed
13
+ description: the message connected to the changeset
14
+ config: the entire configuration of the entire program! This includes
15
+ switches such as :verbose.
16
+
17
+ Be sure to take a look at the default template, to get some ideas!
18
+ %>
@@ -0,0 +1,18 @@
1
+ changeset: <%= revision %>
2
+ <%- parents.each do |rent| -%>
3
+ parent: <%= "#{rent.short_name}" %>
4
+ <%- end -%>
5
+ user: <%= username %>
6
+ date: <%= date %>
7
+ <%- if config[:verbose] -%>
8
+ files: <%= files.join(" ") %>
9
+ <%- end -%>
10
+ <%- if description && description.any?
11
+ if config[:verbose] -%>
12
+ description:
13
+ <%= description %>
14
+ <%- else -%>
15
+ summary: <%= description.split_lines_better.first.strip %>
16
+ <%- end -%>
17
+ <%- end -%>
18
+
@@ -0,0 +1,23 @@
1
+
2
+
3
+ AMP: Enter commit message. Lines beginning with 'AMP:' are removed.
4
+ AMP: --
5
+ AMP: <%= user %>
6
+ <%- if parents[1] != NULL_REV -%>
7
+ AMP: branch merge
8
+ <%- end -%>
9
+ <%- if branch -%>
10
+ AMP: branch '<%= branch %>'
11
+ <%- end -%>
12
+ <%- added.each do |file| -%>
13
+ AMP: added <%= file %>
14
+ <%- end -%>
15
+ <%- updated.each do |file| -%>
16
+ AMP: changed <%= file %>
17
+ <%- end -%>
18
+ <%- removed.each do |file| -%>
19
+ AMP: removed <%= file %>
20
+ <%- end -%>
21
+ <%- if added.size + updated.size + removed.size == 0 -%>
22
+ AMP: no files changed
23
+ <%- end -%>
@@ -0,0 +1,18 @@
1
+ <%# This is the blank template file. Here's some information about the
2
+ variables you have access to in your ERb file!
3
+
4
+ Note: This template is for when you want to print changesets.
5
+
6
+ revision: the revision number of the changeset you're printing
7
+ change_node: the node ID of the changeset. This will be in binary,
8
+ so you'll probably want to use hexlify to make it hexy.
9
+ branch: the name of the branch this changeset belongs to
10
+ parents: parents of the revision, as [index, node (as hex)]
11
+ username: the user who committed this changeset
12
+ date: the date this was committed
13
+ description: the message connected to the changeset
14
+ config: the entire configuration of the entire program! This includes
15
+ switches such as :verbose.
16
+
17
+ Be sure to take a look at the default template, to get some ideas!
18
+ %>
@@ -0,0 +1,23 @@
1
+
2
+
3
+ AMP: Enter commit message. Lines beginning with 'AMP:' are removed.
4
+ AMP: --
5
+ AMP: <%= user %>
6
+ <%- if parents[1] != nil -%>
7
+ AMP: branch merge
8
+ <%- end -%>
9
+ <%- if branch -%>
10
+ AMP: branch '<%= branch %>'
11
+ <%- end -%>
12
+ <%- added.each do |file| -%>
13
+ AMP: added <%= file %>
14
+ <%- end -%>
15
+ <%- updated.each do |file| -%>
16
+ AMP: changed <%= file %>
17
+ <%- end -%>
18
+ <%- removed.each do |file| -%>
19
+ AMP: removed <%= file %>
20
+ <%- end -%>
21
+ <%- if added.size + updated.size + removed.size == 0 -%>
22
+ AMP: no files changed
23
+ <%- end -%>
@@ -0,0 +1,26 @@
1
+ <%- if config[:verbose] -%>
2
+ changeset: <%= revision %>:<%= change_node.hexlify %>
3
+ <%- else -%>
4
+ changeset: <%= revision %>:<%= change_node[0..5].hexlify %>
5
+ <%- end -%>
6
+ branch: <%= branch %>
7
+ <%- parents.each do |index, node| -%>
8
+ parent: <%= "#{index}:#{node}" %>
9
+ <%- end -%>
10
+ <%- if cs_tags.any? -%>
11
+ tags: <%= cs_tags %>
12
+ <%- end -%>
13
+ user: <%= username %>
14
+ date: <%= date %>
15
+ <%- if config[:verbose] -%>
16
+ files: <%= files.join(" ") %>
17
+ <%- end -%>
18
+ <%- if description && description.any?
19
+ if config[:verbose] -%>
20
+ description:
21
+ <%= description %>
22
+ <%- else -%>
23
+ summary: <%= description.split_lines_better.first.strip %>
24
+ <%- end -%>
25
+ <%- end -%>
26
+
@@ -0,0 +1,202 @@
1
+ ##################################################################
2
+ # Licensing Information #
3
+ # #
4
+ # The following code is licensed, as standalone code, under #
5
+ # the Ruby License, unless otherwise directed within the code. #
6
+ # #
7
+ # For information on the license of this code when distributed #
8
+ # with and used in conjunction with the other modules in the #
9
+ # Amp project, please see the root-level LICENSE file. #
10
+ # #
11
+ # © Michael J. Edgar and Ari Brown, 2009-2010 #
12
+ # #
13
+ ##################################################################
14
+
15
+ module Amp
16
+ module Support
17
+ ##
18
+ # = Template
19
+ # @author Michael Edgar
20
+ #
21
+ # Class representing a template in the amp system
22
+ class Template
23
+
24
+ @all_templates = Hash.new {|h, k| h[k] = {} }
25
+ class << self
26
+ attr_accessor :all_templates
27
+
28
+ ##
29
+ # Returns the template with the given name.
30
+ #
31
+ # @param [String, Symbol, #to_sym] template the name of the template to retrieve
32
+ # @return [Template] the template with the given name
33
+ def [](type, template)
34
+ ensure_templates_loaded
35
+ return all_templates[type.to_sym][template.to_sym] || all_templates[:all][template.to_sym]
36
+ end
37
+
38
+ ##
39
+ # Registers a template with the Amp system. Should have a unique name.
40
+ #
41
+ # @param [String, Symbol, #to_sym] name the name of the template. Should be unique.
42
+ # @param [Template] template the template to register.
43
+ def register(type, name, template)
44
+ all_templates[type.to_sym][name.to_sym] = template
45
+ end
46
+
47
+ ##
48
+ # Unregisters a template with the Amp system. If the name is not found, an exception
49
+ # is thrown.
50
+ #
51
+ # @param [String, Symbol, #to_sym] name the name of the template to remove from
52
+ # the system.
53
+ def unregister(type, name)
54
+ raise ArgumentError.new("Unknown template: #{name}") unless all_templates[type.to_sym][name.to_sym]
55
+ all_templates[type.to_sym].delete name.to_sym
56
+ end
57
+
58
+ ##
59
+ # Returns whether any templates have been loaded. Used for lazy loading of templates.
60
+ #
61
+ # @return [Boolean] have the default templates, or any templates, been loaded?
62
+ def templates_loaded?
63
+ all_templates.any?
64
+ end
65
+
66
+ ##
67
+ # Registers the default templates. Separated into a method (instead of automatically
68
+ # run) because templates aren't used enough to justify the IO hit from loading them in.
69
+ def load_default_templates
70
+ Dir[File.expand_path(File.join(File.dirname(__FILE__), "**/*.erb"))].each do |f|
71
+ name = f.split('/').last.chomp('.erb').sub('.','-')
72
+ type = f.split('/')[-2]
73
+ FileTemplate.new(type, name, f)
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Makes sure the default templates have been loaded.
79
+ #
80
+ # About the use of instance_eval - this method could potentially be run more than once. There
81
+ # is no reason for it to ever run more than once. So we'll redefine it to do nothing.
82
+ def ensure_templates_loaded
83
+ load_default_templates
84
+ instance_eval do
85
+ def ensure_templates_loaded; end
86
+ end
87
+ true
88
+ end
89
+ end
90
+
91
+ attr_accessor :name, :renderer, :text
92
+
93
+ ##
94
+ # Creates a new template with the given values. The name is how you will reference the
95
+ # template using the --template option from the command line, so choose it wisely!
96
+ #
97
+ # @param [String, Symbol, #to_s] name the name of the template, which is invoked
98
+ # using --template
99
+ # @param [Symbol] type the type of the template. Indicates the renderer used.
100
+ # @param [String] text the text of the template, which presumably has some templating
101
+ # code to substitute in local variables and make a nice output system.
102
+ def initialize(type, name, renderer = :erb, text = "")
103
+ @name, @renderer, @text = name, renderer, text
104
+ Template.register(type, name, self)
105
+ end
106
+
107
+ ##
108
+ # Renders the template with the given local variables. Uses whichever templating engine
109
+ # you set. Note: if you use HAML, you'll need to have HAML installed. This is why none
110
+ # of the default templates use HAML.
111
+ #
112
+ # @param [Hash] locals the local variables passed to the template. Works for HAML so far,
113
+ # not for erb.
114
+ # @return [String] the parsed template
115
+ # @todo make locals work for ERb without bootleg hax
116
+ def render(locals = {}, render_binding = binding)
117
+ case renderer.to_sym
118
+ when :erb
119
+ require 'erb'
120
+ locals_assigns = locals.map { |k,v| "#{k} = locals[#{k.inspect}]" }
121
+ eval locals_assigns.join("\n"), render_binding
122
+
123
+ erb = ERB.new(text, 0, "-")
124
+ erb.result render_binding
125
+ when :haml
126
+ require 'rubygems'
127
+ require 'haml'
128
+ haml = Haml::Engine.new(text)
129
+ haml.render render_binding, locals
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ end
136
+
137
+ ##
138
+ # = FileTemplate
139
+ # Class for loading a template from a file and registering it in the system.
140
+ # Is smart enough that if the filename ends in a known extension, the appropriate
141
+ # renderer will be used. Otherwise, you will have to specify the renderer.
142
+ class FileTemplate < Template
143
+ KNOWN_EXTENSIONS = ["erb", "haml"]
144
+
145
+ attr_accessor :file
146
+
147
+ ##
148
+ # Initializes a new FileTemplate with a file used as input.
149
+ #
150
+ # The renderer is inferred from the file's extension.
151
+ def initialize(type, name, file, renderer = nil)
152
+ if renderer.nil?
153
+ renderer = KNOWN_EXTENSIONS.select {|ext| file.end_with? ext}.first
154
+ end
155
+ raise ArgumentError.new("No renderer specified for #{file.inspect}") if renderer.nil?
156
+ @file = file
157
+ super(type, name, renderer, File.read(file))
158
+ end
159
+
160
+ def save!
161
+ File.open(file, "w") { |out| out.write text }
162
+ end
163
+ end
164
+
165
+ ##
166
+ # = RawTemplate
167
+ # Class for specifying a tiny bit of text to be interpreted as ERb code, and
168
+ # using that as a template.
169
+ class RawERbTemplate < Template
170
+ def initialize(text)
171
+ text = "<%= #{text} %>" unless text.include?("<%")
172
+ super(:raw, "raw#{(rand * 65535).to_i}", :erb, text)
173
+ end
174
+ end
175
+
176
+ end
177
+ end
178
+
179
+ module Amp
180
+ module KernelMethods
181
+ ##
182
+ # @overload template(name, type, text)
183
+ # Shortcut Kernel-level method for adding templates quickly. A nice
184
+ # method for ampfiles.
185
+ # @param [String] name the name of the template
186
+ # @param [Symbol] type the template engine to use (such as :haml, :erb)
187
+ # @param [String] text the text of the template
188
+ # @overload template(name, text)
189
+ # Shortcut Kernel-level method for adding ERb templates quickly. A
190
+ # nice method for ampfiles.
191
+ # @param [String] name the name of the template
192
+ # @param [String] text the text of the template. Expected to be in ERb format.
193
+ def template(name, *args)
194
+ if args.size > 2 || args.empty?
195
+ raise ArgumentError.new('Usage of template: template(name, text)'+
196
+ 'or template(name, renderer, text)')
197
+ end
198
+ template = (args.size > 1) ? args[0] : :erb
199
+ Support::Template.new(:all, name, template, args.last)
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "amp-core" do
4
+ it "should have a plugin constant: Amp::Plugins::Core" do
5
+ result = nil
6
+ Amp::Plugins.class_eval do
7
+ result = const_defined?(:Core)
8
+ end
9
+ result.should be_true
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Amp::Core::RepositoryLoading do
4
+ describe "#has_repo" do
5
+ before do
6
+ @class_with = Class.new(Amp::Command::Base) do
7
+ has_repo
8
+ end
9
+ @class_without = Class.new(Amp::Command::Base) do
10
+ end
11
+ end
12
+
13
+ it "includes the module in a Command when it called" do
14
+ @class_with.included_modules.should
15
+ include(Amp::Core::RepositoryLoading)
16
+ end
17
+
18
+ it "doesn't include the module in a Command when not called" do
19
+ @class_without.included_modules.should_not
20
+ include(Amp::Core::RepositoryLoading)
21
+ end
22
+
23
+ it "extends the new class with ClassMethods when called" do
24
+ (class << @class_with; self; end).included_modules.should
25
+ include(Amp::Core::RepositoryLoading::ClassMethods)
26
+ end
27
+
28
+ it "doesn't extend the new class with ClassMethods when not called" do
29
+ (class << @class_without; self; end).included_modules.should_not
30
+ include(Amp::Core::RepositoryLoading::ClassMethods)
31
+ end
32
+
33
+ it "includes the new class with InstanceMethods when called" do
34
+ @class_with.included_modules.should
35
+ include(Amp::Core::RepositoryLoading::InstanceMethods)
36
+ end
37
+
38
+ it "doesn't include the new class with InstanceMethods when not called" do
39
+ @class_without.included_modules.should_not
40
+ include(Amp::Core::RepositoryLoading::InstanceMethods)
41
+ end
42
+
43
+ it "doesn't ever modify Amp::Command::Base" do
44
+ Amp::Command::Base.included_modules.should_not
45
+ include(Amp::Core::RepositoryLoading)
46
+ Amp::Command::Base.included_modules.should_not
47
+ include(Amp::Core::RepositoryLoading::InstanceMethods)
48
+ (class << Amp::Command::Base; self; end).included_modules.should_not
49
+ include(Amp::Core::RepositoryLoading::ClassMethods)
50
+ end
51
+
52
+ it "adds a :repository option when called" do
53
+ opts = @class_with.options
54
+ repo_opt = opts.find {|name, desc, config| name == :repository }
55
+ repo_opt.should_not be_nil
56
+ end
57
+
58
+ it "lacks a :repository option when not called" do
59
+ opts = @class_without.options
60
+ repo_opt = opts.find {|name, desc, config| name == :repository }
61
+ repo_opt.should be_nil
62
+ end
63
+ end
64
+ end