MrMurano 1.3.2 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02d6e091a69dd09c071539a97a2b9d3755ad9202
4
- data.tar.gz: 46f2c3f6836c39648de3c08beef0eaf7c4ceb54b
3
+ metadata.gz: 5af905319869d15b61caf042c9ef16ce355013c7
4
+ data.tar.gz: 5859d5041f72f72143e1bde1412857b66ad817a0
5
5
  SHA512:
6
- metadata.gz: fa6b24a0ce013cb732a5563efbe86c54bbeb2cb9264cf69cc0dd1a7a43528f3fb22ef005af188ce7400ac2a7be40a9f716d850b395a24076edd550d8faf47d5c
7
- data.tar.gz: 2633547b8be35f46ee9239b3092f8d6ebb7242fea6e8c62c53bdf9df93c461b57c22945a8fbabc556d054d64d85208052b8545d8a59bae85c464532e2b9ab389
6
+ metadata.gz: 92b19376b2470b30da3196aa702958f837002bac6873f093be78dcf3c60d105c3472bdd9e9b27def0a5f1e539cced568120719dc9338ef33e7d28007371e0aff
7
+ data.tar.gz: a5d950dc9e1089851a0bdd6b3c20a27c308f8b7d7a0a9ef85d47de0037e3109fceaa97b68bfe722c04f010115085532f98c9db5d5492a0afff05614ff71ef346
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile CHANGED
@@ -11,6 +11,8 @@ gem 'http-form_data', '~> 1.0.1'
11
11
  gem 'rainbow', '~> 2.1.0'
12
12
 
13
13
  group :test do
14
- gem 'rake'
14
+ gem 'rake', '~> 10.1.1'
15
+ gem 'rspec', '~> 3.5'
16
+ gem 'webmock', '~> 2.1.0'
15
17
  end
16
18
 
data/MrMurano.gemspec CHANGED
@@ -31,8 +31,9 @@ Gem::Specification.new do |s|
31
31
  s.add_runtime_dependency('rainbow', '~> 2.1.0')
32
32
 
33
33
  s.add_development_dependency('bundler', '~> 1.7.6')
34
- s.add_development_dependency('rspec', '~> 3.2')
35
34
  s.add_development_dependency('rake', '~> 10.1.1')
35
+ s.add_development_dependency('rspec', '~> 3.5')
36
+ s.add_development_dependency('webmock', '~> 2.1.0')
36
37
  end
37
38
 
38
39
 
data/README.markdown CHANGED
@@ -47,12 +47,30 @@ All of these can be toggled with command line options.
47
47
 
48
48
  To aid with debugging, MrMurano has direct access to a solution's Keystore service.
49
49
 
50
- To see all of the keys in the current solution: `mr keystore`
50
+ To see all of the keys in the current solution: `mr keystore`
51
51
 
52
52
  ### Timeseries
53
53
 
54
54
  To aid with debugging, MrMurano has direct access to a solution's Timeseries service.
55
55
 
56
+ ### Product Content Area
57
+
58
+ MrMurano can manage the content area for a product. This area is a place to store
59
+ files for use by devices. Typically holding firmware images for Over-The-Air
60
+ updating. Although any kind of fleet wide data that devices may need to download
61
+ can be stored here.
62
+
63
+ Once the `product.id` is set, the content for that product can be accessed with the
64
+ following commands:
65
+ ```
66
+ > mr content list
67
+ > mr content upload
68
+ > mr content info
69
+ > mr content delete
70
+ > mr content download
71
+ ```
72
+
73
+ Call them with `--help` for details.
56
74
 
57
75
  ### Sub-directories
58
76
 
data/Rakefile CHANGED
@@ -5,24 +5,25 @@ task :default => [:test]
5
5
  # TODO: figure out better way to test.
6
6
  desc "Install gem in user dir"
7
7
  task :bob do
