MrMurano 1.3.2 → 1.4.0

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