dm-svn 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +23 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/dm-svn.gemspec +85 -0
- data/lib/dm-svn.rb +13 -0
- data/lib/dm-svn/config.rb +38 -0
- data/lib/dm-svn/model.rb +12 -0
- data/lib/dm-svn/svn.rb +144 -0
- data/lib/dm-svn/svn/categorized.rb +113 -0
- data/lib/dm-svn/svn/changeset.rb +119 -0
- data/lib/dm-svn/svn/node.rb +128 -0
- data/lib/dm-svn/svn/sync.rb +85 -0
- data/spec/dm-svn/config_spec.rb +51 -0
- data/spec/dm-svn/database.yml +16 -0
- data/spec/dm-svn/fixtures/articles_comments.rb +95 -0
- data/spec/dm-svn/mock_models.rb +53 -0
- data/spec/dm-svn/model_spec.rb +5 -0
- data/spec/dm-svn/spec_helper.rb +50 -0
- data/spec/dm-svn/svn/categorized_spec.rb +138 -0
- data/spec/dm-svn/svn/changeset_spec.rb +42 -0
- data/spec/dm-svn/svn/node_spec.rb +125 -0
- data/spec/dm-svn/svn/sync_spec.rb +111 -0
- data/spec/dm-svn/svn_spec.rb +213 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_helper.rb +23 -0
- metadata +132 -0
    
        data/.gitignore
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            .DS_Store
         | 
| 2 | 
            +
            log/*
         | 
| 3 | 
            +
            tmp/*
         | 
| 4 | 
            +
            TAGS
         | 
| 5 | 
            +
            *~
         | 
| 6 | 
            +
            .#*
         | 
| 7 | 
            +
            schema/schema.rb
         | 
| 8 | 
            +
            schema/*_structure.sql
         | 
| 9 | 
            +
            schema/*.sqlite3
         | 
| 10 | 
            +
            schema/*.sqlite
         | 
| 11 | 
            +
            schema/*.db
         | 
| 12 | 
            +
            *.sqlite
         | 
| 13 | 
            +
            *.sqlite3
         | 
| 14 | 
            +
            *.db
         | 
| 15 | 
            +
            src/*
         | 
| 16 | 
            +
            .hgignore
         | 
| 17 | 
            +
            .hg/*
         | 
| 18 | 
            +
            .svn/*
         | 
| 19 | 
            +
            .project
         | 
| 20 | 
            +
            .loadpath
         | 
| 21 | 
            +
            lib/wistle/tmp/*
         | 
| 22 | 
            +
            config/recaptcha.yml
         | 
| 23 | 
            +
            *qt_temp*
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'rake'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            begin
         | 
| 5 | 
            +
              require 'jeweler'
         | 
| 6 | 
            +
              Jeweler::Tasks.new do |gem|
         | 
| 7 | 
            +
                gem.name = "dm-svn"
         | 
| 8 | 
            +
                gem.summary = %Q{Sync content from a Subversion repository to a DataMapper model}
         | 
| 9 | 
            +
                gem.description = %Q{dm-svn allows you to store data in a Subversion
         | 
| 10 | 
            +
            repository, then sync that data to a DataMapper model (for example, to a
         | 
| 11 | 
            +
            relational database. Essentially, it allows you app quicker access to the
         | 
| 12 | 
            +
            Subversion data.}
         | 
| 13 | 
            +
                gem.email = "jmorgan@morgancreative.net"
         | 
| 14 | 
            +
                gem.homepage = "http://github.com/jm81/dm-svn"
         | 
| 15 | 
            +
                gem.authors = ["Jared Morgan"]
         | 
| 16 | 
            +
                gem.add_dependency('dm-core', '>= 0.10.0')
         | 
| 17 | 
            +
                gem.add_dependency('dm-aggregates', '>= 0.10.0')
         | 
| 18 | 
            +
                gem.add_dependency('dm-validations', '>= 0.10.0')
         | 
| 19 | 
            +
                gem.add_dependency('jm81-svn-fixture', '>= 0.1.1')
         | 
| 20 | 
            +
                # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
              Jeweler::GemcutterTasks.new
         | 
| 23 | 
            +
            rescue LoadError
         | 
| 24 | 
            +
              puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            require 'spec/rake/spectask'
         | 
| 28 | 
            +
            Spec::Rake::SpecTask.new(:spec) do |spec|
         | 
| 29 | 
            +
              spec.libs << 'lib' << 'spec'
         | 
| 30 | 
            +
              spec.spec_files = FileList['spec/**/*_spec.rb']
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            Spec::Rake::SpecTask.new(:rcov) do |spec|
         | 
