amp-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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