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,87 @@
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 Core
17
+ module Repositories
18
+
19
+ ##
20
+ # = CommonVersionedFileMethods
21
+ #
22
+ # These methods are common to all repositories, and this module is mixed into
23
+ # the AbstractLocalRepository class. This guarantees that all repositories will
24
+ # have these methods.
25
+ #
26
+ # No methods should be placed into this module unless it relies on methods in the
27
+ # general API for repositories.
28
+ module CommonVersionedFileMethods
29
+
30
+ def unified_diff_with(other_vf, opts = {})
31
+ Diffs::Mercurial::MercurialDiff.unified_diff(self.data, self.changeset.easy_date,
32
+ other_vf.data, other_vf.changeset.easy_date,
33
+ self.path, other_vf.path || "/dev/null",
34
+ false, opts)
35
+ end
36
+
37
+ ##
38
+ # Compares two versioned files - namely, their data.
39
+ #
40
+ # @param [VersionedFile] other what to compare to
41
+ # @return [Boolean] true if the two are the same
42
+ def ===(other)
43
+ self.path == other.path &&
44
+ self.data == other.data
45
+ end
46
+
47
+ # Returns if the file has been changed since its parent. Slow.
48
+ # If your implementation has a fast way of doing this, we recommend
49
+ # you override this method.
50
+ #
51
+ # @return [Boolean] has the file been changed since its parent?
52
+ def clean?
53
+ self === parents.first
54
+ end
55
+ opposite_method :dirty?, :clean?
56
+
57
+ ##
58
+ # User who committed this revision to this file
59
+ #
60
+ # @return [String] the user
61
+ def user; changeset.user; end
62
+
63
+ ##
64
+ # Date this revision to this file was committed
65
+ #
66
+ # @return [DateTime]
67
+ def date; changeset.date; end
68
+
69
+ ##
70
+ # The description of the commit that contained this file revision
71
+ #
72
+ # @return [String]
73
+ def description; changeset.description; end
74
+
75
+ ##
76
+ # The branch this tracked file belongs to
77
+ #
78
+ # @return [String]
79
+ def branch; changeset.branch; end
80
+
81
+ ##
82
+ # Working directory has no children!
83
+ def children; []; end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,94 @@
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 Core
17
+ module Repositories
18
+
19
+ ##
20
+ # This class is a generic "repo picker". It will return a Repository object
21
+ # for the given path (if there is one), and is capable of telling you if there
22
+ # is a repository of its type in the given directory.
23
+ #
24
+ # Amp started off with a MercurialPicker - it knows how to find Mercurial repos.
25
+ #
26
+ # When amp runs, it iterates over all subclasses of AbstractRepoPicker, in no
27
+ # guaranteed order (so don't stomp on existing pickers!), calling #repo_in_dir? .
28
+ # If only one picker returns +true+, then that picker is used for opening the
29
+ # repository. If more than one returns +true+, the user's configuration is consulted.
30
+ # If nothing is found then, then the user is prompted. When the final picker has been
31
+ # chosen, its #pick method is called to get the repository for the directory/URL.
32
+ #
33
+ # This is an "abstract" class, in that all the methods will raise a NotImplementedError
34
+ # if you try to call them.
35
+ #
36
+ # You must subclass this class, or your custom repo code will be ignored by Amp's
37
+ # dispatch system.
38
+ class GenericRepoPicker
39
+ @all_pickers = []
40
+ class << self
41
+ include Enumerable
42
+ attr_accessor :all_pickers
43
+
44
+ ##
45
+ # Iterate over every RepoPicker in the system.
46
+ def each(*args, &block)
47
+ @all_pickers.each(*args, &block)
48
+ end
49
+
50
+ private
51
+
52
+ ##
53
+ # Assuming you're a Ruby Newbie reading this: The interpreter calls
54
+ # {{inherited}} on the superclass when a class is subclassed.
55
+ #
56
+ # For the non-newbies, we're just keeping track of all subclasses so
57
+ # we know what classes we can pick from.
58
+ #
59
+ # @param [Class] subclass the class that just inherited from this class
60
+ def inherited(subclass)
61
+ all_pickers << subclass.new
62
+ end
63
+ end
64
+
65
+ ##
66
+ # Returns whether or not there is a repository in the given directory. This
67
+ # picker should only be responsible for one type of repository - git, svn, hg,
68
+ # etc. The given path could be deep inside a repository, and must look in parent
69
+ # directories for the root of the VCS repository.
70
+ #
71
+ # @param [String] path the path in which to search for a repository
72
+ # @return [Boolean] is there a repository in this directory (or parent directories)?
73
+ def repo_in_dir?(path)
74
+ raise NotImplementedError.new("repo_in_dir? must be implemented in a concrete subclass.")
75
+ end
76
+ alias_method :repo_in_url?, :repo_in_dir?
77
+
78
+ ##
79
+ # Returns a repository object for the given path. Should respond to the standard repository
80
+ # API to the best of its ability, and raise a CapabilityError if asked to do something it
81
+ # cannot do from the API.
82
+ #
83
+ # @param [AmpConfig] config the configuration of the current environment, loaded from
84
+ # appropriate configuration files
85
+ # @param [String] path the path/URL in which to open the repository.
86
+ # @param [Boolean] create should a repository be created in the given directory/URL?
87
+ # @return [AbstractLocalRepository] the repository for the given URL
88
+ def pick(config, path = '', create = false)
89
+ raise NotImplementedError.new("repo_in_dir? must be implemented in a concrete subclass.")
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,41 @@
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 Core
17
+ module Repositories
18
+ class RepoError < StandardError; end
19
+
20
+ ##
21
+ # Picks a repository provided a user configuration, a path, and whether
22
+ # we have permission to create the repository.
23
+ #
24
+ # This is the entry point for repo-independent command dispatch. We need
25
+ # to know if the given repository is of a particular type. We iterate
26
+ # over all known types, ask each type "does this path look like your kind
27
+ # of repo?", and if it says "yes", use that type.
28
+ #
29
+ # Note: this does NOT handle when there are two types of repositories in
30
+ # a given directory.
31
+ def self.pick(config, path='', create=false)
32
+ GenericRepoPicker.each do |picker|
33
+ return picker.pick(config, path, create) if picker.repo_in_dir?(path)
34
+ end
35
+
36
+ # We have found... nothing
37
+ nil
38
+ end # def self.pick
39
+ end # module Repositories
40
+ end # module Core
41
+ end # module Amp
@@ -0,0 +1,46 @@
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 Core
17
+ module Support
18
+ # This module is a set of string functions that we use frequently.
19
+ # They sued to be monkey-patched onto the String class, but we don't
20
+ # do that anymore.
21
+ module EncodingUtils
22
+ module_function
23
+
24
+ ##
25
+ # Used for byte-swapping a 64-bit double long.
26
+ # Unfortuantely, this will invoke bignum logic, which is ridiculously slow.
27
+ # That's why we have a C extension.
28
+ #
29
+ # If the system is little endian, we work some magic. If the system is big
30
+ # endian, we just return self.
31
+ #
32
+ # @return [Integer] the number swapped as if it were a 64-bit integer
33
+ def network_to_host_64(src)
34
+ if Amp::Core::Support::Platform::SYSTEM[:endian] == :little
35
+ ((src >> 56)) | ((src & 0x00FF000000000000) >> 40) |
36
+ ((src & 0x0000FF0000000000) >> 24) | ((src & 0x000000FF00000000) >> 8 ) |
37
+ ((src & 0x00000000FF000000) << 8 ) | ((src & 0x0000000000FF0000) << 24) |
38
+ ((src & 0x000000000000FF00) << 40) | ((src & 0x00000000000000FF) << 56)
39
+ else
40
+ src
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,92 @@
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
+ module Amp
15
+ module Core
16
+ module Support
17
+ # This module is used to set a few constants regarding which OS,
18
+ # implementation, and architecture the user is running.
19
+ module Platform
20
+ if RUBY_PLATFORM =~ /darwin/i
21
+ OS = :unix
22
+ IMPL = :macosx
23
+ elsif RUBY_PLATFORM =~ /linux/i
24
+ OS = :unix
25
+ IMPL = :linux
26
+ elsif RUBY_PLATFORM =~ /freebsd/i
27
+ OS = :unix
28
+ IMPL = :freebsd
29
+ elsif RUBY_PLATFORM =~ /netbsd/i
30
+ OS = :unix
31
+ IMPL = :netbsd
32
+ elsif RUBY_PLATFORM =~ /mswin/i
33
+ OS = :win32
34
+ IMPL = :mswin
35
+ elsif RUBY_PLATFORM =~ /cygwin/i
36
+ OS = :unix
37
+ IMPL = :cygwin
38
+ elsif RUBY_PLATFORM =~ /mingw/i
39
+ OS = :win32
40
+ IMPL = :mingw
41
+ elsif RUBY_PLATFORM =~ /bccwin/i
42
+ OS = :win32
43
+ IMPL = :bccwin
44
+ elsif RUBY_PLATFORM =~ /wince/i
45
+ OS = :win32
46
+ IMPL = :wince
47
+ elsif RUBY_PLATFORM =~ /vms/i
48
+ OS = :vms
49
+ IMPL = :vms
50
+ elsif RUBY_PLATFORM =~ /os2/i
51
+ OS = :os2
52
+ IMPL = :os2 # maybe there is some better choice here?
53
+ else
54
+ OS = :unknown
55
+ IMPL = :unknown
56
+ end
57
+
58
+ if RUBY_PLATFORM =~ /(i\d86)/i
59
+ ARCH = :x86
60
+ elsif RUBY_PLATFORM =~ /(x86_64|amd64)/i
61
+ ARCH = :x86_64
62
+ elsif RUBY_PLATFORM =~ /ia64/i
63
+ ARCH = :ia64
64
+ elsif RUBY_PLATFORM =~ /powerpc/i
65
+ ARCH = :powerpc
66
+ elsif RUBY_PLATFORM =~ /alpha/i
67
+ ARCH = :alpha
68
+ elsif RUBY_PLATFORM =~ /universal/i
69
+ ARCH = :universal
70
+ else
71
+ ARCH = :unknown
72
+ end
73
+ SYSTEM = {}
74
+
75
+ # Figures up the system is running on a little or big endian processor
76
+ # architecture, and upates the SYSTEM[] hash in the Support module.
77
+ def self.determine_endianness
78
+ num = 0x12345678
79
+ native = [num].pack('l')
80
+ netunpack = native.unpack('N')[0]
81
+ if num == netunpack
82
+ SYSTEM[:endian] = :big
83
+ else
84
+ SYSTEM[:endian] = :little
85
+ end
86
+ end
87
+
88
+ determine_endianness
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,143 @@
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 Core
17
+ module Support
18
+ # This creates an opener that prepends a directory to all
19
+ # File opening, allowing repository formats to worry only
20
+ # about file paths relative to the repository root.
21
+ class RootedOpener
22
+ attr_reader :root
23
+ attr_accessor :create_mode
24
+ attr_accessor :default
25
+
26
+ alias_method :base, :root
27
+
28
+ ##
29
+ # Creates a new opener with a root of +base+, and also set to open files in
30
+ # the .hg subdirectory. If you set .default = :open_file, it will no longer
31
+ # open files in the .hg subdir.
32
+ #
33
+ # @param [String] base the root directory of the repository this opener will be
34
+ # used on
35
+ def initialize(base)
36
+ @root = File.expand_path base
37
+ @create_mode = nil
38
+ @default = :open_hg
39
+ end
40
+
41
+ ##
42
+ # Returns the path to the opener's root.
43
+ #
44
+ # @return path to the opener's root, as an absolute path.
45
+ def path
46
+ case @default
47
+ when :open_file
48
+ "#{root}/"
49
+ when :open_hg
50
+ "#{root}/.hg/"
51
+ when :open_git
52
+ "#{root}/.git"
53
+ else
54
+ raise abort "Unknown opener format #{@default.inspect}"
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Read the file passed in with mode 'r'.
60
+ # Synonymous with File.open(+file+, 'r') {|f| f.read } and
61
+ # File.read(+file+)
62
+ #
63
+ # @param [String] file the relative path to the file we're opening
64
+ # @return [String] the contents of the file
65
+ def read(file)
66
+ res = nil
67
+ open(file, 'r') {|f| res = f.read }
68
+ res
69
+ end
70
+ alias_method :contents, :read
71
+
72
+ ##
73
+ # Opens up the given file, exactly like you would do with File.open.
74
+ # The parameters are the same. Defaults to opening a file in the .hg/
75
+ # folder, but if @default == :open_file, will open it from the working
76
+ # directory.
77
+ #
78
+ # If the mode includes write privileges, then the write will use an
79
+ # atomic temporary file.
80
+ #
81
+ # @param [String] file the path to the file to open
82
+ # @param [String] mode the read/write mode to open with (standard
83
+ # C choices here)
84
+ # @yield Can yield the opened file if the block form is used
85
+ def open(file, mode='r', &block)
86
+ if @default == :open_file
87
+ open_file file, mode, &block
88
+ else
89
+ open_hg file, mode, &block
90
+ end
91
+ end
92
+
93
+ def join(file)
94
+ File.join(root, file)
95
+ end
96
+
97
+ ##
98
+ # Opens a file in the .hg repository using +@root+. This method
99
+ # operates atomically, and ensures that the file is always closed
100
+ # after use. The temporary files (while being atomically written)
101
+ # are stored in "#{@root}/.hg", and are deleted after use. If only
102
+ # a read is being done, it instead uses Kernel::open instead of
103
+ # File::amp_atomic_write.
104
+ #
105
+ # @param [String] file the file to open
106
+ # @param [String] mode the mode with which to open the file ("w", "r", "a", ...)
107
+ # @yield [file] code to run on the file
108
+ # @yieldparam [File] file the opened file
109
+ def open_hg(file, mode='w', &block)
110
+ open_up_file File.join(root, ".hg"), file, mode, &block
111
+ end
112
+
113
+ ##
114
+ # Opens a file in the repository (not in .hg).
115
+ # Writes are done atomically, and reads are efficiently
116
+ # done with Kernel::open. THIS IS NOT +open_up_file+!!!
117
+ #
118
+ # @param [String] file the file to open
119
+ # @param [String] mode the mode with which to open the file ("w", "r", "a", ...)
120
+ # @yield [file] code to run on the file
121
+ # @yieldparam [File] file the opened file
122
+ def open_file(file, mode='w', &block)
123
+ open_up_file root, file, mode, &block
124
+ end
125
+
126
+ ##
127
+ # This does the actual opening of a file.
128
+ #
129
+ # @param [String] dir This dir is where the temp file is made, but ALSO
130
+ # the parent dir of +file+
131
+ # @param [String] file Just the file name. It must exist at "#{dir}/#{file}"
132
+ def open_up_file(dir, file, mode, &block)
133
+ path = File.join dir, file
134
+ if mode == 'r' # if we're doing a read, make this super snappy
135
+ Kernel::open path, mode, &block
136
+ else # we're doing a write
137
+ File::amp_atomic_write path, mode, @create_mode, dir, &block
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end