| 34 | 
            +
              spec.libs << 'lib' << 'spec'
         | 
| 35 | 
            +
              spec.pattern = 'spec/**/*_spec.rb'
         | 
| 36 | 
            +
              spec.rcov = true
         | 
| 37 | 
            +
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
             | 
| 40 | 
            +
            task :default => :spec
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            require 'rake/rdoctask'
         | 
| 43 | 
            +
            Rake::RDocTask.new do |rdoc|
         | 
| 44 | 
            +
              if File.exist?('VERSION.yml')
         | 
| 45 | 
            +
                config = YAML.load(File.read('VERSION.yml'))
         | 
| 46 | 
            +
                version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
         | 
| 47 | 
            +
              else
         | 
| 48 | 
            +
                version = ""
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              rdoc.rdoc_dir = 'rdoc'
         | 
| 52 | 
            +
              rdoc.title = "dm-svn #{version}"
         | 
| 53 | 
            +
              rdoc.rdoc_files.include('README*')
         | 
| 54 | 
            +
              rdoc.rdoc_files.include('lib/**/*.rb')
         | 
| 55 | 
            +
            end
         | 
| 56 | 
            +
             | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.2.0
         | 
    
        data/dm-svn.gemspec
    ADDED
    
    | @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            # Generated by jeweler
         | 
| 2 | 
            +
            # DO NOT EDIT THIS FILE
         | 
| 3 | 
            +
            # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
         | 
| 4 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |s|
         | 
| 7 | 
            +
              s.name = %q{dm-svn}
         | 
| 8 | 
            +
              s.version = "0.2.0"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 | 
            +
              s.authors = ["Jared Morgan"]
         | 
| 12 | 
            +
              s.date = %q{2009-10-11}
         | 
