MuranoCLI 3.0.0 → 3.0.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.
- checksums.yaml +4 -4
 - data/.rubocop.yml +50 -27
 - data/.trustme.vim +12 -8
 - data/bin/murano +23 -8
 - data/docs/basic_example.rst +1 -1
 - data/docs/completions/murano_completion-bash +88 -0
 - data/lib/MrMurano/Account.rb +3 -3
 - data/lib/MrMurano/Business.rb +6 -6
 - data/lib/MrMurano/Config-Migrate.rb +1 -3
 - data/lib/MrMurano/Config.rb +16 -8
 - data/lib/MrMurano/Content.rb +56 -45
 - data/lib/MrMurano/Gateway.rb +62 -21
 - data/lib/MrMurano/Mock.rb +27 -19
 - data/lib/MrMurano/Passwords.rb +7 -7
 - data/lib/MrMurano/ReCommander.rb +171 -28
 - data/lib/MrMurano/Setting.rb +38 -40
 - data/lib/MrMurano/Solution-ServiceConfig.rb +2 -1
 - data/lib/MrMurano/Solution-Services.rb +196 -61
 - data/lib/MrMurano/Solution-Users.rb +7 -7
 - data/lib/MrMurano/Solution.rb +22 -8
 - data/lib/MrMurano/SolutionId.rb +10 -4
 - data/lib/MrMurano/SubCmdGroupContext.rb +14 -5
 - data/lib/MrMurano/SyncAllowed.rb +42 -0
 - data/lib/MrMurano/SyncUpDown.rb +122 -65
 - data/lib/MrMurano/Webservice-Cors.rb +9 -3
 - data/lib/MrMurano/Webservice-Endpoint.rb +39 -33
 - data/lib/MrMurano/Webservice-File.rb +35 -24
 - data/lib/MrMurano/commands/business.rb +18 -18
 - data/lib/MrMurano/commands/content.rb +3 -2
 - data/lib/MrMurano/commands/devices.rb +137 -22
 - data/lib/MrMurano/commands/globals.rb +8 -2
 - data/lib/MrMurano/commands/keystore.rb +3 -2
 - data/lib/MrMurano/commands/link.rb +13 -13
 - data/lib/MrMurano/commands/login.rb +3 -2
 - data/lib/MrMurano/commands/mock.rb +4 -3
 - data/lib/MrMurano/commands/password.rb +4 -2
 - data/lib/MrMurano/commands/postgresql.rb +5 -3
 - data/lib/MrMurano/commands/settings.rb +78 -62
 - data/lib/MrMurano/commands/show.rb +79 -74
 - data/lib/MrMurano/commands/solution.rb +6 -4
 - data/lib/MrMurano/commands/solution_picker.rb +5 -4
 - data/lib/MrMurano/commands/status.rb +15 -4
 - data/lib/MrMurano/commands/timeseries.rb +3 -2
 - data/lib/MrMurano/commands/tsdb.rb +3 -2
 - data/lib/MrMurano/hash.rb +6 -6
 - data/lib/MrMurano/http.rb +66 -67
 - data/lib/MrMurano/makePretty.rb +18 -12
 - data/lib/MrMurano/progress.rb +9 -2
 - data/lib/MrMurano/verbosing.rb +14 -2
 - data/lib/MrMurano/version.rb +2 -2
 - data/spec/GatewayDevice_spec.rb +190 -149
 - data/spec/Mock_spec.rb +3 -3
 - data/spec/Solution-ServiceEventHandler_spec.rb +170 -137
 - data/spec/SyncUpDown_spec.rb +205 -191
 - metadata +3 -2
 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Last Modified: 2017.08. 
     | 
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.22 /coding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
         @@ -34,11 +34,13 @@ module MrMurano 
     | 
|
| 
       34 
34 
     | 
    
         
             
                end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
                def remove(id)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return unless remove_item_allowed(id)
         
     | 
| 
       37 
38 
     | 
    
         
             
                  delete('/' + id.to_s)
         
     | 
| 
       38 
39 
     | 
    
         
             
                end
         
     | 
| 
       39 
40 
     | 
    
         | 
| 
       40 
41 
     | 
    
         
             
                # @param modify Bool: True if item exists already and this is changing it
         
     | 
| 
       41 
42 
     | 
    
         
             
                def upload(_local, remote, _modify)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  return unless upload_item_allowed(remote[@itemkey])
         
     | 
| 
       42 
44 
     | 
    
         
             
                  # Roles cannot be modified, so must delete and post.
         
     | 
| 
       43 
45 
     | 
    
         
             
                  delete('/' + remote[@itemkey]) do |request, http|
         
     | 
| 
       44 
46 
     | 
    
         
             
                    response = http.request(request)
         
     | 
| 
         @@ -59,9 +61,6 @@ module MrMurano 
     | 
|
| 
       59 
61 
     | 
    
         
             
                  # for now, we'll read, modify, write
         
     | 
| 
       60 
62 
     | 
    
         
             
                  here = []
         
     | 
| 
       61 
63 
     | 
    
         
             
                  if local.exist?
         
     | 
| 
       62 
     | 
    
         
            -
                    # FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
         
     | 
| 
       63 
     | 
    
         
            -
                    #   Disabling [rubo]cop for now.
         
     | 
| 
       64 
     | 
    
         
            -
                    # rubocop:disable Security/YAMLLoad
         
     | 
| 
       65 
64 
     | 
    
         
             
                    local.open('rb') { |io| here = YAML.load(io) }
         
     | 
| 
       66 
65 
     | 
    
         
             
                    here = [] if here == false
         
     | 
| 
       67 
66 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -69,6 +68,7 @@ module MrMurano 
     | 
|
| 
       69 
68 
     | 
    
         
             
                    Hash.transform_keys_to_symbols(i)[@itemkey] == item[@itemkey]
         
     | 
| 
       70 
69 
     | 
    
         
             
                  end
         
     | 
| 
       71 
70 
     | 
    
         
             
                  here << item.reject { |k, _v| %i[synckey synctype].include? k }
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return unless download_item_allowed(item[@itemkey])
         
     | 
| 
       72 
72 
     | 
    
         
             
                  local.open('wb') do |io|
         
     | 
| 
       73 
73 
     | 
    
         
             
                    io.write here.map { |i| Hash.transform_keys_to_strings(i) }.to_yaml
         
     | 
| 
       74 
74 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -79,7 +79,6 @@ module MrMurano 
     | 
|
| 
       79 
79 
     | 
    
         
             
                  # for now, we'll read, modify, write
         
     | 
| 
       80 
80 
     | 
    
         
             
                  here = []
         
     | 
| 
       81 
81 
     | 
    
         
             
                  if local.exist?
         
     | 
| 
       82 
     | 
    
         
            -
                    # FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
         
     | 
| 
       83 
82 
     | 
    
         
             
                    local.open('rb') { |io| here = YAML.load(io) }
         
     | 
