repo_manager 0.7.1

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 (116) hide show
  1. data/.gemfiles +115 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +3 -0
  5. data/.yardopts +11 -0
  6. data/Gemfile +11 -0
  7. data/Gemfile.lock +99 -0
  8. data/Guardfile +63 -0
  9. data/HISTORY.markdown +12 -0
  10. data/LICENSE +20 -0
  11. data/README.markdown +192 -0
  12. data/Rakefile +94 -0
  13. data/TODO.markdown +15 -0
  14. data/VERSION +1 -0
  15. data/bin/repo +151 -0
  16. data/cucumber.yml +28 -0
  17. data/examples/pc_saved_game_backup/.gitignore +2 -0
  18. data/examples/pc_saved_game_backup/INSTALL.markdown +420 -0
  19. data/examples/pc_saved_game_backup/README.markdown +108 -0
  20. data/examples/pc_saved_game_backup/remote/.gitignore +2 -0
  21. data/examples/pc_saved_game_backup/repo_manager/Gemfile +12 -0
  22. data/examples/pc_saved_game_backup/repo_manager/Gemfile.lock +66 -0
  23. data/examples/pc_saved_game_backup/repo_manager/assets/.gitignore +2 -0
  24. data/examples/pc_saved_game_backup/repo_manager/features/support/aruba.rb +15 -0
  25. data/examples/pc_saved_game_backup/repo_manager/features/support/env.rb +11 -0
  26. data/examples/pc_saved_game_backup/repo_manager/features/support/steps.rb +3 -0
  27. data/examples/pc_saved_game_backup/repo_manager/features/tasks/update.feature +144 -0
  28. data/examples/pc_saved_game_backup/repo_manager/global/default/asset.conf +2 -0
  29. data/examples/pc_saved_game_backup/repo_manager/repo.conf +64 -0
  30. data/examples/pc_saved_game_backup/repo_manager/tasks/.gitignore +0 -0
  31. data/examples/pc_saved_game_backup/repo_manager/tasks/remote.rb +57 -0
  32. data/examples/pc_saved_game_backup/repo_manager/tasks/update.rb +65 -0
  33. data/examples/pc_saved_game_backup/saved_games/hearts/save1 +1 -0
  34. data/examples/pc_saved_game_backup/saved_games/hearts/save2 +1 -0
  35. data/examples/pc_saved_game_backup/saved_games/mines/my_profile.ini +1 -0
  36. data/examples/pc_saved_game_backup/saved_games/mines/saves/save1 +1 -0
  37. data/examples/pc_saved_game_backup/saved_games/mines/saves/save2 +1 -0
  38. data/features/actions/git.feature +296 -0
  39. data/features/actions/help.feature +53 -0
  40. data/features/actions/list.feature +624 -0
  41. data/features/actions/path.feature +195 -0
  42. data/features/actions/status.feature +261 -0
  43. data/features/actions/task.feature +127 -0
  44. data/features/assets/configuration.feature +204 -0
  45. data/features/assets/rendering.feature +42 -0
  46. data/features/assets/user_attributes.feature +98 -0
  47. data/features/bin.feature +42 -0
  48. data/features/logger.feature +218 -0
  49. data/features/settings.feature +240 -0
  50. data/features/support/aruba.rb +15 -0
  51. data/features/support/env.rb +11 -0
  52. data/features/support/steps.rb +3 -0
  53. data/features/tasks/add/asset.feature +178 -0
  54. data/features/tasks/generate/init.feature +56 -0
  55. data/lib/repo_manager.rb +36 -0
  56. data/lib/repo_manager/actions.rb +8 -0
  57. data/lib/repo_manager/actions/action_helper.rb +39 -0
  58. data/lib/repo_manager/actions/app_action.rb +30 -0
  59. data/lib/repo_manager/actions/base_action.rb +296 -0
  60. data/lib/repo_manager/actions/git_action.rb +113 -0
  61. data/lib/repo_manager/actions/help_action.rb +52 -0
  62. data/lib/repo_manager/actions/list_action.rb +123 -0
  63. data/lib/repo_manager/actions/path_action.rb +22 -0
  64. data/lib/repo_manager/actions/status_action.rb +192 -0
  65. data/lib/repo_manager/actions/task_action.rb +71 -0
  66. data/lib/repo_manager/app.rb +116 -0
  67. data/lib/repo_manager/assets.rb +3 -0
  68. data/lib/repo_manager/assets/app_asset.rb +15 -0
  69. data/lib/repo_manager/assets/asset_accessors.rb +67 -0
  70. data/lib/repo_manager/assets/asset_configuration.rb +137 -0
  71. data/lib/repo_manager/assets/asset_manager.rb +72 -0
  72. data/lib/repo_manager/assets/base_asset.rb +199 -0
  73. data/lib/repo_manager/assets/repo_asset.rb +30 -0
  74. data/lib/repo_manager/core.rb +2 -0
  75. data/lib/repo_manager/core/array.rb +21 -0
  76. data/lib/repo_manager/core/hash.rb +83 -0
  77. data/lib/repo_manager/errors.rb +10 -0
  78. data/lib/repo_manager/extensions/hash.rb +86 -0
  79. data/lib/repo_manager/git.rb +2 -0
  80. data/lib/repo_manager/git/lib.rb +69 -0
  81. data/lib/repo_manager/git/status.rb +196 -0
  82. data/lib/repo_manager/logger.rb +39 -0
  83. data/lib/repo_manager/settings.rb +98 -0
  84. data/lib/repo_manager/tasks.rb +3 -0
  85. data/lib/repo_manager/tasks/add/asset.rb +213 -0
  86. data/lib/repo_manager/tasks/generate/init.rb +42 -0
  87. data/lib/repo_manager/tasks/generate/templates/config/repo.conf.tt +61 -0
  88. data/lib/repo_manager/tasks/generate/templates/init/assets/.gitignore +0 -0
  89. data/lib/repo_manager/tasks/generate/templates/init/global/default/asset.conf +2 -0
  90. data/lib/repo_manager/tasks/generate/templates/init/tasks/.gitignore +0 -0
  91. data/lib/repo_manager/tasks/task_manager.rb +166 -0
  92. data/lib/repo_manager/tasks/thor_helper.rb +29 -0
  93. data/lib/repo_manager/test/asset_steps.rb +19 -0
  94. data/lib/repo_manager/test/base_steps.rb +152 -0
  95. data/lib/repo_manager/test/repo_api.rb +41 -0
  96. data/lib/repo_manager/test/repo_steps.rb +83 -0
  97. data/lib/repo_manager/test/test_api.rb +88 -0
  98. data/lib/repo_manager/views.rb +2 -0
  99. data/lib/repo_manager/views/app_view.rb +15 -0
  100. data/lib/repo_manager/views/base_view.rb +137 -0
  101. data/lib/repo_manager/views/templates/css/basic.css +26 -0
  102. data/lib/repo_manager/views/templates/default.erb +40 -0
  103. data/lib/repo_manager/views/templates/default.slim +37 -0
  104. data/lib/repo_manager/views/view_helper.rb +55 -0
  105. data/repo_manager.gemspec +75 -0
  106. data/spec/basic_app/actions/action_helper_spec.rb +54 -0
  107. data/spec/basic_app/assets/base_asset_spec.rb +210 -0
  108. data/spec/basic_app/core_spec.rb +78 -0
  109. data/spec/basic_app/settings_spec.rb +64 -0
  110. data/spec/basic_app/views/view_helper_spec.rb +28 -0
  111. data/spec/basic_gem/aruba_helper_spec.rb +33 -0
  112. data/spec/basic_gem/basic_gem_spec.rb +84 -0
  113. data/spec/basic_gem/gemspec_spec.rb +68 -0
  114. data/spec/repo_manager/git_spec.rb +31 -0
  115. data/spec/spec_helper.rb +25 -0
  116. metadata +472 -0
