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.
- data/.gemfiles +115 -0
- data/.gitattributes +1 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/.yardopts +11 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +99 -0
- data/Guardfile +63 -0
- data/HISTORY.markdown +12 -0
- data/LICENSE +20 -0
- data/README.markdown +192 -0
- data/Rakefile +94 -0
- data/TODO.markdown +15 -0
- data/VERSION +1 -0
- data/bin/repo +151 -0
- data/cucumber.yml +28 -0
- data/examples/pc_saved_game_backup/.gitignore +2 -0
- data/examples/pc_saved_game_backup/INSTALL.markdown +420 -0
- data/examples/pc_saved_game_backup/README.markdown +108 -0
- data/examples/pc_saved_game_backup/remote/.gitignore +2 -0
- data/examples/pc_saved_game_backup/repo_manager/Gemfile +12 -0
- data/examples/pc_saved_game_backup/repo_manager/Gemfile.lock +66 -0
- data/examples/pc_saved_game_backup/repo_manager/assets/.gitignore +2 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/aruba.rb +15 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/env.rb +11 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/steps.rb +3 -0
- data/examples/pc_saved_game_backup/repo_manager/features/tasks/update.feature +144 -0
- data/examples/pc_saved_game_backup/repo_manager/global/default/asset.conf +2 -0
- data/examples/pc_saved_game_backup/repo_manager/repo.conf +64 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/.gitignore +0 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/remote.rb +57 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/update.rb +65 -0
- data/examples/pc_saved_game_backup/saved_games/hearts/save1 +1 -0
- data/examples/pc_saved_game_backup/saved_games/hearts/save2 +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/my_profile.ini +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/saves/save1 +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/saves/save2 +1 -0
- data/features/actions/git.feature +296 -0
- data/features/actions/help.feature +53 -0
- data/features/actions/list.feature +624 -0
- data/features/actions/path.feature +195 -0
- data/features/actions/status.feature +261 -0
- data/features/actions/task.feature +127 -0
- data/features/assets/configuration.feature +204 -0
- data/features/assets/rendering.feature +42 -0
- data/features/assets/user_attributes.feature +98 -0
- data/features/bin.feature +42 -0
- data/features/logger.feature +218 -0
- data/features/settings.feature +240 -0
- data/features/support/aruba.rb +15 -0
- data/features/support/env.rb +11 -0
- data/features/support/steps.rb +3 -0
- data/features/tasks/add/asset.feature +178 -0
- data/features/tasks/generate/init.feature +56 -0
- data/lib/repo_manager.rb +36 -0
- data/lib/repo_manager/actions.rb +8 -0
- data/lib/repo_manager/actions/action_helper.rb +39 -0
- data/lib/repo_manager/actions/app_action.rb +30 -0
- data/lib/repo_manager/actions/base_action.rb +296 -0
- data/lib/repo_manager/actions/git_action.rb +113 -0
- data/lib/repo_manager/actions/help_action.rb +52 -0
- data/lib/repo_manager/actions/list_action.rb +123 -0
- data/lib/repo_manager/actions/path_action.rb +22 -0
- data/lib/repo_manager/actions/status_action.rb +192 -0
- data/lib/repo_manager/actions/task_action.rb +71 -0
- data/lib/repo_manager/app.rb +116 -0
- data/lib/repo_manager/assets.rb +3 -0
- data/lib/repo_manager/assets/app_asset.rb +15 -0
- data/lib/repo_manager/assets/asset_accessors.rb +67 -0
- data/lib/repo_manager/assets/asset_configuration.rb +137 -0
- data/lib/repo_manager/assets/asset_manager.rb +72 -0
- data/lib/repo_manager/assets/base_asset.rb +199 -0
- data/lib/repo_manager/assets/repo_asset.rb +30 -0
- data/lib/repo_manager/core.rb +2 -0
- data/lib/repo_manager/core/array.rb +21 -0
- data/lib/repo_manager/core/hash.rb +83 -0
- data/lib/repo_manager/errors.rb +10 -0
- data/lib/repo_manager/extensions/hash.rb +86 -0
- data/lib/repo_manager/git.rb +2 -0
- data/lib/repo_manager/git/lib.rb +69 -0
- data/lib/repo_manager/git/status.rb +196 -0
- data/lib/repo_manager/logger.rb +39 -0
- data/lib/repo_manager/settings.rb +98 -0
- data/lib/repo_manager/tasks.rb +3 -0
- data/lib/repo_manager/tasks/add/asset.rb +213 -0
- data/lib/repo_manager/tasks/generate/init.rb +42 -0
- data/lib/repo_manager/tasks/generate/templates/config/repo.conf.tt +61 -0
- data/lib/repo_manager/tasks/generate/templates/init/assets/.gitignore +0 -0
- data/lib/repo_manager/tasks/generate/templates/init/global/default/asset.conf +2 -0
- data/lib/repo_manager/tasks/generate/templates/init/tasks/.gitignore +0 -0
- data/lib/repo_manager/tasks/task_manager.rb +166 -0
- data/lib/repo_manager/tasks/thor_helper.rb +29 -0
- data/lib/repo_manager/test/asset_steps.rb +19 -0
- data/lib/repo_manager/test/base_steps.rb +152 -0
- data/lib/repo_manager/test/repo_api.rb +41 -0
- data/lib/repo_manager/test/repo_steps.rb +83 -0
- data/lib/repo_manager/test/test_api.rb +88 -0
- data/lib/repo_manager/views.rb +2 -0
- data/lib/repo_manager/views/app_view.rb +15 -0
- data/lib/repo_manager/views/base_view.rb +137 -0
- data/lib/repo_manager/views/templates/css/basic.css +26 -0
- data/lib/repo_manager/views/templates/default.erb +40 -0
- data/lib/repo_manager/views/templates/default.slim +37 -0
- data/lib/repo_manager/views/view_helper.rb +55 -0
- data/repo_manager.gemspec +75 -0
- data/spec/basic_app/actions/action_helper_spec.rb +54 -0
- data/spec/basic_app/assets/base_asset_spec.rb +210 -0
- data/spec/basic_app/core_spec.rb +78 -0
- data/spec/basic_app/settings_spec.rb +64 -0
- data/spec/basic_app/views/view_helper_spec.rb +28 -0
- data/spec/basic_gem/aruba_helper_spec.rb +33 -0
- data/spec/basic_gem/basic_gem_spec.rb +84 -0
- data/spec/basic_gem/gemspec_spec.rb +68 -0
- data/spec/repo_manager/git_spec.rb +31 -0
- data/spec/spec_helper.rb +25 -0
- 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,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,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,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
|