| 
       84 
83 
     | 
    
         
             
                    here = [] if here == false
         
     | 
| 
       85 
84 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -87,6 +86,7 @@ module MrMurano 
     | 
|
| 
       87 
86 
     | 
    
         
             
                  here.delete_if do |it|
         
     | 
| 
       88 
87 
     | 
    
         
             
                    Hash.transform_keys_to_symbols(it)[key] == item[key]
         
     | 
| 
       89 
88 
     | 
    
         
             
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  return unless removelocal_item_allowed(item[key])
         
     | 
| 
       90 
90 
     | 
    
         
             
                  local.open('wb') do |io|
         
     | 
| 
       91 
91 
     | 
    
         
             
                    io.write here.map { |i| Hash.transform_keys_to_strings(i) }.to_yaml
         
     | 
| 
       92 
92 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -109,7 +109,6 @@ module MrMurano 
     | 
|
| 
       109 
109 
     | 
    
         | 
| 
       110 
110 
     | 
    
         
             
                  # MAYBE/2017-07-03: Do we care if there are duplicate keys in the yaml? See dup_count.
         
     | 
| 
       111 
111 
     | 
    
         
             
                  here = []
         
     | 
| 
       112 
     | 
    
         
            -
                  # FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
         
     | 
| 
       113 
112 
     | 
    
         
             
                  from.open { |io| here = YAML.load(io) }
         
     | 
| 
       114 
113 
     | 
    
         
             
                  here = [] if here == false
         
     | 
| 
       115 
114 
     | 
    
         | 
| 
         @@ -152,10 +151,11 @@ module MrMurano 
     | 
|
| 
       152 
151 
     | 
    
         
             
                end
         
     | 
| 
       153 
152 
     | 
    
         | 
| 
       154 
153 
     | 
    
         
             
                # @param modify Bool: True if item exists already and this is changing it
         
     | 