@@ -0,0 +1,199 @@
1
+ ####################################################
2
+ # The file is was originally cloned from "Basic App"
3
+ # More information on "Basic App" can be found in the
4
+ # "Basic App" repository.
5
+ #
6
+ # See http://github.com/robertwahler
7
+ ####################################################
8
+ require 'mustache'
9
+ require 'pathname'
10
+
11
+ require 'repo_manager/assets/asset_configuration'
12
+ require 'repo_manager/assets/asset_accessors'
13
+
14
+ module RepoManager
15
+
16
+ class BaseAsset
17
+ include RepoManager::AssetAccessors
18
+ extend RepoManager::AssetAccessors
19
+
20
+ #
21
+ # --- Asset attributes START here ---
22
+ #
23
+
24
+ # Asset defined path
25
+ #
26
+ # Defaults to asset name when the path attribute is blank
27
+ #
28
+ # NOTE: This is not the path to the asset configuration file. If not an
29
+ # absolute path, then it is relative to the current working directory
30
+ #
31
+ # @example Full paths
32
+ #
33
+ # path: /home/robert/photos/photo1.jpg
34
+ #
35
+ # path: /home/robert/app/appfolder
36
+ #
37
+ # @example Home folder '~' paths are expanded automatically
38
+ #
39
+ # path: ~/photos/photo1.jpg -> /home/robert/photos/photo1.jpg
40
+ #
41
+ # @example Relative paths are expanded automatically relative to the CWD
42
+ #
43
+ # path: photos/photo1.jpg -> /home/robert/photos/photo1.jpg
44
+ #
45
+ # @example Mustache templates are supported
46
+ #
47
+ # path: /home/robert/{{name}}/appfolder -> /home/robert/app1/appfolder
48
+ #
49
+ # @example Mustache braces that come at the start must be quoted
50
+ #
51
+ # path: "{{name}}/appfolder" -> /home/robert/app1/appfolder
52
+ #
53
+ # @return [String] an absolute path
54
+ def path
55
+ return @path if @path
56
+
57
+ path = attributes[:path] || name
58
+ path = render(path)
59
+ if (path && !Pathname.new(path).absolute?)
60
+ # expand path if starts with '~'
61
+ path = File.expand_path(path) if path.match(/^~/)
62
+ # paths can be relative to cwd
63
+ path = File.join(File.expand_path(FileUtils.pwd), path) if (!Pathname.new(path).absolute?)
64
+ end
65
+ @path = path
66
+ end
67
+ def path=(value)
68
+ @path = nil
69
+ attributes[:path] = value
70
+ end
71
+
72
+ # Description (short)
73
+ #
74
+ # @return [String]
75
+ create_accessors :description
76
+
77
+ # Notes (user)
78
+ #
79
+ # @return [String]
80
+ create_accessors :notes
81
+
82
+ # Classification tags, an array of strings
83
+ #
84
+ # @return [Array] of tag strings
85
+ def tags
86
+ attributes[:tags] || []
87
+ end
88
+ def tags=(value)
89
+ attributes[:tags] = value
90
+ end
91
+
92
+ #
93
+ # --- Asset attributes END here ---
94
+ #
95
+
96
+ # The asset name is loosely tied to the name of the configuration folder (datastore).
97
+ # The name may also be a hash key from a YAML config file.
98
+ #
99
+ # The name should be a valid ruby variable name, in turn, a valid folder name, but this
100
+ # is not enforced.
101
+ #
102
+ # @see self.path_to_name
103
+ attr_accessor :name
104
+
105
+ # subclass factory to create Assets
106
+ #
107
+ # Call with classname to create. Pass in optional configuration folder
108
+ # name and/or a hash of attributes
109
+ #
110
+ # @param [String] asset_type (AppAsset) classname to initialize
111
+ # @param [String] asset_name (nil) asset name or folder name, if folder, will load YAML config
112
+ # @param [Hash] attributes ({}) initial attributes
113
+ #
114
+ # @return [BaseAsset] the created BaseAsset or decendent asset
115
+ def self.create(asset_type=:app_asset, asset_name=nil, attributes={})
116
+ classified_name = asset_type.to_s.split('_').collect!{ |w| w.capitalize }.join
117
+ Object.const_get('RepoManager').const_get(classified_name).new(asset_name, attributes)
118
+ end
119
+
120
+ # takes any path and returns a string suitable for asset name (Ruby identifier)
121
+ #
122
+ # @return [String] valid asset name
123
+ def self.path_to_name(path)
124
+ basename = File.basename(path)
125
+ basename = basename.gsub(/\&/,' and ')
126
+ basename = basename.downcase.strip.gsub(/ /,'_')
127
+ basename = basename.gsub(/[^a-zA-Z_0-9]/,'')
128
+ basename = basename.downcase.strip.gsub(/ /,'_')
129
+ basename.gsub(/[_]+/,'_')
130
+ end
131
+
132
+ # @param [String/Symbol] asset_name_or_folder (nil) if folder exists, will load YAML config
133
+ # @param [Hash] attributes ({}) initial attributes
134
+ def initialize(asset_name_or_folder=nil, attributes={})
135
+ # allow for lazy loading (TODO), don't assign empty attributes
136
+ @attributes = attributes.dup unless attributes.empty?
137
+
138
+ # create user_attribute methods
139
+ create_accessors(@attributes[:user_attributes]) if @attributes && @attributes[:user_attributes]
140
+
141
+ return unless asset_name_or_folder
142
+
143
+ folder = asset_name_or_folder.to_s
144
+ @name = File.basename(folder)
145
+
146
+ logger.debug "Asset name: #{name}"
147
+ logger.debug "Asset configuration folder: #{folder}"
148
+
149
+ if File.exists?(folder)
150
+ logger.debug "initializing new asset with folder: #{folder}"
151
+ configuration.load(folder)
152
+ end
153
+ end
154
+
155
+ def configuration
156
+ @configuration ||= RepoManager::AssetConfiguration.new(self)
157
+ end
158
+
159
+ # attributes is the hash loaded from the asset config file
160
+ def attributes
161
+ @attributes ||= {}
162
+ end
163
+
164
+ def to_hash
165
+ result = {}
166
+ result.merge!(:name => name) if name
167
+ result.merge!(:attributes => attributes)
168
+ result
169
+ end
170
+
171
+ # ERB binding
172
+ def get_binding
173
+ binding
174
+ end
175
+
176
+ # render a string with mustache tags replaced in the context of this class
177
+ #
178
+ # @return [String/nil] with mustache tags replaced or nil if template is nil
179
+ def render(template)
180
+ return nil unless template
181
+
182
+ Mustache.render(template, self)
183
+ end
184
+
185
+ # support for Mustache rendering of ad hoc user defined variables
186
+ # if the key exists in the hash, use if for a lookup
187
+ def method_missing(name, *args, &block)
188
+ return attributes[name.to_sym] if attributes.include?(name.to_sym)
189
+ return super
190
+ end
191
+
192
+ # method_missing support
193
+ def respond_to?(name)
194
+ return true if attributes.include?(name.to_sym)
195
+ super
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,30 @@
1
+ module RepoManager
2
+
3
+ # wrapper class for a source code repository configuration
4
+ class RepoAsset < AppAsset
5
+
6
+ # The asset_key, if defined, will be used as key to asset attributes when
7
+ # loading from YAML, if not defined, the entire YAML file will load.
8
+ #
9
+ # Override in decendants.
10
+ #
11
+ # @ return [Symbol] or nil
12
+ def asset_key
13
+ :repo
14
+ end
15
+
16
+ def status
17
+ @status ||= RepoManager::Status.new(scm)
18
+ end
19
+
20
+ # version control system wrapper
21
+ def scm
22
+ return @scm if @scm
23
+ raise NoSuchPathError unless File.exists?(path)
24
+ raise InvalidRepositoryError unless File.exists?(File.join(path, '.git'))
25
+ @scm = Git.open(path)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,2 @@
1
+ require 'repo_manager/core/hash'
2
+ require 'repo_manager/core/array'
@@ -0,0 +1,21 @@
1
+ class Array
2
+ def recursively_symbolize_keys!
3
+ self.each do |item|
4
+ if item.is_a? Hash
5
+ item.recursively_symbolize_keys!
6
+ elsif item.is_a? Array
7
+ item.recursively_symbolize_keys!
8
+ end
9
+ end
10
+ end
11
+
12
+ def recursively_stringify_keys!
13
+ self.each do |item|
14
+ if item.is_a? Hash
15
+ item.recursively_stringify_keys!
16
+ elsif item.is_a? Array
17
+ item.recursively_stringify_keys!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: UTF-8
2
+
3
+ class Hash
4
+
5
+ # YAML suitable for configuration files
6
+ #
7
+ # returns sorted YAML if Ruby 1.8
8
+ # returns insertion ordered YAML on Ruby 1.9+
9
+ def to_conf
10
+ unless RUBY_VERSION =~ /^1.8/
11
+ # fix issue with strings that can end up in non UTF-8 encodings, like
12
+ # ASCII-8BIT, force encoding so that they are not written to YAML as binary
13
+ self.each_pair { |k,v| self[k] = ((v.is_a? String) ? v.force_encoding("UTF-8") : v)}
14
+
15
+ # allow 1.9+ to_yaml to do insertion ordered conf files
16
+ return to_yaml
17
+ end
18
+
19
+ opts = {}
20
+ YAML::quick_emit( object_id, opts ) do |out|
21
+ out.map( taguri, to_yaml_style ) do |map|
22
+ sorted_keys = keys
23
+ sorted_keys = begin
24
+ sorted_keys.sort
25
+ rescue
26
+ sorted_keys.sort_by {|k| k.to_s} rescue sorted_keys
27
+ end
28
+
29
+ sorted_keys.each do |k|
30
+ map.add( k, fetch(k) )
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # active_support hash key functions
37
+ def symbolize_keys!
38
+ self.replace(self.symbolize_keys)
39
+ end
40
+
41
+ def symbolize_keys
42
+ inject({}) do |options, (key, value)|
43
+ options[(key.to_sym rescue key) || key] = value
44
+ options
45
+ end
46
+ end
47
+
48
+ def recursively_symbolize_keys!
49
+ self.symbolize_keys!
50
+ self.values.each do |v|
51
+ if v.is_a? Hash
52
+ v.recursively_symbolize_keys!
53
+ elsif v.is_a? Array
54
+ v.recursively_symbolize_keys!
55
+ end
56
+ end
57
+ self
58
+ end
59
+
60
+ def stringify_keys!
61
+ self.replace(self.stringify_keys)
62
+ end
63
+
64
+ def stringify_keys
65
+ inject({}) do |options, (key, value)|
66
+ options[(key.to_s rescue key) || key] = value
67
+ options
68
+ end
69
+ end
70
+
71
+ def recursively_stringify_keys!
72
+ self.stringify_keys!
73
+ self.values.each do |v|
74
+ if v.is_a? Hash
75
+ v.recursively_stringify_keys!
76
+ elsif v.is_a? Array
77
+ v.recursively_stringify_keys!
78
+ end
79
+ end
80
+ self
81
+ end
82
+
83
+ end
@@ -0,0 +1,10 @@
1
+ module RepoManager
2
+
3
+ class InvalidRepositoryError < StandardError
4
+ end
5
+
6
+ class NoSuchPathError < StandardError
7
+ end
8
+
9
+
10
+ end
@@ -0,0 +1,86 @@
1
+ # This is a modified version of the unreleased (as of 3/9/2012) Hashie Version 2
2
+ #
3
+ # It is modified to always convert new attributes to symbols and
4
+ # return nil if missing attribute so it works more like OpenStruct but
5
+ # still retains Hash as super
6
+ module RepoManager
7
+ module Extensions
8
+
9
+ # MethodReader allows you to access keys of the hash
10
+ # via method calls. This gives you an OStruct like way
11
+ # to access your hash's keys. It will recognize keys
12
+ # either as strings or symbols.
13
+ #
14
+ # @example Extending the Hash class
15
+ #
16
+ # class User < Hash
17
+ # include RepoManager::Extensions::MethodReader
18
+ # end
19
+ #
20
+ # user = User.new
21
+ # user['first_name'] = 'Michael'
22
+ # user.first_name # => 'Michael'
23
+ #
24
+ # user[:last_name] = 'Bleigh'
25
+ # user.last_name # => 'Bleigh'
26
+ #
27
+ # user[:birthday] = nil
28
+ # user.birthday # => nil
29
+ #
30
+ # user.not_declared # => nil
31
+ #
32
+ module MethodReader
33
+ def respond_to?(name)
34
+ return true if key?(name.to_s) || key?(name.to_sym)
35
+ super
36
+ end
37
+
38
+ def method_missing(name, *args)
39
+ return self[name.to_s] if key?(name.to_s)
40
+ return self[name.to_sym] if key?(name.to_sym)
41
+
42
+ # mod to return nil instead of 'undefined method'
43
+ return nil if args.length == 0
44
+
45
+ super
46
+ end
47
+ end
48
+
49
+ # MethodWriter gives you #key_name= shortcuts for
50
+ # writing to your hash. Keys are written as symbols
51
+ #
52
+ # Note that MethodWriter also overrides #respond_to such
53
+ # that any #method_name= will respond appropriately as true.
54
+ #
55
+ # @example
56
+ #
57
+ # class MyHash < Hash
58
+ # include RepoManager::Extensions::MethodWriter
59
+ # end
60
+ #
61
+ # h = MyHash.new
62
+ # h.awesome = 'sauce'
63
+ # h['awesome'] # => 'sauce'
64
+ #
65
+ module MethodWriter
66
+ def respond_to?(name)
67
+ return true if name.to_s =~ /=$/
68
+ super
69
+ end
70
+
71
+ def method_missing(name, *args)
72
+ if args.size == 1 && name.to_s =~ /(.*)=$/
73
+ return self[convert_key($1)] = args.first
74
+ end
75
+
76
+ super
77
+ end
78
+
79
+ def convert_key(key)
80
+ # mod to return symbol keys instead of string keys
81
+ key.to_sym
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,2 @@
1
+ require 'repo_manager/git/lib'
2
+ require 'repo_manager/git/status'
@@ -0,0 +1,69 @@
1
+ require 'git'
2
+
3
+ module Git
4
+
5
+ class CommandFailed < StandardError
6
+ attr_reader :command
7
+ attr_reader :exitstatus
8
+ attr_reader :error
9
+
10
+ def initialize(command, exitstatus, error='')
11
+ @command = command
12
+ @exitstatus = exitstatus
13
+ @error = error
14
+ super "Command exited with #{exitstatus}: #{command}"
15
+ end
16
+ end
17
+
18
+ class Lib
19
+
20
+ # need 'git status --porcelain'
21
+ def required_command_version
22
+ [1, 7, 0, 0]
23
+ end
24
+
25
+ # validatation once and only once with warning to STDERR
26
+ def validate
27
+ return if defined? @@validated
28
+ unless meets_required_version?
29
+ $stderr.puts "[WARNING] The repo_manager gem requires git #{required_command_version.join('.')} or later for the status command functionality, but only found #{current_command_version.join('.')}. You should probably upgrade."
30
+ end
31
+ @@validated = true
32
+ end
33
+
34
+ # liberate the ruby-git's private command method with a few tweaks
35
+ def native(cmd, opts = [], chdir = true, redirect = '', &block)
36
+ validate
37
+
38
+ ENV['GIT_DIR'] = @git_dir
39
+ ENV['GIT_INDEX_FILE'] = @git_index_file
40
+ ENV['GIT_WORK_TREE'] = @git_work_dir
41
+ path = @git_work_dir || @git_dir || @path
42
+
43
+ opts = [opts].flatten.map {|s| escape(s) }.join(' ')
44
+ git_cmd = "git #{cmd} #{opts} #{redirect} 2>&1"
45
+
46
+ out = nil
47
+ if chdir && (Dir.getwd != path)
48
+ Dir.chdir(path) { out = run_command(git_cmd, &block) }
49
+ else
50
+ out = run_command(git_cmd, &block)
51
+ end
52
+
53
+ if @logger
54
+ @logger.info(git_cmd)
55
+ @logger.debug(out)
56
+ end
57
+
58
+ if $?.exitstatus > 0
59
+ if $?.exitstatus == 1 && out == ''
60
+ return ''
61
+ end
62
+ raise Git::CommandFailed.new(git_cmd, $?.exitstatus, out.to_s)
63
+ end
64
+ out
65
+ end
66
+
67
+ end
68
+
69
+ end