On Jul 27, 2009, at 9:52 AM, Steven Jenkins wrote:

>
> Luke Kanies wrote:
> ...
>>> +    # Deal with backups.
>>> +    def handlebackup(file = nil)
>>> +        # let the path be specified
>>> +        file ||= self[:path]
>>> +        return true unless FileTest.exists?(file)
>>> +        # if they specifically don't want a backup, then just say
>>> +        # we're good
>>> +        return true unless self[:backup]
>>> +        unless self.bucket || self[:backup]
>>> +            self.err "Need a bucket or a backup setting for  
>>> backups"
>>> +            return false
>>> +        end
>>
>> This code isn't really necessary, is it?  We already return  
>> if :backup
>> isn't set, which should cover all of the bases, since the 'bucket'
>> value can only be derived from :backup.
>>
>
> I left that code in the handlebackup method in case setup code for  
> 'we have a
> filebucket, but we set 'backup' to false); however, that's getting  
> normalized
> earlier in the process (e.g., at File.new).
>
> I don't think that leaving the explicit checks in is bad, though, as  
> defensive
> programming is a good idea in general.

If we are confident of the validation of the :backup value, and I  
think we are, then I think we should leave this out.  The only real  
input, other than the file name, to this method is a result of  
the :backup setting, so we are closely coupled to it already.

>
>>> +        return handlebucket(file) if self.bucket
>>> +        return handlebackuplocal(file, self[:backup])
>>
>> Is it reasonable to get rid of the 'handle' term here? I think I  
>> did a
>> poor job of naming the method in the first place; I used to have a
>> tendency to use generic verbs all over the place -- evaluate, handle,
>> etc.  It probably even makes sense to just change the 'handlebackup'
>> entry point to be just 'perform_backup' or something similar.  And
>> while most old code doesn't do it, I've been working toward more
>> common naming methods:  Longer method names, using underscores.
>>
>
> Sure.  s/handle/perform_/ is not a problem.

Great.

>
>>> +    end
>>> +
>>> +    private
>>> +
>>> +    def handlebucket(fileobj)
>>> +        file = (fileobj.class == String) ? fileobj : fileobj.name
>>> +        case File.stat(file).ftype
>>> +        when "directory"
>>> +            # we don't need to backup directories when recurse is  
>>> on
>>> +            return true if self[:recurse]
>>> +            info "Recursively backing up to filebucket"
>>> +            require 'find'
>>> +            Find.find(file) do |f|
>>> +                if File.file?(f)
>>> +                    sum = self.bucket.backup(f)
>>> +                    self.info "Filebucketed %s to %s with sum %s" %
>>> +                        [f, self.bucket.name, sum]
>>> +                end
>>> +            end
>>> +            return true
>>> +        when "file"
>>> +            sum = self.bucket.backup(file)
>>> +            self.info "Filebucketed %s to %s with sum %s" %
>>> +                [file, self.bucket.name, sum]
>>> +            return true
>>> +        when "link"; return true
>>> +        end
>>> +    end
>>
>> I'd still like to see this method broken into two methods:  One with
>> the case statement, and one to do the backing up.
>
> I'm not sure I follow you here.  If we move the actual backing up  
> into a
> separate method, the current 'handlebackup' method is only going to  
> call that
> method, so it's purpose is unclear to me.

As I mentioned in my first comment, you would start with a method like  
this:

  def backup_with_filebucket
    ftype = stat().ftype # this is part of the file type already

    case ftype
    when "directory"
      Find.find(self[:path]) { |f| backup_file_with_filebucket(f) if  
File.file?(f) }
    when "file"; backup_file_with_filebucket(f)
    when "link"; return true
    end
  end

And then you have:

def backup_file_with_filebucket(f)
   sum = self.bucket.backup(f)
   self.info "Filebucketed %s to %s with sum %s" % f,  
self.bucket.name, sum]
end

Should end up being a bit shorter, and at worst won't be any longer,  
and you now have two simple methods: One that does the branching, and  
one that does the work.  You've also removed the small amount of code  
duplication (the part that actually calls out to the filebucket).

>
> On the other hand, I can see having two new methods: one to  handle  
> directories
> and one to handle files, and having perform_bucket call those, but  
> the current
> method is fairly small, so breaking it up into even smaller pieces  
> doesn't seem
> to be a win.

I think the current method is a combination of enough length and  
enough nesting that it's hard to read, and it's certainly hard to see  
that the heart of the method -- calling out to the filebucket client  
-- is actually duplicated (as it was in the original code).

>
> Let me know how you'd like to proceed on this.
> ...
>>> +
>>> +describe Puppet::Util::Backups do
>>> +    describe "when backing up a file" do
> ...
>> We should be moving all backup-related tests from the file tests into
>> this test module.  Initially just copy them, make sure they all still
>> work, and once you're confident in the new tests, just add a test to
>> the file tests to verify this method gets called appropriately and
>> remove the old tests.
>>
>
> Will do.
>
>>> +    end
>>> +end
>>
>> For the tests, you can make a simple test class that skips all of the
>> normal complexity:
>>
>> class BackupTesting
>>   include Puppet::Util::Backups
>> end
>>
>> describe Puppet::Util::Backups do
>>   before do
>>     @backup = BackupTesting.new
>>   end
>>   ...
>> end
>>
>> Then you can skip all of the interactions with the type and focus on
>> the behaviour of this module.
>
> You're still going to have to do
>
> SomeType.new
>
> Going via the File type is at least close to how the code is used in  
> practice.
> I see that as a toss-up decision.

That's true.

>
>> I'm even considering whether it makes
>> sense to pass the needed information to the method, so the file can
>> call it something like this:
>>
>>   perform_backup(self[:path], self[:backup], self.bucket)
>>
>> I think that's overkill in this case, but it's appealing.
>>
>
> Methods that take parameters rather than depend on implicit types  
> are easier to
> understand; however, from what I can tell, that is far more  
> widespread than this
> part of the code.  I suggest doing that refactoring as a different  
> commit: e.g.,
> do a single commit that does that normalization throughout an entire  
> directory;
> this commit already mixes far more changes than I'd normally suggest  
> in a single
> patch.

I think leaving the method signature like it is makes sense; it was  
just something I was considering.

-- 
Progress isn't made by early risers. It's made by lazy men trying to
find easier ways to do something. --Robert Heinlein
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to