| 
       155 
     | 
    
         
            -
                def upload( 
     | 
| 
      
 154 
     | 
    
         
            +
                def upload(local, _remote, _modify)
         
     | 
| 
       156 
155 
     | 
    
         
             
                  # TODO: figure out APIs for updating users.
         
     | 
| 
       157 
156 
     | 
    
         
             
                  warning %(Updating Users is not yet implemented.)
         
     | 
| 
       158 
157 
     | 
    
         
             
                  # post does work if the :password field is set.
         
     | 
| 
      
 158 
     | 
    
         
            +
                  return unless upload_item_allowed(local)
         
     | 
| 
       159 
159 
     | 
    
         
             
                end
         
     | 
| 
       160 
160 
     | 
    
         | 
| 
       161 
161 
     | 
    
         
             
                def synckey(item)
         
     | 
    
        data/lib/MrMurano/Solution.rb
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Last Modified: 2017.08. 
     | 
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.23 /coding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
         @@ -96,7 +96,7 @@ module MrMurano 
     | 
|
| 
       96 
96 
     | 
    
         
             
                        #query.push ['limit', 20]
         
     | 
| 
       97 
97 
     | 
    
         
             
                        query.push ['offset', total - remaining]
         
     | 
| 
       98 
98 
     | 
    
         
             
                      elsif remaining != 0
         
     | 
| 
       99 
     | 
    
         
            -
                        warning "Unexpected: negative remaining:  
     | 
| 
      
 99 
     | 
    
         
            +
                        warning "Unexpected: negative remaining: #{fancy_ticks(total)}"
         
     | 
| 
       100 
100 
     | 
    
         
             
                        remaining = 0
         
     | 
| 
       101 
101 
     | 
    
         
             
                      end
         
     | 
| 
       102 
102 
     | 
    
         
             
                      if aggregate.nil?
         
     | 
| 
         @@ -127,6 +127,8 @@ module MrMurano 
     | 
|
| 
       127 
127 
     | 
    
         
             
                  #     the results.
         
     | 
| 
       128 
128 
     | 
    
         
             
                  #path = path || '?select=id,service'
         
     | 
| 
       129 
129 
     | 
    
         
             
                  matches = list(path)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  # 2017-08-21: The only caller so far is the link command,
         
     | 
| 
      
 131 
     | 
    
         
            +
                  #   which passes the Solution ID as svc_name.
         
     | 
| 
       130 
132 
     | 
    
         
             
                  matches.select { |match| match[:service] == svc_name }
         
     | 
| 
       131 
133 
     | 
    
         
             
                end
         
     | 
| 
       132 
134 
     | 
    
         | 
| 
         @@ -240,23 +242,35 @@ module MrMurano 
     | 
|
| 
       240 
242 
     | 
    
         
             
                    end
         
     | 
| 
       241 
243 
     | 
    
         
             
                  end
         
     | 
| 
       242 
244 
     | 
    
         
             
                  unless @sid.to_s.empty? || sid.to_s == @sid.to_s
         
     | 
| 
       243 
     | 
    
         
            -
                    warning 
     | 
| 
      
 245 
     | 
    
         
            +
                    warning(
         
     | 
| 
      
 246 
     | 
    
         
            +
                      "#{type_name} ID mismatch. Server says #{fancy_ticks(sid)}, " \
         
     | 
| 
      
 247 
     | 
    
         
            +
                      "but config says #{fancy_ticks(@sid)}."
         
     | 
| 
      
 248 
     | 
    
         
            +
                    )
         
     | 
| 
       244 
249 
     | 
    
         
             
                  end
         
     | 
| 
       245 
250 
     | 
    
         
             
                  self.sid = sid
         
     | 
| 
       246 
251 
     | 
    
         
             
                  # Verify/set the name.
         
     | 
| 
       247 
252 
     | 
    
         
             
                  unless @name.to_s.empty? || @meta[:name].to_s == @name.to_s
         
     | 
| 
       248 
     | 
    
         
            -
                    warning 
     | 
| 
      
 253 
     | 
    
         
            +
                    warning(
         
     | 
| 
      
 254 
     | 
    
         
            +
                      "Name mismatch. Server says #{fancy_ticks(@meta[:name])}, " \
         
     | 
| 
      
 255 
     | 
    
         
            +
                      "but config says #{fancy_ticks(@name)}."
         
     | 
| 
      
 256 
     | 
    
         
            +
                    )
         
     | 
| 
       249 
257 
     | 
    
         
             
                  end
         
     | 
| 
       250 
258 
     | 
    
         
             
                  if !@meta[:name].to_s.empty?
         
     | 
| 
       251 
259 
     | 
    
         
             
                    set_name(@meta[:name])
         
     | 
| 
       252 
260 
     | 
    
         
             
                    unless @valid_name || type == :solution
         
     | 
| 
       253 
     | 
    
         
            -
                      warning 
     | 
| 
      
 261 
     | 
    
         
            +
                      warning(
         
     | 
| 
      
 262 
     | 
    
         
            +
                        "Unexpected: Server returned invalid name: #{fancy_ticks(@meta[:name])}"
         
     | 
| 
      
 263 
     | 
    
         
            +
                      )
         
     | 
| 
       254 
264 
     | 
    
         
             
                    end
         
     | 
| 
       255 
265 
     | 
    
         
             
                  elsif @meta[:domain]
         
     | 
| 
       256 
266 
     | 
    
         
             
                    # This could be a pre-ADC/pre-Murano business.
         
     | 
| 
       257 
     | 
    
         
            -
                    warning 
     | 
| 
      
 267 
     | 
    
         
            +
                    warning(
         
     | 
| 
      
 268 
     | 
    
         
            +
                      "Unexpected: Server returned no name for domain: #{fancy_ticks(@meta[:domain])}"
         
     | 
| 
      
 269 
     | 
    
         
            +
                    )
         
     | 
| 
       258 
270 
     | 
    
         
             
                  else
         
     | 
| 
       259 
     | 
    
         
            -
                    warning 
     | 
| 
      
 271 
     | 
    
         
            +
                    warning(
         
     | 
| 
      
 272 
     | 
    
         
            +
                      "Unexpected: Server returned no name for solution: #{fancy_ticks(@meta)}"
         
     | 
| 
      
 273 
     | 
    
         
            +
                    )
         
     | 
| 
       260 
274 
     | 
    
         
             
                  end
         
     | 
| 
       261 
275 
     | 
    
         
             
                end
         
     | 
| 
       262 
276 
     | 
    
         | 
| 
         @@ -322,7 +336,7 @@ module MrMurano 
     | 
|
| 
       322 
336 
     | 
    
         
             
                  if @name.to_s.empty?
         
     | 
| 
       323 
337 
     | 
    
         
             
                    ''
         
     | 
| 
       324 
338 
     | 
    
         
             
                  else
         
     | 
| 
       325 
     | 
    
         
            -
                     
     | 
| 
      
 339 
     | 
    
         
            +
                    fancy_ticks(@name)
         
     | 
| 
       326 
340 
     | 
    
         
             
                  end
         
     | 
| 
       327 
341 
     | 
    
         
             
                end
         
     | 
| 
       328 
342 
     | 
    
         | 
    
        data/lib/MrMurano/SolutionId.rb
    CHANGED
    
    | 
         @@ -1,14 +1,18 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Last Modified: 2017.08. 
     | 
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.23 /coding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
       5 
5 
     | 
    
         
             
            # License: MIT. See LICENSE.txt.
         
     | 
| 
       6 
6 
     | 
    
         
             
            #  vim:tw=0:ts=2:sw=2:et:ai
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
      
 8 
     | 
    
         
            +
            require 'MrMurano/verbosing'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       8 
10 
     | 
    
         
             
            module MrMurano
         
     | 
| 
       9 
11 
     | 
    
         
             
              module SolutionId
         
     | 
| 
       10 
12 
     | 
    
         
             
                INVALID_SID = '-1'
         
     | 
| 
       11 
     | 
    
         
            -
                UNEXPECTED_TYPE_OR_ERROR_MSG =  
     | 
| 
      
 13 
     | 
    
         
            +
                UNEXPECTED_TYPE_OR_ERROR_MSG = (
         
     | 
| 
      
 14 
     | 
    
         
            +
                  'Unexpected result type or error: assuming empty instead'
         
     | 
| 
      
 15 
     | 
    
         
            +
                )
         
     | 
| 
       12 
16 
     | 
    
         | 
| 
       13 
17 
     | 
    
         
             
                attr_reader :sid
         
     | 
| 
       14 
18 
     | 
    
         
             
                attr_reader :valid_sid
         
     | 
| 
         @@ -41,7 +45,9 @@ module MrMurano 
     | 
|
| 
       41 
45 
     | 
    
         | 
| 
       42 
46 
     | 
    
         
             
                def sid=(sid)
         
     | 
| 
       43 
47 
     | 
    
         
             
                  sid = INVALID_SID if sid.nil? || !sid.is_a?(String) || sid.empty?
         
     | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
      
 48 
     | 
    
         
            +
                  if sid.to_s.empty? || sid == INVALID_SID || (defined?(@sid) && sid != @sid)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @valid_sid = false
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
       45 
51 
     | 
    
         
             
                  @sid = sid
         
     | 
| 
       46 
52 
     | 
    
         
             
                  # MAGIC_NUMBER: The 2nd element is the solution ID, e.g., solution/<sid>/...
         
     | 
| 
       47 
53 
     | 
    
         
             
                  raise "Unexpected @uriparts_sidex #{@uriparts_sidex}" unless @uriparts_sidex == 1
         
     | 
| 
         @@ -65,7 +71,7 @@ module MrMurano 
     | 
|
| 
       65 
71 
     | 
    
         
             
                def endpoint(_path='')
         
     | 
| 
       66 
72 
     | 
    
         
             
                  # This is hopefully just a DEV error, and not something user will ever see!
         
     | 
| 
       67 
73 
     | 
    
         
             
                  return unless @uriparts[@uriparts_sidex] == INVALID_SID
         
     | 
| 
       68 
     | 
    
         
            -
                  error("Solution ID missing! Invalid  
     | 
| 
      
 74 
     | 
    
         
            +
                  error("Solution ID missing! Invalid #{MrMurano::Verbose.fancy_ticks(@solntype)}")
         
     | 
| 
       69 
75 
     | 
    
         
             
                  exit 2
         
     | 
| 
       70 
76 
     | 
    
         
             
                end
         
     | 
| 
       71 
77 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,16 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.21 /coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # License: MIT. See LICENSE.txt.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #  vim:tw=0:ts=2:sw=2:et:ai
         
     | 
| 
       1 
7 
     | 
    
         | 
| 
       2 
8 
     | 
    
         
             
            module MrMurano
         
     | 
| 
       3 
9 
     | 
    
         
             
              class SubCmdGroupHelp
         
     | 
| 
       4 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
                attr_reader :description
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :name
         
     | 
| 
       5 
12 
     | 
    
         | 
| 
       6 
13 
     | 
    
         
             
                def initialize(command)
         
     | 
| 
       7 
14 
     | 
    
         
             
                  @name = command.syntax.to_s
         
     | 
| 
       8 
15 
     | 
    
         
             
                  @description = command.description.to_s
         
     | 
| 
       9 
16 
     | 
    
         
             
                  @runner = ::Commander::Runner.instance
         
     | 
| 
       10 
17 
     | 
    
         
             
                  prefix = /^#{command.name.to_s} /
         
     | 
| 
       11 
     | 
    
         
            -
                  cmds = @runner.instance_variable_get(:@commands).select{|n,_| n.to_s =~ prefix}
         
     | 
| 
      
 18 
     | 
    
         
            +
                  cmds = @runner.instance_variable_get(:@commands).select { |n, _| n.to_s =~ prefix }
         
     | 
| 
       12 
19 
     | 
    
         
             
                  @commands = cmds
         
     | 
| 
       13 
     | 
    
         
            -
                  als = @runner.instance_variable_get(:@aliases).select{|n,_| n.to_s =~ prefix}
         
     | 
| 
      
 20 
     | 
    
         
            +
                  als = @runner.instance_variable_get(:@aliases).select { |n, _| n.to_s =~ prefix }
         
     | 
| 
       14 
21 
     | 
    
         
             
                  @aliases = als
         
     | 
| 
       15 
22 
     | 
    
         | 
| 
       16 
23 
     | 
    
         
             
                  @options = {}
         
     | 
| 
         @@ -24,6 +31,8 @@ module MrMurano 
     | 
|
| 
       24 
31 
     | 
    
         
             
                    @description
         
     | 
| 
       25 
32 
     | 
    
         
             
                  when :help
         
     | 
| 
       26 
33 
     | 
    
         
             
                    nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # rubocop:disable Style/EmptyElse: "Redundant else-clause."
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # Rubocop seems incorrect about this one.
         
     | 
| 
       27 
36 
     | 
    
         
             
                  else
         
     | 
| 
       28 
37 
     | 
    
         
             
                    nil
         
     | 
| 
       29 
38 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -37,6 +46,8 @@ module MrMurano 
     | 
|
| 
       37 
46 
     | 
    
         
             
                  @commands[name.to_s]
         
     | 
| 
       38 
47 
     | 
    
         
             
                end
         
     | 
| 
       39 
48 
     | 
    
         | 
| 
      
 49 
     | 
    
         
            +
                # rubocop:disable Style/AccessorMethodName
         
     | 
| 
      
 50 
     | 
    
         
            +
                # "Do not prefix reader method names with get_."
         
     | 
| 
       40 
51 
     | 
    
         
             
                def get_help
         
     | 
| 
       41 
52 
     | 
    
         
             
                  hf = @runner.program(:help_formatter).new(self)
         
     | 
| 
       42 
53 
     | 
    
         
             
                  pc = Commander::HelpFormatter::ProgramContext.new(self).get_binding
         
     | 
| 
         @@ -45,5 +56,3 @@ module MrMurano 
     | 
|
| 
       45 
56 
     | 
    
         
             
              end
         
     | 
| 
       46 
57 
     | 
    
         
             
            end
         
     | 
| 
       47 
58 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
            #  vim: set ai et sw=2 ts=2 :
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.23 /coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # License: MIT. See LICENSE.txt.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #  vim:tw=0:ts=2:sw=2:et:ai
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            #require 'MrMurano/progress'
         
     | 
| 
      
 9 
     | 
    
         
            +
            #require 'MrMurano/verbosing'
         
     | 
| 
      
 10 
     | 
    
         
            +
            #require 'MrMurano/hash'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module MrMurano
         
     | 
| 
      
 13 
     | 
    
         
            +
              module SyncAllowed
         
     | 
| 
      
 14 
     | 
    
         
            +
                def sync_item_allowed(actioning, item_name)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  if $cfg['tool.dry']
         
     | 
| 
      
 16 
     | 
    
         
            +
                    MrMurano::Verbose.whirly_interject do
         
     | 
| 
      
 17 
     | 
    
         
            +
                      say("--dry: Not #{actioning} item: #{fancy_ticks(item_name)}")
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    false
         
     | 
| 
      
 20 
     | 
    
         
            +
                  else
         
     | 
| 
      
 21 
     | 
    
         
            +
                    true
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def remove_item_allowed(id)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  sync_item_allowed('removing', id)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def upload_item_allowed(id)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  sync_item_allowed('uploading', id)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def download_item_allowed(id)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  sync_item_allowed('downloading', id)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def removelocal_item_allowed(id)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  sync_item_allowed('removing-local', id)
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
    
        data/lib/MrMurano/SyncUpDown.rb
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Last Modified: 2017.08. 
     | 
| 
      
 1 
     | 
    
         
            +
            # Last Modified: 2017.08.23 /coding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            # Copyright © 2016-2017 Exosite LLC.
         
     | 
| 
         @@ -18,6 +18,7 @@ require 'MrMurano/verbosing' 
     | 
|
| 
       18 
18 
     | 
    
         
             
            require 'MrMurano/hash'
         
     | 
| 
       19 
19 
     | 
    
         
             
            #require 'MrMurano/Config'
         
     | 
| 
       20 
20 
     | 
    
         
             
            #require 'MrMurano/ProjectFile'
         
     | 
| 
      
 21 
     | 
    
         
            +
            require 'MrMurano/SyncAllowed'
         
     | 
| 
       21 
22 
     | 
    
         
             
            ##require 'MrMurano/SyncRoot'
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
            module MrMurano
         
     | 
| 
         @@ -27,6 +28,8 @@ module MrMurano 
     | 
|
| 
       27 
28 
     | 
    
         
             
              # pulling those things.
         
     | 
| 
       28 
29 
     | 
    
         
             
              #
         
     | 
| 
       29 
30 
     | 
    
         
             
              module SyncUpDown
         
     | 
| 
      
 31 
     | 
    
         
            +
                include SyncAllowed
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       30 
33 
     | 
    
         
             
                # This is one item that can be synced.
         
     | 
| 
       31 
34 
     | 
    
         
             
                class Item
         
     | 
| 
       32 
35 
     | 
    
         
             
                  # @return [String] The name of this item.
         
     | 
| 
         @@ -49,8 +52,6 @@ module MrMurano 
     | 
|
| 
       49 
52 
     | 
    
         
             
                  attr_accessor :synckey
         
     | 
| 
       50 
53 
     | 
    
         
             
                  # @return [String] The syncable type.
         
     | 
| 
       51 
54 
     | 
    
         
             
                  attr_accessor :synctype
         
     | 
| 
       52 
     | 
    
         
            -
                  # @return [String] For device2, the event type.
         
     | 
| 
       53 
     | 
    
         
            -
                  attr_accessor :type
         
     | 
| 
       54 
55 
     | 
    
         
             
                  # @return [String] The updated_at time from the server is used to detect changes.
         
     | 
| 
       55 
56 
     | 
    
         
             
                  attr_accessor :updated_at
         
     | 
| 
       56 
57 
     | 
    
         
             
                  # @return [Integer] Positive if multiple conflicting files found for same item.
         
     | 
| 
         @@ -295,11 +296,10 @@ module MrMurano 
     | 
|
| 
       295 
296 
     | 
    
         
             
                # @param local [Pathname] Full path of where to download to
         
     | 
| 
       296 
297 
     | 
    
         
             
                # @param item [Item] The item to download
         
     | 
| 
       297 
298 
     | 
    
         
             
                def download(local, item)
         
     | 
| 
       298 
     | 
    
         
            -
            # 
     | 
| 
       299 
     | 
    
         
            -
            # 
     | 
| 
       300 
     | 
    
         
            -
            # 
     | 
| 
       301 
     | 
    
         
            -
            # 
     | 
| 
       302 
     | 
    
         
            -
                  local.dirname.mkpath
         
     | 
| 
      
 299 
     | 
    
         
            +
                  #if item[:bundled]
         
     | 
| 
      
 300 
     | 
    
         
            +
                  #  warning "Not downloading into bundled item #{synckey(item)}"
         
     | 
| 
      
 301 
     | 
    
         
            +
                  #  return
         
     | 
| 
      
 302 
     | 
    
         
            +
                  #end
         
     | 
| 
       303 
303 
     | 
    
         
             
                  id = item[@itemkey.to_sym]
         
     | 
| 
       304 
304 
     | 
    
         
             
                  if id.nil?
         
     | 
| 
       305 
305 
     | 
    
         
             
                    debug "!!! Missing '#{@itemkey}', using :id instead!"
         
     | 
| 
         @@ -307,9 +307,14 @@ module MrMurano 
     | 
|
| 
       307 
307 
     | 
    
         
             
                    id = item[:id]
         
     | 
| 
       308 
308 
     | 
    
         
             
                    raise "Both #{@itemkey} and id in item are nil!" if id.nil?
         
     | 
| 
       309 
309 
     | 
    
         
             
                  end
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
      
 311 
     | 
    
         
            +
                  relpath = local.relative_path_from(Pathname.pwd).to_s
         
     | 
| 
      
 312 
     | 
    
         
            +
                  return unless download_item_allowed(relpath)
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
      
 314 
     | 
    
         
            +
                  local.dirname.mkpath
         
     | 
| 
       310 
315 
     | 
    
         
             
                  local.open('wb') do |io|
         
     | 
| 
       311 
316 
     | 
    
         
             
                    fetch(id) do |chunk|
         
     | 
| 
       312 
     | 
    
         
            -
                      io.write chunk
         
     | 
| 
      
 317 
     | 
    
         
            +
                      io.write config_vars_encode chunk
         
     | 
| 
       313 
318 
     | 
    
         
             
                    end
         
     | 
| 
       314 
319 
     | 
    
         
             
                  end
         
     | 
| 
       315 
320 
     | 
    
         
             
                  update_mtime(local, item)
         
     | 
| 
         @@ -390,6 +395,7 @@ module MrMurano 
     | 
|
| 
       390 
395 
     | 
    
         
             
                # @param dest [Pathname] Full path of item to be removed
         
     | 
| 
       391 
396 
     | 
    
         
             
                # @param item [Item] Full details of item to be removed
         
     | 
| 
       392 
397 
     | 
    
         
             
                def removelocal(dest, _item)
         
     | 
| 
      
 398 
     | 
    
         
            +
                  return unless removelocal_item_allowed(dest)
         
     | 
| 
       393 
399 
     | 
    
         
             
                  dest.unlink if dest.exist?
         
     | 
| 
       394 
400 
     | 
    
         
             
                end
         
     | 
| 
       395 
401 
     | 
    
         | 
| 
         @@ -408,7 +414,9 @@ module MrMurano 
     | 
|
| 
       408 
414 
     | 
    
         
             
                end
         
     | 
| 
       409 
415 
     | 
    
         | 
| 
       410 
416 
     | 
    
         
             
                def diff_item_write(io, merged, _local, _remote)
         
     | 
| 
       411 
     | 
    
         
            -
                   
     | 
| 
      
 417 
     | 
    
         
            +
                  contents = merged[:local_path].read
         
     | 
| 
      
 418 
     | 
    
         
            +
                  contents = config_vars_decode(contents)
         
     | 
| 
      
 419 
     | 
    
         
            +
                  io << contents
         
     | 
| 
       412 
420 
     | 
    
         
             
                end
         
     | 
| 
       413 
421 
     | 
    
         | 
| 
       414 
422 
     | 
    
         
             
                #
         
     | 
| 
         @@ -458,33 +466,36 @@ module MrMurano 
     | 
|
| 
       458 
466 
     | 
    
         
             
                    # Get a list of SyncUpDown::Item's, or a class derived thereof.
         
     | 
| 
       459 
467 
     | 
    
         
             
                    bitems = localitems(location)
         
     | 
| 
       460 
468 
     | 
    
         
             
                    # Use synckey for quicker merging.
         
     | 
| 
       461 
     | 
    
         
            -
                    # 2017-07-02: Argh. If two files have the same identity, this
         
     | 
| 
       462 
     | 
    
         
            -
                    # simple loop masks that there are two files with the same identity!
         
     | 
| 
       463 
469 
     | 
    
         
             
                    #bitems.each { |b| items[synckey(b)] = b }
         
     | 
| 
       464 
     | 
    
         
            -
                     
     | 
| 
      
 470 
     | 
    
         
            +
                    # 2017-07-02: If two files have the same identity, the simple loop
         
     | 
| 
      
 471 
     | 
    
         
            +
                    #   masks that there are two files with the same identity. So check
         
     | 
| 
      
 472 
     | 
    
         
            +
                    #   first for duplicates, and then process each item.
         
     | 
| 
      
 473 
     | 
    
         
            +
                    seen = {}
         
     | 
| 
       465 
474 
     | 
    
         
             
                    bitems.each do |item|
         
     | 
| 
       466 
475 
     | 
    
         
             
                      skey = synckey(item)
         
     | 
| 
       467 
     | 
    
         
            -
                       
     | 
| 
       468 
     | 
    
         
            -
             
     | 
| 
       469 
     | 
    
         
            -
             
     | 
| 
       470 
     | 
    
         
            -
             
     | 
| 
       471 
     | 
    
         
            -
             
     | 
| 
       472 
     | 
    
         
            -
             
     | 
| 
       473 
     | 
    
         
            -
             
     | 
| 
       474 
     | 
    
         
            -
                           
     | 
| 
       475 
     | 
    
         
            -
                           
     | 
| 
       476 
     | 
    
         
            -
                          # Don't delete the original item, so that other dupes see it.
         
     | 
| 
       477 
     | 
    
         
            -
                          #items.delete(skey)
         
     | 
| 
       478 
     | 
    
         
            -
                          msg = "Duplicate local file(s) found for ‘#{skey}’"
         
     | 
| 
       479 
     | 
    
         
            -
                          msg += " for ‘#{self.class.description}’" if self.class.description.to_s != ''
         
     | 
| 
       480 
     | 
    
         
            -
                          #msg += '!'
         
     | 
| 
       481 
     | 
    
         
            -
                          warning(msg)
         
     | 
| 
      
 476 
     | 
    
         
            +
                      seen[skey] = seen.key?(skey) && seen[skey] + 1 || 1
         
     | 
| 
      
 477 
     | 
    
         
            +
                    end
         
     | 
| 
      
 478 
     | 
    
         
            +
                    counts = {}
         
     | 
| 
      
 479 
     | 
    
         
            +
                    bitems.each do |item|
         
     | 
| 
      
 480 
     | 
    
         
            +
                      skey = synckey(item)
         
     | 
| 
      
 481 
     | 
    
         
            +
                      if seen[skey] > 1
         
     | 
| 
      
 482 
     | 
    
         
            +
                        if items[skey].nil?
         
     | 
| 
      
 483 
     | 
    
         
            +
                          items[skey] = MrMurano::EventHandler::EventHandlerItem.new(item)
         
     | 
| 
      
 484 
     | 
    
         
            +
                          items[skey][:dup_count] = 0
         
     | 
| 
       482 
485 
     | 
    
         
             
                        end
         
     | 
| 
       483 
     | 
    
         
            -
                         
     | 
| 
       484 
     | 
    
         
            -
                         
     | 
| 
       485 
     | 
    
         
            -
                         
     | 
| 
       486 
     | 
    
         
            -
                        item[ 
     | 
| 
       487 
     | 
    
         
            -
                         
     | 
| 
      
 486 
     | 
    
         
            +
                        counts[skey] = counts.key?(skey) && counts[skey] + 1 || 1
         
     | 
| 
      
 487 
     | 
    
         
            +
                        # Use a unique synckey so all duplicates make it in the list.
         
     | 
| 
      
 488 
     | 
    
         
            +
                        uniq_synckey = "#{skey}-#{counts[skey]}"
         
     | 
| 
      
 489 
     | 
    
         
            +
                        item[:dup_count] = counts[skey]
         
     | 
| 
      
 490 
     | 
    
         
            +
                        # This sets the alias for the output, so duplicates look unique.
         
     | 
| 
      
 491 
     | 
    
         
            +
                        item[@itemkey.to_sym] = uniq_synckey
         
     | 
| 
      
 492 
     | 
    
         
            +
                        items[uniq_synckey] = item
         
     | 
| 
      
 493 
     | 
    
         
            +
                        msg = "Duplicate definition found for #{fancy_ticks(skey)}"
         
     | 
| 
      
 494 
     | 
    
         
            +
                        if self.class.description.to_s != ''
         
     | 
| 
      
 495 
     | 
    
         
            +
                          msg += " for #{fancy_ticks(self.class.description)}"
         
     | 
| 
      
 496 
     | 
    
         
            +
                        end
         
     | 
| 
      
 497 
     | 
    
         
            +
                        warning(msg)
         
     | 
| 
      
 498 
     | 
    
         
            +
                        warning(" #{item.local_path}")
         
     | 
| 
       488 
499 
     | 
    
         
             
                      else
         
     | 
| 
       489 
500 
     | 
    
         
             
                        items[skey] = item
         
     | 
| 
       490 
501 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -495,9 +506,10 @@ module MrMurano 
     | 
|
| 
       495 
506 
     | 
    
         
             
                      # MEH/2017-07-31: This message is a little misleading on syncdown,
         
     | 
| 
       496 
507 
     | 
    
         
             
                      #   e.g., in rspec ./spec/cmd_syncdown_spec.rb, one test blows away
         
     | 
| 
       497 
508 
     | 
    
         
             
                      #   local directories and does a syncdown, and on stderr you'll see
         
     | 
| 
       498 
     | 
    
         
            -
                      #     Skipping missing location 
     | 
| 
      
 509 
     | 
    
         
            +
                      #     Skipping missing location
         
     | 
| 
      
 510 
     | 
    
         
            +
                      #      ‘/tmp/d20170731-3150-1f50uj4/project/specs/resources.yaml’ (Resources)
         
     | 
| 
       499 
511 
     | 
    
         
             
                      #   but then later in the syncdown, that directory and file gets created.
         
     | 
| 
       500 
     | 
    
         
            -
                      msg = "Skipping missing location  
     | 
| 
      
 512 
     | 
    
         
            +
                      msg = "Skipping missing location #{fancy_ticks(location)}"
         
     | 
| 
       501 
513 
     | 
    
         
             
                      unless self.class.description.to_s.empty?
         
     | 
| 
       502 
514 
     | 
    
         
             
                        msg += " (#{Inflecto.pluralize(self.class.description)})"
         
     | 
| 
       503 
515 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -605,7 +617,21 @@ module MrMurano 
     | 
|
| 
       605 
617 
     | 
    
         
             
                    pattern = pattern.gsub(%r{^\*\*\/}, '')
         
     | 
| 
       606 
618 
     | 
    
         
             
                  end
         
     | 
| 
       607 
619 
     | 
    
         | 
| 
       608 
     | 
    
         
            -
                  ::File.fnmatch(pattern, path)
         
     | 
| 
      
 620 
     | 
    
         
            +
                  ignore = ::File.fnmatch(pattern, path)
         
     | 
| 
      
 621 
     | 
    
         
            +
                  debug "Excluded #{path}" if ignore
         
     | 
| 
      
 622 
     | 
    
         
            +
                  ignore
         
     | 
| 
      
 623 
     | 
    
         
            +
                end
         
     | 
| 
      
 624 
     | 
    
         
            +
             
     | 
| 
      
 625 
     | 
    
         
            +
                def resolve_config_var_usage!(there, local)
         
     | 
| 
      
 626 
     | 
    
         
            +
                  # pass; derived classes should implement.
         
     | 
| 
      
 627 
     | 
    
         
            +
                end
         
     | 
| 
      
 628 
     | 
    
         
            +
             
     | 
| 
      
 629 
     | 
    
         
            +
                def config_vars_decode(script)
         
     | 
| 
      
 630 
     | 
    
         
            +
                  script
         
     | 
| 
      
 631 
     | 
    
         
            +
                end
         
     | 
| 
      
 632 
     | 
    
         
            +
             
     | 
| 
      
 633 
     | 
    
         
            +
                def config_vars_encode(script)
         
     | 
| 
      
 634 
     | 
    
         
            +
                  script
         
     | 
| 
       609 
635 
     | 
    
         
             
                end
         
     | 
| 
       610 
636 
     | 
    
         | 
| 
       611 
637 
     | 
    
         
             
                #######################################################################
         
     | 
| 
         @@ -669,16 +695,11 @@ module MrMurano 
     | 
|
| 
       669 
695 
     | 
    
         | 
| 
       670 
696 
     | 
    
         
             
                def syncup_item(item, options, action, verbage)
         
     | 
| 
       671 
697 
     | 
    
         
             
                  if options[action]
         
     | 
| 
       672 
     | 
    
         
            -
                     
     | 
| 
       673 
     | 
    
         
            -
             
     | 
| 
       674 
     | 
    
         
            -
             
     | 
| 
       675 
     | 
    
         
            -
             
     | 
| 
       676 
     | 
    
         
            -
             
     | 
| 
       677 
     | 
    
         
            -
                    else
         
     | 
| 
       678 
     | 
    
         
            -
                      MrMurano::Verbose.whirly_interject do
         
     | 
| 
       679 
     | 
    
         
            -
                        say("--dry: Not #{verbage.downcase} item #{item[:synckey]}")
         
     | 
| 
       680 
     | 
    
         
            -
                      end
         
     | 
| 
       681 
     | 
    
         
            -
                    end
         
     | 
| 
      
 698 
     | 
    
         
            +
                    # It's up to the callback to check and honor $cfg['tool.dry'].
         
     | 
| 
      
 699 
     | 
    
         
            +
                    prog_msg = "#{verbage.capitalize} item #{item[:synckey]}"
         
     | 
| 
      
 700 
     | 
    
         
            +
                    prog_msg += " (#{item[:synctype]})" if $cfg['tool.verbose']
         
     | 
| 
      
 701 
     | 
    
         
            +
                    sync_update_progress(prog_msg)
         
     | 
| 
      
 702 
     | 
    
         
            +
                    yield item
         
     | 
| 
       682 
703 
     | 
    
         
             
                  elsif $cfg['tool.verbose']
         
     | 
| 
       683 
704 
     | 
    
         
             
                    MrMurano::Verbose.whirly_interject do
         
     | 
| 
       684 
705 
     | 
    
         
             
                      say("--no-#{action}: Not #{verbage.downcase} item #{item[:synckey]}")
         
     | 
| 
         @@ -734,15 +755,11 @@ module MrMurano 
     | 
|
| 
       734 
755 
     | 
    
         | 
| 
       735 
756 
     | 
    
         
             
                def syncdown_item(item, into, options, action, verbage)
         
     | 
| 
       736 
757 
     | 
    
         
             
                  if options[action]
         
     | 
| 
       737 
     | 
    
         
            -
                     
     | 
| 
       738 
     | 
    
         
            -
             
     | 
| 
       739 
     | 
    
         
            -
             
     | 
| 
       740 
     | 
    
         
            -
             
     | 
| 
       741 
     | 
    
         
            -
             
     | 
| 
       742 
     | 
    
         
            -
                      yield dest, item
         
     | 
| 
       743 
     | 
    
         
            -
                    else
         
     | 
| 
       744 
     | 
    
         
            -
                      say("--dry: Not #{verbage.downcase} item #{item[:synckey]}")
         
     | 
| 
       745 
     | 
    
         
            -
                    end
         
     | 
| 
      
 758 
     | 
    
         
            +
                    prog_msg = "#{verbage.capitalize} item #{item[:synckey]}"
         
     | 
| 
      
 759 
     | 
    
         
            +
                    prog_msg += " (#{item[:synctype]})" if $cfg['tool.verbose']
         
     | 
| 
      
 760 
     | 
    
         
            +
                    sync_update_progress(prog_msg)
         
     | 
| 
      
 761 
     | 
    
         
            +
                    dest = tolocalpath(into, item)
         
     | 
| 
      
 762 
     | 
    
         
            +
                    yield dest, item
         
     | 
| 
       746 
763 
     | 
    
         
             
                  elsif $cfg['tool.verbose']
         
     | 
| 
       747 
764 
     | 
    
         
             
                    say("--no-#{action}: Not #{verbage.downcase} item #{item[:synckey]}")
         
     | 
| 
       748 
765 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -760,7 +777,7 @@ module MrMurano 
     | 
|
| 
       760 
777 
     | 
    
         
             
                  tlcl = Tempfile.new([tolocalname(merged, @itemkey) + '_local_', '.lua'])
         
     | 
| 
       761 
778 
     | 
    
         
             
                  Pathname.new(tlcl.path).open('wb') do |io|
         
     | 
| 
       762 
779 
     | 
    
         
             
                    if merged.key?(:script)
         
     | 
| 
       763 
     | 
    
         
            -
                      io << merged[:script]
         
     | 
| 
      
 780 
     | 
    
         
            +
                      io << config_vars_decode(merged[:script])
         
     | 
| 
       764 
781 
     | 
    
         
             
                    else
         
     | 
| 
       765 
782 
     | 
    
         
             
                      # For most items, read the local file.
         
     | 
| 
       766 
783 
     | 
    
         
             
                      # For resources, it's a bit trickier.
         
     | 
| 
         @@ -864,15 +881,25 @@ module MrMurano 
     | 
|
| 
       864 
881 
     | 
    
         | 
| 
       865 
882 
     | 
    
         
             
                  tomod, unchg = items_mods_and_chgs(options, therebox, localbox)
         
     | 
| 
       866 
883 
     | 
    
         | 
| 
      
 884 
     | 
    
         
            +
                  clash = items_cull_clashes!([toadd, todel, tomod, unchg])
         
     | 
| 
      
 885 
     | 
    
         
            +
             
     | 
| 
       867 
886 
     | 
    
         
             
                  if options[:unselected]
         
     | 
| 
       868 
     | 
    
         
            -
                    { 
     | 
| 
      
 887 
     | 
    
         
            +
                    {
         
     | 
| 
      
 888 
     | 
    
         
            +
                      toadd: toadd,
         
     | 
| 
      
 889 
     | 
    
         
            +
                      todel: todel,
         
     | 
| 
      
 890 
     | 
    
         
            +
                      tomod: tomod,
         
     | 
| 
      
 891 
     | 
    
         
            +
                      unchg: unchg,
         
     | 
| 
      
 892 
     | 
    
         
            +
                      skipd: [],
         
     | 
| 
      
 893 
     | 
    
         
            +
                      clash: clash,
         
     | 
| 
      
 894 
     | 
    
         
            +
                    }
         
     | 
| 
       869 
895 
     | 
    
         
             
                  else
         
     | 
| 
       870 
896 
     | 
    
         
             
                    {
         
     | 
| 
       871 
     | 
    
         
            -
                      toadd: toadd 
     | 
| 
       872 
     | 
    
         
            -
                      todel: todel 
     | 
| 
       873 
     | 
    
         
            -
                      tomod: tomod 
     | 
| 
       874 
     | 
    
         
            -
                      unchg: unchg 
     | 
| 
      
 897 
     | 
    
         
            +
                      toadd: select_selected(toadd),
         
     | 
| 
      
 898 
     | 
    
         
            +
                      todel: select_selected(todel),
         
     | 
| 
      
 899 
     | 
    
         
            +
                      tomod: select_selected(tomod),
         
     | 
| 
      
 900 
     | 
    
         
            +
                      unchg: select_selected(unchg),
         
     | 
| 
       875 
901 
     | 
    
         
             
                      skipd: [],
         
     | 
| 
      
 902 
     | 
    
         
            +
                      clash: select_selected(clash),
         
     | 
| 
       876 
903 
     | 
    
         
             
                    }
         
     | 
| 
       877 
904 
     | 
    
         
             
                  end
         
     | 
| 
       878 
905 
     | 
    
         
             
                end
         
     | 
| 
         @@ -928,7 +955,7 @@ module MrMurano 
     | 
|
| 
       928 
955 
     | 
    
         
             
                    skip_sol = true if tested && !passed
         
     | 
| 
       929 
956 
     | 
    
         
             
                  end
         
     | 
| 
       930 
957 
     | 
    
         
             
                  return nil unless skip_sol
         
     | 
| 
       931 
     | 
    
         
            -
                  ret = { toadd: [], todel: [], tomod: [], unchg: [], skipd: [] }
         
     | 
| 
      
 958 
     | 
    
         
            +
                  ret = { toadd: [], todel: [], tomod: [], unchg: [], skipd: [], clash: [] }
         
     | 
| 
       932 
959 
     | 
    
         
             
                  ret[:skipd] << { synckey: self.class.description }
         
     | 
| 
       933 
960 
     | 
    
         
             
                  ret
         
     | 
| 
       934 
961 
     | 
    
         
             
                end
         
     | 
| 
         @@ -945,8 +972,11 @@ module MrMurano 
     | 
|
| 
       945 
972 
     | 
    
         
             
                def items_lists(options, selected)
         
     | 
| 
       946 
973 
     | 
    
         
             
                  # Fetch arrays of items there, and items here/local.
         
     | 
| 
       947 
974 
     | 
    
         
             
                  there = list
         
     | 
| 
       948 
     | 
    
         
            -
                  there = _matcher(there, selected)
         
     | 
| 
       949 
975 
     | 
    
         
             
                  local = locallist(skip_warn: options[:skip_missing_warning])
         
     | 
| 
      
 976 
     | 
    
         
            +
             
     | 
| 
      
 977 
     | 
    
         
            +
                  resolve_config_var_usage!(there, local)
         
     | 
| 
      
 978 
     | 
    
         
            +
             
     | 
| 
      
 979 
     | 
    
         
            +
                  there = _matcher(there, selected)
         
     | 
| 
       950 
980 
     | 
    
         
             
                  local = _matcher(local, selected)
         
     | 
| 
       951 
981 
     | 
    
         | 
| 
       952 
982 
     | 
    
         
             
                  therebox = {}
         
     | 
| 
         @@ -960,10 +990,12 @@ module MrMurano 
     | 
|
| 
       960 
990 
     | 
    
         
             
                  local.each do |item|
         
     | 
| 
       961 
991 
     | 
    
         
             
                    skey = synckey(item)
         
     | 
| 
       962 
992 
     | 
    
         
             
                    # 2017-07-02: Check for local duplicates.
         
     | 
| 
       963 
     | 
    
         
            -
                     
     | 
| 
      
 993 
     | 
    
         
            +
                    unless item[:dup_count].nil? || item[:dup_count].zero?
         
     | 
| 
      
 994 
     | 
    
         
            +
                      skey += "-#{item[:dup_count]}"
         
     | 
| 
      
 995 
     | 
    
         
            +
                    end
         
     | 
| 
       964 
996 
     | 
    
         
             
                    item[:synckey] = skey
         
     | 
| 
       965 
997 
     | 
    
         
             
                    item[:synctype] = self.class.description
         
     | 
| 
       966 
     | 
    
         
            -
                    localbox[ 
     | 
| 
      
 998 
     | 
    
         
            +
                    localbox[skey] = item
         
     | 
| 
       967 
999 
     | 
    
         
             
                  end
         
     | 
| 
       968 
1000 
     | 
    
         | 
| 
       969 
1001 
     | 
    
         
             
                  # Some items are considered "undeletable", meaning if a
         
     | 
| 
         @@ -990,6 +1022,8 @@ module MrMurano 
     | 
|
| 
       990 
1022 
     | 
    
         
             
                  unchg = []
         
     | 
| 
       991 
1023 
     | 
    
         | 
| 
       992 
1024 
     | 
    
         
             
                  (localbox.keys & therebox.keys).each do |key|
         
     | 
| 
      
 1025 
     | 
    
         
            +
                    # Skip this item if it's got duplicate conflicts.
         
     | 
| 
      
 1026 
     | 
    
         
            +
                    next if !localbox[key].is_a?(Hash) && localbox[key].dup_count == 0
         
     | 
| 
       993 
1027 
     | 
    
         
             
                    # Want 'local' to override 'there' except for itemkey.
         
     | 
| 
       994 
1028 
     | 
    
         
             
                    if options[:asdown]
         
     | 
| 
       995 
1029 
     | 
    
         
             
                      mrg = therebox[key].reject { |k, _v| k == @itemkey.to_sym }
         
     | 
| 
         @@ -1021,6 +1055,29 @@ module MrMurano 
     | 
|
| 
       1021 
1055 
     | 
    
         
             
                    list.sort_by(&:name)
         
     | 
| 
       1022 
1056 
     | 
    
         
             
                  end
         
     | 
| 
       1023 
1057 
     | 
    
         
             
                end
         
     | 
| 
      
 1058 
     | 
    
         
            +
             
     | 
| 
      
 1059 
     | 
    
         
            +
                def select_selected(items)
         
     | 
| 
      
 1060 
     | 
    
         
            +
                  items.select { |i| i[:selected] }.map { |i| i.delete(:selected); i }
         
     | 
| 
      
 1061 
     | 
    
         
            +
                end
         
     | 
| 
      
 1062 
     | 
    
         
            +
             
     | 
| 
      
 1063 
     | 
    
         
            +
                def items_cull_clashes!(items_list)
         
     | 
| 
      
 1064 
     | 
    
         
            +
                  items_list = [items_list] unless items_list.is_a?(Array)
         
     | 
| 
      
 1065 
     | 
    
         
            +
                  clash = []
         
     | 
| 
      
 1066 
     | 
    
         
            +
                  items_list.each do |items|
         
     | 
| 
      
 1067 
     | 
    
         
            +
                    items.select! do |item|
         
     | 
| 
      
 1068 
     | 
    
         
            +
                      if item[:dup_count].nil?
         
     | 
| 
      
 1069 
     | 
    
         
            +
                        true
         
     | 
| 
      
 1070 
     | 
    
         
            +
                      elsif item[:dup_count].zero?
         
     | 
| 
      
 1071 
     | 
    
         
            +
                        # This is the control item.
         
     | 
| 
      
 1072 
     | 
    
         
            +
                        false
         
     | 
| 
      
 1073 
     | 
    
         
            +
                      else
         
     | 
| 
      
 1074 
     | 
    
         
            +
                        clash.push(item)
         
     | 
| 
      
 1075 
     | 
    
         
            +
                        false
         
     | 
| 
      
 1076 
     | 
    
         
            +
                      end
         
     | 
| 
      
 1077 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1078 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1079 
     | 
    
         
            +
                  clash
         
     | 
| 
      
 1080 
     | 
    
         
            +
                end
         
     | 
| 
       1024 
1081 
     | 
    
         
             
              end
         
     | 
| 
       1025 
1082 
     | 
    
         
             
            end
         
     | 
| 
       1026 
1083 
     | 
    
         |