8
- sh %{gem install --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
8
+ sh %{gem install --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
9
9
  end
10
10
 
11
11
  desc "Uninstall from user dir"
12
12
  task :unbob do
13
- sh %{gem uninstall --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
13
+ sh %{gem uninstall --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
14
14
  end
15
15
 
16
16
  task :echo do
17
- puts "= #{Bundler::GemHelper.gemspec.version} ="
17
+ puts "= #{Bundler::GemHelper.gemspec.version} ="
18
18
  end
19
19
 
20
20
  task :run do
21
- sh %{ruby -Ilib bin/mr }
21
+ sh %{ruby -Ilib bin/mr }
22
22
  end
23
23
 
24
24
 
25
25
  task :test do
26
+ sh %{rspec}
26
27
  end
27
28
 
28
29
  # vim: set sw=4 ts=4 :
data/bin/mr CHANGED
@@ -7,7 +7,7 @@ require 'MrMurano'
7
7
  require 'pp'
8
8
 
9
9
  program :version, MrMurano::VERSION
10
- program :description, %{Manage a Solution in Exosite's Murano}
10
+ program :description, %{Manage a Solution and Product in Exosite's Murano}
11
11
 
12
12
  global_option('-V', '--verbose', 'Be chatty') {
13
13
  $cfg['tool.verbose'] = true
@@ -87,9 +87,9 @@ module MrMurano
87
87
  # Cannot have token call token, so cannot use workit.
88
88
  uri = endPoint('token/')
89
89
  request = Net::HTTP::Post.new(uri)
90
- curldebug(request)
91
-
90
+ request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
92
91
  request.content_type = 'application/json'
92
+ curldebug(request)
93
93
  #request.basic_auth(username(), password())
94
94
  request.body = JSON.generate(_loginInfo)
95
95
 
@@ -0,0 +1,255 @@
1
+ require 'uri'
2
+ require 'mime/types'
3
+ require 'csv'
4
+ require 'pp'
5
+
6
+ module MrMurano
7
+ class ProductBase
8
+ def initialize
9
+ @pid = $cfg['product.id']
10
+ raise "No Product ID!" if @pid.nil?
11
+ @uriparts = [:product, @pid]
12
+ end
13
+
14
+ include Http
15
+ include Verbose
16
+
17
+ def endPoint(path='')
18
+ parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
19
+ s = parts.map{|v| v.to_s}.join('/')
20
+ URI(s + path.to_s)
21
+ end
22
+ end
23
+
24
+ class Product < ProductBase
25
+ def info
26
+ get('/info')
27
+ end
28
+
29
+ def list(offset=0, limit=50)
30
+ get("/device/?offset=#{offset}&limit=#{limit}")
31
+ end
32
+
33
+ def enable(sn)
34
+ post("/device/#{sn.to_s}")
35
+ end
36
+
37
+ def update(specFile)
38
+ specFile = Pathname.new(specFile) unless specFile.kind_of? Pathname
39
+
40
+ uri = endPoint('/definition')
41
+ request = Net::HTTP::Post.new(uri)
42
+ ret = nil
43
+
44
+ specFile.open do |io|
45
+ request.body_stream = io
46
+ request.content_length = specFile.size
47
+ set_def_headers(request)
48
+ request.content_type = 'text/yaml'
49
+ ret = workit(request)
50
+ end
51
+ ret
52
+ end
53
+
54
+ def write(sn, values)
55
+ post("/write/#{sn}", values)
56
+ end
57
+
58
+ end
59
+
60
+ ##
61
+ # Manage the uploadable content for products.
62
+ class ProductContent < ProductBase
63
+ def initialize
64
+ super
65
+ @uriparts << :proxy
66
+ @uriparts << :provision
67
+ @uriparts << :manage
68
+ @uriparts << :content
69
+ @uriparts << @pid
70
+ end
71
+
72
+ ## List all things in content area
73
+ def list
74
+ ret = get('/')
75
+ return [] if ret.kind_of?(Hash)
76
+ ret.lines.map{|i|i.chomp}
77
+ end
78
+
79
+ ## List all contents allowed for sn
80
+ def list_for(sn)
81
+ ret = get("/?sn=#{sn}")
82
+ return [] if ret.kind_of?(Hash)
83
+ ret.lines.map{|i|i.chomp}
84
+ end
85
+
86
+ ## Create a new content item
87
+ def create(id, meta='', protect=false)
88
+ http_reset
89
+ data = {:id=>id, :meta=>meta}
90
+ data[:protected] = true if protect
91
+ postf('/', data)
92
+ end
93
+
94
+ ## Remove Content item
95
+ def remove(id)
96
+ postf('/', {:id=>id, :delete=>true})
97
+ end
98
+
99
+ ## Get info for content item
100
+ def info(id)
101
+ get("/#{id}") do |request, http|
102
+ http.request(request) do |resp|
103
+ case resp
104
+ when Net::HTTPSuccess
105
+ return CSV.parse(resp.body)
106
+ else
107
+ return nil
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ ## Download data for content item
114
+ def download(id, &block)
115
+ get("/#{id}?download=true") do |request, http|
116
+ http.request(request) do |resp|
117
+ case resp
118
+ when Net::HTTPSuccess
119
+ if block_given? then
120
+ resp.read_body &block
121
+ else
122
+ resp.read_body do |chunk|
123
+ $stdout.write chunk
124
+ end
125
+ end
126
+ else
127
+ say_error "got #{resp.to_s} from #{request} #{request.uri.to_s}"
128
+ raise resp
129
+ end
130
+ end
131
+ nil
132
+ end
133
+ end
134
+
135
+ ## Upload data for content item
136
+ # TODO: add support for passing in IOStream
137
+ def upload(id, path)
138
+ path = Pathname.new(path) unless path.kind_of? Pathname
139
+
140
+ mime = MIME::Types.type_for(path.to_s)[0] || MIME::Types["application/octet-stream"][0]
141
+ uri = endPoint("/#{id}")
142
+ request = Net::HTTP::Post.new(uri)
143
+ ret = nil
144
+
145
+ path.open do |io|
146
+ request.body_stream = io
147
+ set_def_headers(request)
148
+ request.content_length = path.size
149
+ request.content_type = mime.simplified
150
+ ret = workit(request)
151
+ end
152
+ ret
153
+ end
154
+
155
+ ## Delete data for content item
156
+ # Note that the content item is still present and listed.
157
+ def remove_content(id)
158
+ delete("/#{id}")
159
+ end
160
+
161
+ end
162
+
163
+ ##
164
+ # This is not applicable to Murano. Remove?
165
+ class ProductModel < ProductBase
166
+ def initialize
167
+ super
168
+ @uriparts << :proxy
169
+ @uriparts << :provision
170
+ @uriparts << :manage
171
+ @uriparts << :model
172
+ end
173
+
174
+ # In Murano, there should only ever be one.
175
+ # AND it should be @pid
176
+ def list
177
+ get('/')
178
+ end
179
+
180
+ def info(modelID=@pid)
181
+ get("/#{modelID}")
182
+ end
183
+
184
+ def list_sn(modelID=@pid)
185
+ get("/#{modelID}/")
186
+ end
187
+ end
188
+
189
+ ## TODO: Determine which of these are expected to be used.
190
+ class ProductSerialNumber < ProductBase
191
+ def initialize
192
+ super
193
+ @uriparts << :proxy
194
+ @uriparts << :provision
195
+ @uriparts << :manage
196
+ @uriparts << :model
197
+ @uriparts << @pid
198
+ end
199
+
200
+ def list(offset=0, limit=1000)
201
+ ret = get("/?offset=#{offset}&limit=#{limit}&status=true")
202
+ return [] if ret.kind_of?(Hash)
203
+ CSV.parse(ret)
204
+ end
205
+
206
+ def logs(sn)
207
+ get("/#{sn}?show=log") # results are empty
208
+ end
209
+
210
+ def regen(sn)
211
+ postf("/#{sn}", {:enable=>true})
212
+ end
213
+
214
+ def disable(sn)
215
+ postf("/#{sn}", {:disable=>true})
216
+ end
217
+
218
+ def activate(sn)
219
+ # TODO: Need to create a new @http for the different host. Fails otherwise
220
+ uri = URI("https://#{@pid}.m2.exosite.com/provision/activate")
221
+ request = Net::HTTP::Post.new(uri)
222
+ request.form_data = {
223
+ :vendor => @pid,
224
+ :model => @pid,
225
+ :sn => sn
226
+ }
227
+ request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
228
+ request['authorization'] = nil
229
+ request.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
230
+ workit(request)
231
+ end
232
+
233
+ def add_sn(sn, extra='')
234
+ # this does add, but what does that mean?
235
+ # Still need to call …/device/<sn> to enable.
236
+ # How long is the activation window?
237
+ postf('/', {:sn=>sn,:extra=>extra})
238
+ end
239
+
240
+ def remove_sn(sn)
241
+ postf('/', {:sn=>sn, :delete=>true})
242
+ end
243
+
244
+ def ranges
245
+ get('/?show=ranges')
246
+ end
247
+
248
+ def add_range()
249
+ post('/', {:ranges=>[ ]})
250
+ end
251
+
252
+ end
253
+
254
+ end
255
+ # vim: set ai et sw=2 ts=2 :
@@ -81,6 +81,7 @@ module MrMurano
81
81
  file = HTTP::FormData::File.new(local.to_s, {:mime_type=>remote[:mime_type]})
82
82
  form = HTTP::FormData.create(:file=>file)
83
83
  req = Net::HTTP::Put.new(uri)
84
+ set_def_headers(req)
84
85
  workit(req) do |request,http|
85
86
  request.content_type = form.content_type
86
87
  request.content_length = form.content_length
@@ -135,6 +136,7 @@ module MrMurano
135
136
  sha1 << hexit(chunk)
136
137
  end
137
138
  end
139
+ debug "Checking #{name} (#{mime.simplified} #{sha1.hexdigest})"
138
140
 
139
141
  {:path=>name, :mime_type=>mime.simplified, :checksum=>sha1.hexdigest}
140
142
  end
@@ -126,8 +126,15 @@ module MrMurano
126
126
 
127
127
  def list
128
128
  ret = get()
129
+ # eventhandler.skiplist is a list of whitespace seperated dot-paired values.
130
+ # fe: service.event service service service.event
129
131
  skiplist = ($cfg['eventhandler.skiplist'] or '').split
130
- ret[:items].reject{|i| i.has_key?(:service) and skiplist.include? i[:service] }
132
+ ret[:items].reject { |i|
133
+ i.has_key?(:service) and i.has_key?(:event) and
134
+ ( skiplist.include? i[:service] or
135
+ skiplist.include? "#{i[:service]}.#{i[:event]}"
136
+ )
137
+ }
131
138
  end
132
139
 
133
140
  def fetch(name)
@@ -18,12 +18,7 @@ module MrMurano
18
18
  end
19
19
 
20
20
  include Http
21
-
22
- def verbose(msg)
23
- if $cfg['tool.verbose'] then
24
- say msg
25
- end
26
- end
21
+ include Verbose
27
22
 
28
23
  def endPoint(path='')
29
24
  parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
@@ -0,0 +1,68 @@
1
+
2
+ command :config do |c|
3
+ c.syntax = %{mr config [options] <key> [<new value>]}
4
+ c.summary = %{Get and set options}
5
+ c.description = %{
6
+ You can get, set, or query config options with this command. All config
7
+ options are in a 'section.key' format. There is also a layer of scopes
8
+ that the keys can be saved in.
9
+ }
10
+
11
+ c.example %{See what the current combined config is}, 'mr config --dump'
12
+ c.example %{Query a value}, 'mr config solution.id'
13
+ c.example %{Set a new value; writing to the project config file}, 'mr config solution.id XXXXXXXX'
14
+ c.example %{Set a new value; writing to the private config file}, 'mr config --private solution.id XXXXXXXX'
15
+ c.example %{Set a new value; writing to the user config file}, 'mr config --user user.name my@email.address'
16
+ c.example %{Unset a value in a configfile. (lower scopes will become visible if set)},
17
+ 'mr config diff.cmd --unset'
18
+
19
+
20
+ c.option '--system', 'Use only the system config file. (/etc/mrmuranorc)'
21
+ c.option '--user', 'Use only the config file in $HOME (.mrmuranorc)'
22
+ c.option '--project', 'Use only the config file in the project (.mrmuranorc)'
23
+ c.option '--private', 'Use only the private config file in the project (.mrmuranorc.private)'
24
+ c.option '--specified', 'Use only the config file from the --config option.'
25
+
26
+ c.option '--unset', 'Remove key from config file.'
27
+ c.option '--dump', 'Dump the current combined view of the config'
28
+
29
+ c.action do |args, options|
30
+
31
+ if options.dump then
32
+ puts $cfg.dump()
33
+ elsif args.count == 0 then
34
+ say_error "Need a config key"
35
+ elsif args.count == 1 and not options.unset then
36
+ options.defaults :system=>false, :user=>false, :project=>false,
37
+ :specified=>false, :private=>false
38
+
39
+ # For read, if no scopes, than all. Otherwise just those specified
40
+ scopes = []
41
+ scopes << :system if options.system
42
+ scopes << :user if options.user
43
+ scopes << :project if options.project
44
+ scopes << :private if options.private
45
+ scopes << :specified if options.specified
46
+ scopes = MrMurano::Config::CFG_SCOPES if scopes.empty?
47
+
48
+ say $cfg.get(args[0], scopes)
49
+ else
50
+
51
+ options.defaults :system=>false, :user=>false, :project=>true,
52
+ :specified=>false, :private=>false
53
+ # For write, if scope is specified, only write to that scope.
54
+ scope = :project
55
+ scope = :system if options.system
56
+ scope = :user if options.user
57
+ scope = :project if options.project
58
+ scope = :private if options.private
59
+ scope = :specified if options.specified
60
+
61
+ args[1] = nil if options.unset
62
+ $cfg.set(args[0], args[1], scope)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ # vim: set ai et sw=2 ts=2 :
@@ -1,6 +1,5 @@
1
1
  require 'pathname'
2
2
  require 'inifile'
3
- require 'pp'
4
3
 
5
4
  module MrMurano
6
5
  class Config
@@ -37,27 +36,31 @@ module MrMurano
37
36
  CFG_SCOPES=%w{internal specified project private user system defaults}.map{|i| i.to_sym}.freeze
38
37
  CFG_FILE_NAME = '.mrmuranorc'.freeze
39
38
  CFG_PRVT_NAME = '.mrmuranorc.private'.freeze
39
+ CFG_DIR_NAME = '.mrmurano'.freeze
40
+ CFG_ALTRC_NAME = '.mrmurano/config'.freeze
41
+ CFG_SYS_NAME = '/etc/mrmuranorc'.freeze
40
42
 
41
43
  def initialize
42
44
  @paths = []
43
45
  @paths << ConfigFile.new(:internal, nil, IniFile.new())
44
46
  # :specified --configfile FILE goes here. (see load_specific)
45
- prjfile = findProjectFile()
47
+ prjfile = findProjectDir()
46
48
  unless prjfile.nil? then
47
- @paths << ConfigFile.new(:private, prjfile.dirname + CFG_PRVT_NAME)
48
- @paths << ConfigFile.new(:project, prjfile)
49
+ @paths << ConfigFile.new(:private, prjfile + CFG_PRVT_NAME)
50
+ @paths << ConfigFile.new(:project, prjfile + CFG_FILE_NAME)
49
51
  end
50
52
  @paths << ConfigFile.new(:user, Pathname.new(Dir.home) + CFG_FILE_NAME)
51
- @paths << ConfigFile.new(:system, Pathname.new('/etc') + CFG_FILE_NAME.sub(/^\./,''))
53
+ @paths << ConfigFile.new(:system, Pathname.new(CFG_SYS_NAME))
52
54
  @paths << ConfigFile.new(:defaults, nil, IniFile.new())
53
55
 
54
56
 
55
57
  set('tool.verbose', false, :defaults)
58
+ set('tool.debug', false, :defaults)
56
59
  set('tool.dry', false, :defaults)
57
60
 
58
61
  set('net.host', 'bizapi.hosted.exosite.io', :defaults)
59
62
 
60
- set('location.base', prjfile.dirname, :defaults) unless prjfile.nil?
63
+ set('location.base', prjfile, :defaults) unless prjfile.nil?
61
64
  set('location.files', 'files', :defaults)
62
65
  set('location.endpoints', 'endpoints', :defaults)
63
66
  set('location.modules', 'modules', :defaults)
@@ -67,27 +70,43 @@ module MrMurano
67
70
 
68
71
  set('files.default_page', 'index.html', :defaults)
69
72
 
70
- set('eventhandler.skiplist', 'websocket webservice', :defaults)
73
+ set('eventhandler.skiplist', 'websocket webservice device.service_call', :defaults)
71
74
 
72
75
  set('diff.cmd', 'diff -u', :defaults)
73
76
  end
74
77
 
75
- # Look at parent directories until HOME
76
- # Stop at first.
77
- def findProjectFile()
78
+ ## Find the root of this project Directory.
79
+ #
80
+ # The Project dir is the directory between PWD and HOME that has one of (in
81
+ # order of preference):
82
+ # - .mrmuranorc
83
+ # - .mrmuranorc.private
84
+ # - .mrmurano/config
85
+ # - .mrmurano/
86
+ # - .git/
87
+ def findProjectDir()
78
88
  result=nil
89
+ fileNames=[CFG_FILE_NAME, CFG_PRVT_NAME, CFG_ALTRC_NAME]
90
+ dirNames=[CFG_DIR_NAME, '.git']
79
91
  home = Pathname.new(Dir.home)
80
92
  pwd = Pathname.new(Dir.pwd)
81
93
  return nil if home == pwd
82
- pwd.dirname.ascend do |i|
94
+ pwd.dirname.ascend do |i|
95
+ break unless result.nil?
83
96
  break if i == home
84
- if (i + CFG_FILE_NAME).exist? then
85
- result = i + CFG_FILE_NAME
86
- break
97
+ fileNames.each do |f|
98
+ if (i + f).exist? then
99
+ result = i
100
+ end
101
+ end
102
+ dirNames.each do |f|
103
+ if (i + f).directory? then
104
+ result = i
105
+ end
87
106
  end
88
107
  end
89
108
  # if nothing found, assume it will live in pwd.
90
- result = Pathname.new(Dir.pwd) + CFG_FILE_NAME if result.nil?
109
+ result = Pathname.new(Dir.pwd) if result.nil?
91
110
  return result
92
111
  end
93
112
 
@@ -182,70 +201,4 @@ module MrMurano
182
201
  end
183
202
  end
184
203
 
185
- command :config do |c|
186
- c.syntax = %{mr config [options] <key> [<new value>]}
187
- c.summary = %{Get and set options}
188
- c.description = %{
189
- You can get, set, or query config options with this command. All config
190
- options are in a 'section.key' format. There is also a layer of scopes
191
- that the keys can be saved in.
192
- }
193
-
194
- c.example %{See what the current combined config is}, 'mr config --dump'
195
- c.example %{Query a value}, 'mr config solution.id'
196
- c.example %{Set a new value; writing to the project config file}, 'mr config solution.id XXXXXXXX'
197
- c.example %{Set a new value; writing to the private config file}, 'mr config --private solution.id XXXXXXXX'
198
- c.example %{Set a new value; writing to the user config file}, 'mr config --user user.name my@email.address'
199
- c.example %{Unset a value in a configfile. (lower scopes will become visible if set)},
200
- 'mr config diff.cmd --unset'
201
-
202
-
203
- c.option '--system', 'Use only the system config file. (/etc/mrmuranorc)'
204
- c.option '--user', 'Use only the config file in $HOME (.mrmuranorc)'
205
- c.option '--project', 'Use only the config file in the project (.mrmuranorc)'
206
- c.option '--private', 'Use only the private config file in the project (.mrmuranorc.private)'
207
- c.option '--specified', 'Use only the config file from the --config option.'
208
-
209
- c.option '--unset', 'Remove key from config file.'
210
- c.option '--dump', 'Dump the current combined view of the config'
211
-
212
- c.action do |args, options|
213
-
214
- if options.dump then
215
- puts $cfg.dump()
216
- elsif args.count == 0 then
217
- say_error "Need a config key"
218
- elsif args.count == 1 and not options.unset then
219
- options.defaults :system=>false, :user=>false, :project=>false,
220
- :specified=>false, :private=>false
221
-
222
- # For read, if no scopes, than all. Otherwise just those specified
223
- scopes = []
224
- scopes << :system if options.system
225
- scopes << :user if options.user
226
- scopes << :project if options.project
227
- scopes << :private if options.private
228
- scopes << :specified if options.specified
229
- scopes = MrMurano::Config::CFG_SCOPES if scopes.empty?
230
-
231
- say $cfg.get(args[0], scopes)
232
- else
233
-
234
- options.defaults :system=>false, :user=>false, :project=>true,
235
- :specified=>false, :private=>false
236
- # For write, if scope is specified, only write to that scope.
237
- scope = :project
238
- scope = :system if options.system
239
- scope = :user if options.user
240
- scope = :project if options.project
241
- scope = :private if options.private
242
- scope = :specified if options.specified
243
-
244
- args[1] = nil if options.unset
245
- $cfg.set(args[0], args[1], scope)
246
- end
247
- end
248
-
249
- end
250
-
251
204
  # vim: set ai et sw=2 ts=2 :