| 13 | 
            +
              s.description = %q{dm-svn allows you to store data in a Subversion
         | 
| 14 | 
            +
            repository, then sync that data to a DataMapper model (for example, to a
         | 
| 15 | 
            +
            relational database. Essentially, it allows you app quicker access to the
         | 
| 16 | 
            +
            Subversion data.}
         | 
| 17 | 
            +
              s.email = %q{jmorgan@morgancreative.net}
         | 
| 18 | 
            +
              s.files = [
         | 
| 19 | 
            +
                ".gitignore",
         | 
| 20 | 
            +
                 "Rakefile",
         | 
| 21 | 
            +
                 "VERSION",
         | 
| 22 | 
            +
                 "dm-svn.gemspec",
         | 
| 23 | 
            +
                 "lib/dm-svn.rb",
         | 
| 24 | 
            +
                 "lib/dm-svn/config.rb",
         | 
| 25 | 
            +
                 "lib/dm-svn/model.rb",
         | 
| 26 | 
            +
                 "lib/dm-svn/svn.rb",
         | 
| 27 | 
            +
                 "lib/dm-svn/svn/categorized.rb",
         | 
| 28 | 
            +
                 "lib/dm-svn/svn/changeset.rb",
         | 
| 29 | 
            +
                 "lib/dm-svn/svn/node.rb",
         | 
| 30 | 
            +
                 "lib/dm-svn/svn/sync.rb",
         | 
| 31 | 
            +
                 "spec/dm-svn/config_spec.rb",
         | 
| 32 | 
            +
                 "spec/dm-svn/database.yml",
         | 
| 33 | 
            +
                 "spec/dm-svn/fixtures/articles_comments.rb",
         | 
| 34 | 
            +
                 "spec/dm-svn/mock_models.rb",
         | 
| 35 | 
            +
                 "spec/dm-svn/model_spec.rb",
         | 
| 36 | 
            +
                 "spec/dm-svn/spec_helper.rb",
         | 
| 37 | 
            +
                 "spec/dm-svn/svn/categorized_spec.rb",
         | 
| 38 | 
            +
                 "spec/dm-svn/svn/changeset_spec.rb",
         | 
| 39 | 
            +
                 "spec/dm-svn/svn/node_spec.rb",
         | 
| 40 | 
            +
                 "spec/dm-svn/svn/sync_spec.rb",
         | 
| 41 | 
            +
                 "spec/dm-svn/svn_spec.rb",
         | 
| 42 | 
            +
                 "spec/spec.opts",
         | 
| 43 | 
            +
                 "spec/spec_helper.rb"
         | 
| 44 | 
            +
              ]
         | 
| 45 | 
            +
              s.homepage = %q{http://github.com/jm81/dm-svn}
         | 
| 46 | 
            +
              s.rdoc_options = ["--charset=UTF-8"]
         | 
| 47 | 
            +
              s.require_paths = ["lib"]
         | 
| 48 | 
            +
              s.rubygems_version = %q{1.3.5}
         | 
| 49 | 
            +
              s.summary = %q{Sync content from a Subversion repository to a DataMapper model}
         | 
| 50 | 
            +
              s.test_files = [
         | 
| 51 | 
            +
                "spec/dm-svn/config_spec.rb",
         | 
| 52 | 
            +
                 "spec/dm-svn/fixtures/articles_comments.rb",
         | 
| 53 | 
            +
                 "spec/dm-svn/mock_models.rb",
         | 
| 54 | 
            +
                 "spec/dm-svn/model_spec.rb",
         | 
| 55 | 
            +
                 "spec/dm-svn/spec_helper.rb",
         | 
| 56 | 
            +
                 "spec/dm-svn/svn/categorized_spec.rb",
         | 
| 57 | 
            +
                 "spec/dm-svn/svn/changeset_spec.rb",
         | 
| 58 | 
            +
                 "spec/dm-svn/svn/node_spec.rb",
         | 
| 59 | 
            +
                 "spec/dm-svn/svn/sync_spec.rb",
         | 
| 60 | 
            +
                 "spec/dm-svn/svn_spec.rb",
         | 
| 61 | 
            +
                 "spec/spec_helper.rb"
         | 
| 62 | 
            +
              ]
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              if s.respond_to? :specification_version then
         | 
| 65 | 
            +
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         | 
| 66 | 
            +
                s.specification_version = 3
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
         | 
| 69 | 
            +
                  s.add_runtime_dependency(%q<dm-core>, [">= 0.10.0"])
         | 
| 70 | 
            +
                  s.add_runtime_dependency(%q<dm-aggregates>, [">= 0.10.0"])
         | 
| 71 | 
            +
                  s.add_runtime_dependency(%q<dm-validations>, [">= 0.10.0"])
         | 
| 72 | 
            +
                  s.add_runtime_dependency(%q<jm81-svn-fixture>, [">= 0.1.1"])
         | 
| 73 | 
            +
                else
         | 
| 74 | 
            +
                  s.add_dependency(%q<dm-core>, [">= 0.10.0"])
         | 
| 75 | 
            +
                  s.add_dependency(%q<dm-aggregates>, [">= 0.10.0"])
         | 
| 76 | 
            +
                  s.add_dependency(%q<dm-validations>, [">= 0.10.0"])
         | 
| 77 | 
            +
                  s.add_dependency(%q<jm81-svn-fixture>, [">= 0.1.1"])
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
              else
         | 
| 80 | 
            +
                s.add_dependency(%q<dm-core>, [">= 0.10.0"])
         | 
| 81 | 
            +
                s.add_dependency(%q<dm-aggregates>, [">= 0.10.0"])
         | 
| 82 | 
            +
                s.add_dependency(%q<dm-validations>, [">= 0.10.0"])
         | 
| 83 | 
            +
                s.add_dependency(%q<jm81-svn-fixture>, [">= 0.1.1"])
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
            end
         | 
    
        data/lib/dm-svn.rb
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'dm-core'
         | 
| 3 | 
            +
            require 'dm-aggregates' # Only needed by specs, but this seems the easiest place to require.
         | 
| 4 | 
            +
            require 'dm-validations'
         | 
| 5 | 
            +
            require 'svn/client'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module DmSvn
         | 
| 8 | 
            +
              VERSION = '0.2.0'
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            require 'dm-svn/config'
         | 
| 12 | 
            +
            require 'dm-svn/svn'
         | 
| 13 | 
            +
            require 'dm-svn/model'
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            module DmSvn
         | 
| 2 | 
            +
              class Config
         | 
| 3 | 
            +
                OPTS = [:uri, :username, :password,
         | 
| 4 | 
            +
                        :body_property, :property_prefix, :extension]
         | 
| 5 | 
            +
                
         | 
| 6 | 
            +
                attr_accessor *OPTS
         | 
| 7 | 
            +
                attr_accessor :path_from_root # Used by Sync.
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                def initialize
         | 
| 10 | 
            +
                  # Set defaults
         | 
| 11 | 
            +
                  @body_property = 'body'
         | 
| 12 | 
            +
                  @property_prefix = 'ws:'
         | 
| 13 | 
            +
                  @extension = 'txt'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  # Try to set variables from database.yml.
         | 
| 16 | 
            +
                  # The location of database.yml should, I suppose, be configurable.
         | 
| 17 | 
            +
                  # Oh, well.
         | 
| 18 | 
            +
                  if Object.const_defined?("RAILS_ROOT")
         | 
| 19 | 
            +
                    f = "#{RAILS_ROOT}/config/database.yml"
         | 
| 20 | 
            +
                    env = Kernel.const_defined?("RAILS_ENV") ? RAILS_ENV : "development"
         | 
| 21 | 
            +
                  elsif Object.const_defined?("Merb")
         | 
| 22 | 
            +
                    f = "#{Merb.root}/config/database.yml"
         | 
| 23 | 
            +
                    env = Merb.env.to_sym || :development
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  
         | 
| 26 | 
            +
                  if f
         | 
| 27 | 
            +
                    config = YAML.load(IO.read(f))[env]
         | 
| 28 | 
            +
                    OPTS.each do |field|
         | 
| 29 | 
            +
                      config_field = config["svn_#{field}"] || config["svn_#{field}".to_sym]
         | 
| 30 | 
            +
                      if config_field
         | 
| 31 | 
            +
                        instance_variable_set("@#{field}", config_field)
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
    
        data/lib/dm-svn/model.rb
    ADDED
    
    
    
        data/lib/dm-svn/svn.rb
    ADDED
    
    | @@ -0,0 +1,144 @@ | |
| 1 | 
            +
            module DmSvn
         | 
| 2 | 
            +
              module Svn
         | 
| 3 | 
            +
                
         | 
| 4 | 
            +
                class << self
         | 
| 5 | 
            +
                  def included(klass) # Set a few 'magic' properties
         | 
| 6 | 
            +
                    klass.extend(ClassMethods)
         | 
| 7 | 
            +
                    
         | 
| 8 | 
            +
                    # svn_name could be just a name, or a full path, always excluding
         | 
| 9 | 
            +
                    # extension. If directories are stored in a model (not yet supported),
         | 
| 10 | 
            +
                    # it contains the full path (from the config.uri).
         | 
| 11 | 
            +
                    klass.property :svn_name, DataMapper::Types::Text, :lazy => false
         | 
| 12 | 
            +
                    klass.property :svn_created_at, DateTime
         | 
| 13 | 
            +
                    klass.property :svn_updated_at, DateTime
         | 
| 14 | 
            +
                    klass.property :svn_created_rev, String
         | 
| 15 | 
            +
                    klass.property :svn_updated_rev, String
         | 
| 16 | 
            +
                    klass.property :svn_created_by, String
         | 
| 17 | 
            +
                    klass.property :svn_updated_by, String
         | 
| 18 | 
            +
                    
         | 
| 19 | 
            +
                    # On create, set svn_created_* attrs based on svn_updated_* attrs
         | 
| 20 | 
            +
                    klass.before :create do
         | 
| 21 | 
            +
                      attribute_set(:svn_created_at, svn_updated_at)
         | 
| 22 | 
            +
                      attribute_set(:svn_created_rev, svn_updated_rev)
         | 
| 23 | 
            +
                      attribute_set(:svn_created_by, svn_updated_by)
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                # +name+ could be reasonably used for another property, but may normally
         | 
| 29 | 
            +
                # be assumed to be the 'svn_name'.
         | 
| 30 | 
            +
                def name
         | 
| 31 | 
            +
                  @svn_name
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
                
         | 
| 34 | 
            +
                # The path from the svn root for the model. For the moment, just an alias
         | 
| 35 | 
            +
                # of +svn_name+.
         | 
| 36 | 
            +
                def path
         | 
| 37 | 
            +
                  @svn_name
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                # Set the path. This may be responsible for moving the record to a different
         | 
| 41 | 
            +
                # parent, etc.
         | 
| 42 | 
            +
                def path=(value)
         | 
| 43 | 
            +
                  attribute_set(:svn_name, value)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                # Move to a different path and save
         | 
| 47 | 
            +
                def move_to(new_path)
         | 
| 48 | 
            +
                  self.path = new_path
         | 
| 49 | 
            +
                  self.save
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                # Update properties (body and other properties) from a DmSvn::Svn::Node
         | 
| 53 | 
            +
                # or similar (expects #body as a String and #properties as a Hash).
         | 
| 54 | 
            +
                # This method calls #save.
         | 
| 55 | 
            +
                def update_from_svn(node)
         | 
| 56 | 
            +
                  attribute_set(self.class.config.body_property, node.body) if node.body
         | 
| 57 | 
            +
                  self.path = node.short_path
         | 
| 58 | 
            +
                  
         | 
| 59 | 
            +
                  node.properties.each do | attr, value |
         | 
| 60 | 
            +
                    if self.respond_to?("#{attr}=")
         | 
| 61 | 
            +
                      self.__send__("#{attr}=", value)
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                  
         | 
| 65 | 
            +
                  if !valid?
         | 
| 66 | 
            +
                    puts "Invalid #{node.short_path} at revision #{node.revision}"
         | 
| 67 | 
            +
                    puts " - " + errors.full_messages.join(".\n - ")
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                  
         | 
| 70 | 
            +
                  save
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
                
         | 
| 73 | 
            +
                module ClassMethods
         | 
| 74 | 
            +
                  def config
         | 
| 75 | 
            +
                    @config ||= Config.new
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                  
         | 
| 78 | 
            +
                  # Override belongs_to to add +:svn+ option. If :svn => true is
         | 
| 79 | 
            +
                  # included in the options, SvnSync will also sync the +belongs_to+ model.
         | 
| 80 | 
            +
                  # For example, <code>belongs_to :category, :svn => true</code>, means
         | 
| 81 | 
            +
                  # that the Category model will also be updated by SvnSync, and be based on
         | 
| 82 | 
            +
                  # folders. Folders can have svn properties set, and/or a meta.yml file
         | 
| 83 | 
            +
                  # with properties.
         | 
| 84 | 
            +
                  # def belongs_to(name, options={})
         | 
| 85 | 
            +
                  #   
         | 
| 86 | 
            +
                  # end
         | 
| 87 | 
            +
                  
         | 
| 88 | 
            +
                  # Override DataMapper's +property+ class method to accept as an option
         | 
| 89 | 
            +
                  # +body_property+. Setting this option tells DmSvn::Svn that this field
         | 
| 90 | 
            +
                  # will store the contents of the repository file.
         | 
| 91 | 
            +
                  def property(name, type, options = {})
         | 
| 92 | 
            +
                    if options.delete(:body_property)
         | 
| 93 | 
            +
                      config.body_property = name.to_s
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                    
         | 
| 96 | 
            +
                    super(name, type, options)
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                  
         | 
| 99 | 
            +
                  # DataMapper uses +repository+, so prepend "svn_"
         | 
| 100 | 
            +
                  def svn_repository
         | 
| 101 | 
            +
                    return @svn_repository if @svn_repository
         | 
| 102 | 
            +
                    
         | 
| 103 | 
            +
                    @svn_repository = DmSvn::Model.first(:name => self.name)
         | 
| 104 | 
            +
                    @svn_repository ||= DmSvn::Model.create(:name => self.name, :revision => 0)
         | 
| 105 | 
            +
                    @svn_repository.config = config
         | 
| 106 | 
            +
                    @svn_repository
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                  
         | 
| 109 | 
            +
                  def sync
         | 
| 110 | 
            +
                    DmSvn::Svn::Sync.new(svn_repository).run
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
                  
         | 
| 113 | 
            +
                  # Override normal get behavior to try to get based on path if the argument
         | 
| 114 | 
            +
                  # is a String. Extra args are ignored by default.
         | 
| 115 | 
            +
                  def get(path_or_id, *args)
         | 
| 116 | 
            +
                    if path_or_id.is_a?(String)
         | 
| 117 | 
            +
                      get_by_path(path_or_id)
         | 
| 118 | 
            +
                    else
         | 
| 119 | 
            +
                      super
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                  
         | 
| 123 | 
            +
                  # Try to get by path. If not, create a new record and so set its path.
         | 
| 124 | 
            +
                  def get_or_create(path)
         | 
| 125 | 
            +
                    i = get_by_path(path)
         | 
| 126 | 
            +
                    return i if i
         | 
| 127 | 
            +
                    
         | 
| 128 | 
            +
                    i = create
         | 
| 129 | 
            +
                    i.path = path
         | 
| 130 | 
            +
                    i.save
         | 
| 131 | 
            +
                    return i
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                  
         | 
| 134 | 
            +
                  def get_by_path(path)
         | 
| 135 | 
            +
                    first(:svn_name => path)
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
                
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
            end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            %w{sync changeset node categorized}.each do |f|
         | 
| 143 | 
            +
              require "dm-svn/svn/#{f}"
         | 
| 144 | 
            +
            end
         | 
| @@ -0,0 +1,113 @@ | |
| 1 | 
            +
            module DmSvn
         | 
| 2 | 
            +
              module Svn
         | 
| 3 | 
            +
                
         | 
| 4 | 
            +
                module ClassMethods
         | 
| 5 | 
            +
                  
         | 
| 6 | 
            +
                  # Override belongs_to to add a :dm-svn option if :dm-svn => true, include
         | 
| 7 | 
            +
                  # Categorized and set up @svn_category and @svn_category_model instance
         | 
| 8 | 
            +
                  # methods.
         | 
| 9 | 
            +
                  def belongs_to(what, options = {})
         | 
| 10 | 
            +
                    svn = options.delete(:svn)
         | 
| 11 | 
            +
                    if svn
         | 
| 12 | 
            +
                      @svn_category = what
         | 
| 13 | 
            +
                      @svn_category_model = options[:class_name] || what.to_s.camel_case
         | 
| 14 | 
            +
                      include(DmSvn::Svn::Categorized)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                    
         | 
| 17 | 
            +
                    super
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                  
         | 
| 20 | 
            +
                  # Method name for accessing the parent instance.
         | 
| 21 | 
            +
                  def svn_category
         | 
| 22 | 
            +
                    @svn_category
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                  
         | 
| 25 | 
            +
                  # Name of the parent model class (as a String)
         | 
| 26 | 
            +
                  def svn_category_model
         | 
| 27 | 
            +
                    @svn_category_model
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                # This module is including when belongs_to is called with :dm-svn => true.
         | 
| 33 | 
            +
                # It overrides #path and #path= to take into account categories (folders in 
         | 
| 34 | 
            +
                # the Subversion repository). It also overrides .get to accept get_parent
         | 
| 35 | 
            +
                # argument.
         | 
| 36 | 
            +
                module Categorized
         | 
| 37 | 
            +
                  class << self
         | 
| 38 | 
            +
                    def included(klass)
         | 
| 39 | 
            +
                      klass.extend(ClassMethods)
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                  
         | 
| 43 | 
            +
                  # The path from the svn root for the model. Includes any folders.
         | 
| 44 | 
            +
                  def path
         | 
| 45 | 
            +
                    cat = self.send(self.class.svn_category)
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    if cat && !cat.path.blank?
         | 
| 48 | 
            +
                      return cat.path + "/" + @svn_name
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                    
         | 
| 51 | 
            +
                    return @svn_name
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                  # Set the path. This is responsible for moving the record to a different
         | 
| 55 | 
            +
                  # parent, etc.
         | 
| 56 | 
            +
                  def path=(value)
         | 
| 57 | 
            +
                    value = value[1..-1] while value[0..0] == "/"
         | 
| 58 | 
            +
                    ary = value.split("/")
         | 
| 59 | 
            +
                    immediate = ary.pop
         | 
| 60 | 
            +
                    parent = ary.join("/")
         | 
| 61 | 
            +
                    
         | 
| 62 | 
            +
                    if parent.blank?
         | 
| 63 | 
            +
                      self.send("#{self.class.svn_category}=", nil)
         | 
| 64 | 
            +
                    else
         | 
| 65 | 
            +
                      category_model = Object.const_get(self.class.svn_category_model)
         | 
| 66 | 
            +
                      category = category_model.get_or_create(parent)
         | 
| 67 | 
            +
                      self.send("#{self.class.svn_category}=", category)
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    
         | 
| 70 | 
            +
                    attribute_set(:svn_name, immediate)
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  
         | 
| 73 | 
            +
                  module ClassMethods
         | 
| 74 | 
            +
                    
         | 
| 75 | 
            +
                    # Get by path, which gets parent (possibly recursively) first.
         | 
| 76 | 
            +
                    def get_by_path(value)
         | 
| 77 | 
            +
                      value = value[1..-1] while value[0..0] == "/"
         | 
| 78 | 
            +
                      ary = value.split("/")
         | 
| 79 | 
            +
                      immediate = ary.pop
         | 
| 80 | 
            +
                      parent = ary.join("/")
         | 
| 81 | 
            +
                      
         | 
| 82 | 
            +
                      if parent.blank?
         | 
| 83 | 
            +
                        first(:svn_name => immediate, "#{self.svn_category}_id".to_sym => nil)
         | 
| 84 | 
            +
                      else
         | 
| 85 | 
            +
                        category_model = Object.const_get(self.svn_category_model)
         | 
| 86 | 
            +
                        category = category_model.get_by_path(parent)
         | 
| 87 | 
            +
                        return nil if category.nil?
         | 
| 88 | 
            +
                        first(:svn_name => immediate, "#{self.svn_category}_id".to_sym => category.id)
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                    
         | 
| 92 | 
            +
                    # Add get_parent argument. If true and a path is not found for the 
         | 
| 93 | 
            +
                    # model, try to find path in parent model (if any).
         | 
| 94 | 
            +
                    def get(path_or_id, get_parent = false)
         | 
| 95 | 
            +
                      if path_or_id.is_a?(String)
         | 
| 96 | 
            +
                        i = get_by_path(path_or_id)
         | 
| 97 | 
            +
                        if i || !get_parent
         | 
| 98 | 
            +
                          return i 
         | 
| 99 | 
            +
                        else # if get_parent
         | 
| 100 | 
            +
                          category_model = Object.const_get(@svn_category_model)
         | 
| 101 | 
            +
                          return nil if category_model == self.class
         | 
| 102 | 
            +
                          category_model.get_by_path(path_or_id)
         | 
| 103 | 
            +
                        end
         | 
| 104 | 
            +
                      else
         | 
| 105 | 
            +
                        super(path_or_id)
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                    
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
                
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
            end
         |