soup 0.2.1 → 0.9.9
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/Manifest +1 -7
- data/Rakefile +115 -0
- data/lib/soup.rb +101 -75
- data/lib/soup/empty_class.rb +1 -1
- data/lib/soup/snip.rb +17 -127
- data/test/soup_test.rb +54 -0
- metadata +48 -53
- data/lib/soup/tuples/active_record_tuple.rb +0 -48
- data/lib/soup/tuples/data_mapper_tuple.rb +0 -50
- data/lib/soup/tuples/sequel_tuple.rb +0 -36
- data/soup.gemspec +0 -68
- data/spec/snip_spec.rb +0 -102
- data/spec/soup_spec.rb +0 -148
- data/spec/spec_helper.rb +0 -8
- data/spec/spec_runner.rb +0 -12
    
        data/Manifest
    CHANGED
    
    | @@ -1,12 +1,6 @@ | |
| 1 1 | 
             
            lib/soup/empty_class.rb
         | 
| 2 2 | 
             
            lib/soup/snip.rb
         | 
| 3 | 
            -
            lib/soup/tuples/active_record_tuple.rb
         | 
| 4 | 
            -
            lib/soup/tuples/data_mapper_tuple.rb
         | 
| 5 | 
            -
            lib/soup/tuples/sequel_tuple.rb
         | 
| 6 3 | 
             
            lib/soup.rb
         | 
| 7 4 | 
             
            Manifest
         | 
| 8 5 | 
             
            README
         | 
| 9 | 
            -
             | 
| 10 | 
            -
            spec/soup_spec.rb
         | 
| 11 | 
            -
            spec/spec_helper.rb
         | 
| 12 | 
            -
            spec/spec_runner.rb
         | 
| 6 | 
            +
            test/soup_test.rb
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            require "rubygems"
         | 
| 2 | 
            +
            require "rake/gempackagetask"
         | 
| 3 | 
            +
            require "rake/rdoctask"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            task :default => :test
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require "rake/testtask"
         | 
| 8 | 
            +
            Rake::TestTask.new do |t|
         | 
| 9 | 
            +
              t.libs << "test"
         | 
| 10 | 
            +
              t.test_files = FileList["test/**/*_test.rb"]
         | 
| 11 | 
            +
              t.verbose = true
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            # This builds the actual gem. For details of what all these options
         | 
| 15 | 
            +
            # mean, and other ones you can add, check the documentation here:
         | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
            #   http://rubygems.org/read/chapter/20
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            spec = Gem::Specification.new do |s|
         | 
| 20 | 
            +
              
         | 
| 21 | 
            +
              # Change these as appropriate
         | 
| 22 | 
            +
              s.name              = "soup"
         | 
| 23 | 
            +
              s.version           = "0.9.9"
         | 
| 24 | 
            +
              s.summary           = "A super-simple data store"
         | 
| 25 | 
            +
              s.author            = "James Adam"
         | 
| 26 | 
            +
              s.email             = "james@lazyatom.com"
         | 
| 27 | 
            +
              s.homepage          = "http://lazyatom.com"
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              s.has_rdoc          = true
         | 
| 30 | 
            +
              s.extra_rdoc_files  = %w(README)
         | 
| 31 | 
            +
              s.rdoc_options      = %w(--main README)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              # Add any extra files to include in the gem
         | 
| 34 | 
            +
              s.files             = %w(Manifest Rakefile README) + Dir.glob("{test,lib}/**/*")
         | 
| 35 | 
            +
               
         | 
| 36 | 
            +
              s.require_paths     = ["lib"]
         | 
| 37 | 
            +
              
         | 
| 38 | 
            +
              # If you want to depend on other gems, add them here, along with any
         | 
| 39 | 
            +
              # relevant versions
         | 
| 40 | 
            +
              # s.add_dependency("some_other_gem", "~> 0.1.0")
         | 
| 41 | 
            +
              
         | 
| 42 | 
            +
              # If your tests use any gems, include them here
         | 
| 43 | 
            +
              # s.add_development_dependency("mocha")
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              # If you want to publish automatically to rubyforge, you'll may need
         | 
| 46 | 
            +
              # to tweak this, and the publishing task below too.
         | 
| 47 | 
            +
              s.rubyforge_project = "soup"
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            # This task actually builds the gem. We also regenerate a static 
         | 
| 51 | 
            +
            # .gemspec file, which is useful if something (i.e. GitHub) will
         | 
| 52 | 
            +
            # be automatically building a gem for this project. If you're not
         | 
| 53 | 
            +
            # using GitHub, edit as appropriate.
         | 
| 54 | 
            +
            Rake::GemPackageTask.new(spec) do |pkg|
         | 
| 55 | 
            +
              pkg.gem_spec = spec
         | 
| 56 | 
            +
              
         | 
| 57 | 
            +
              # Generate the gemspec file for github.
         | 
| 58 | 
            +
              file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
         | 
| 59 | 
            +
              File.open(file, "w") {|f| f << spec.to_ruby }
         | 
| 60 | 
            +
            end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            # Generate documentation
         | 
| 63 | 
            +
            Rake::RDocTask.new do |rd|
         | 
| 64 | 
            +
              rd.main = "README"
         | 
| 65 | 
            +
              rd.rdoc_files.include("README", "lib/**/*.rb")
         | 
| 66 | 
            +
              rd.rdoc_dir = "rdoc"
         | 
| 67 | 
            +
            end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            desc 'Clear out RDoc and generated packages'
         | 
| 70 | 
            +
            task :clean => [:clobber_rdoc, :clobber_package] do
         | 
| 71 | 
            +
              rm "#{spec.name}.gemspec"
         | 
| 72 | 
            +
            end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            # If you want to publish to RubyForge automatically, here's a simple 
         | 
| 75 | 
            +
            # task to help do that. If you don't, just get rid of this.
         | 
| 76 | 
            +
            # Be sure to set up your Rubyforge account details with the Rubyforge
         | 
| 77 | 
            +
            # gem; you'll need to run `rubyforge setup` and `rubyforge config` at
         | 
| 78 | 
            +
            # the very least.
         | 
| 79 | 
            +
            begin
         | 
| 80 | 
            +
              require "rake/contrib/sshpublisher"
         | 
| 81 | 
            +
              namespace :rubyforge do
         | 
| 82 | 
            +
                
         | 
| 83 | 
            +
                desc "Release gem and RDoc documentation to RubyForge"
         | 
| 84 | 
            +
                task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
         | 
| 85 | 
            +
                
         | 
| 86 | 
            +
                namespace :release do
         | 
| 87 | 
            +
                  desc "Release a new version of this gem"
         | 
| 88 | 
            +
                  task :gem => [:package] do
         | 
| 89 | 
            +
                    require 'rubyforge'
         | 
| 90 | 
            +
                    rubyforge = RubyForge.new
         | 
| 91 | 
            +
                    rubyforge.configure
         | 
| 92 | 
            +
                    rubyforge.login
         | 
| 93 | 
            +
                    rubyforge.userconfig['release_notes'] = spec.summary
         | 
| 94 | 
            +
                    path_to_gem = File.join(File.dirname(__FILE__), "pkg", "#{spec.name}-#{spec.version}.gem")
         | 
| 95 | 
            +
                    puts "Publishing #{spec.name}-#{spec.version.to_s} to Rubyforge..."
         | 
| 96 | 
            +
                    rubyforge.add_release(spec.rubyforge_project, spec.name, spec.version.to_s, path_to_gem)
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                
         | 
| 99 | 
            +
                  desc "Publish RDoc to RubyForge."
         | 
| 100 | 
            +
                  task :docs => [:rdoc] do
         | 
| 101 | 
            +
                    config = YAML.load(
         | 
| 102 | 
            +
                        File.read(File.expand_path('~/.rubyforge/user-config.yml'))
         | 
| 103 | 
            +
                    )
         | 
| 104 | 
            +
             
         | 
| 105 | 
            +
                    host = "#{config['username']}@rubyforge.org"
         | 
| 106 | 
            +
                    remote_dir = "/var/www/gforge-projects/soup/" # Should be the same as the rubyforge project name
         | 
| 107 | 
            +
                    local_dir = 'rdoc'
         | 
| 108 | 
            +
             
         | 
| 109 | 
            +
                    Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
            rescue LoadError
         | 
| 114 | 
            +
              puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
         | 
| 115 | 
            +
            end
         | 
    
        data/lib/soup.rb
    CHANGED
    
    | @@ -2,95 +2,121 @@ | |
| 2 2 | 
             
            $LOAD_PATH.unshift(File.dirname(__FILE__)).uniq!
         | 
| 3 3 |  | 
| 4 4 | 
             
            require 'soup/snip'
         | 
| 5 | 
            +
            require 'yaml'
         | 
| 6 | 
            +
            require 'fileutils'
         | 
| 5 7 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
              VERSION = "0. | 
| 8 | 
            -
             | 
| 9 | 
            -
               | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
              
         | 
| 14 | 
            -
             | 
| 15 | 
            -
              
         | 
| 16 | 
            -
             | 
| 17 | 
            -
               | 
| 18 | 
            -
             | 
| 19 | 
            -
               | 
| 20 | 
            -
             | 
| 21 | 
            -
               | 
| 22 | 
            -
             | 
| 23 | 
            -
               | 
| 24 | 
            -
             | 
| 25 | 
            -
              # Call this to set which tuple implementation to use, i.e.
         | 
| 26 | 
            -
              #
         | 
| 27 | 
            -
              #   Soup.flavour = :active_record
         | 
| 28 | 
            -
              #
         | 
| 29 | 
            -
              def self.flavour=(tuple_implementation)
         | 
| 30 | 
            -
                @tuple_implementation = "#{tuple_implementation}_tuple"
         | 
| 31 | 
            -
                # We want to reset the tuple class if we re-flavour the soup.
         | 
| 32 | 
            -
                @tuple_class = nil
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
              
         | 
| 35 | 
            -
              def self.tuple_class
         | 
| 36 | 
            -
                @tuple_class ||= case (@tuple_implementation || DEFAULT_TUPLE_IMPLEMENTATION)
         | 
| 37 | 
            -
                when "active_record_tuple", nil
         | 
| 38 | 
            -
                  Soup::Tuples::ActiveRecordTuple
         | 
| 39 | 
            -
                when "data_mapper_tuple"
         | 
| 40 | 
            -
                  Soup::Tuples::DataMapperTuple
         | 
| 41 | 
            -
                when "sequel_tuple"
         | 
| 42 | 
            -
                  Soup::Tuples::SequelTuple
         | 
| 43 | 
            -
                end
         | 
| 8 | 
            +
            class Soup
         | 
| 9 | 
            +
              VERSION = "0.9.9"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              # You can access a default soup using this methods.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def self.default_instance #:nodoc:
         | 
| 14 | 
            +
                @@instance ||= new
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def self.[](*args)
         | 
| 18 | 
            +
                default_instance[*args]
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def self.<<(attributes)
         | 
| 22 | 
            +
                default_instance << attributes
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def self.sieve(*args)
         | 
| 26 | 
            +
                default_instance.sieve(*args)
         | 
| 44 27 | 
             
              end
         | 
| 45 | 
            -
             | 
| 28 | 
            +
             | 
| 29 | 
            +
              def self.destroy(*args)
         | 
| 30 | 
            +
                default_instance.destroy(*args)
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              attr_reader :base_path
         | 
| 34 | 
            +
             | 
| 46 35 | 
             
              # Get the soup ready!
         | 
| 47 | 
            -
              def  | 
| 48 | 
            -
                 | 
| 49 | 
            -
                 | 
| 36 | 
            +
              def initialize(base_path=nil)
         | 
| 37 | 
            +
                @base_path = base_path || "soup"
         | 
| 38 | 
            +
                FileUtils.mkdir_p(base_path)
         | 
| 50 39 | 
             
              end
         | 
| 51 | 
            -
             | 
| 40 | 
            +
             | 
| 52 41 | 
             
              # The main interface
         | 
| 53 42 | 
             
              # ==================
         | 
| 54 | 
            -
             | 
| 55 | 
            -
              #  | 
| 56 | 
            -
              #  | 
| 57 | 
            -
               | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
                Snip.sieve(*args)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              # A shorthand for #sieve, with the addition that only a name may be
         | 
| 45 | 
            +
              # supplied (i.e. Soup['my snip'])
         | 
| 46 | 
            +
              def [](conditions)
         | 
| 47 | 
            +
                conditions = {:name => conditions} unless conditions.respond_to?(:keys)
         | 
| 48 | 
            +
                sieve(conditions)
         | 
| 61 49 | 
             
              end
         | 
| 62 | 
            -
             | 
| 50 | 
            +
             | 
| 63 51 | 
             
              # Puts some data into the soup, and returns an object that contains
         | 
| 64 52 | 
             
              # that data. The object should respond to accessing and setting its
         | 
| 65 53 | 
             
              # attributes as if they were defined using attr_accessor on the object's
         | 
| 66 54 | 
             
              # class.
         | 
| 67 | 
            -
              def  | 
| 68 | 
            -
                 | 
| 69 | 
            -
                 | 
| 70 | 
            -
                s
         | 
| 55 | 
            +
              def <<(attributes)
         | 
| 56 | 
            +
                save_snip(attributes)
         | 
| 57 | 
            +
                Snip.new(attributes, self)
         | 
| 71 58 | 
             
              end
         | 
| 72 | 
            -
             | 
| 73 | 
            -
              #  | 
| 74 | 
            -
               | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 59 | 
            +
             | 
| 60 | 
            +
              # Finds bits in the soup that make the given attribute hash.
         | 
| 61 | 
            +
              # This method should eventually be delegated to the underlying persistence
         | 
| 62 | 
            +
              # layers (i.e. Snips and Tuples, or another document database). The expected
         | 
| 63 | 
            +
              # behaviour is
         | 
| 64 | 
            +
              def sieve(conditions)
         | 
| 65 | 
            +
                conditions = symbolize_keys(conditions)
         | 
| 66 | 
            +
                if conditions.keys == [:name]
         | 
| 67 | 
            +
                  load_snip(conditions[:name])
         | 
| 77 68 | 
             
                else
         | 
| 78 | 
            -
                   | 
| 69 | 
            +
                  all_snips.select do |s|
         | 
| 70 | 
            +
                    conditions.inject(true) do |matches, (key, value)|
         | 
| 71 | 
            +
                      matches && (s.__send__(key) == value)
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 79 74 | 
             
                end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
              
         | 
| 83 | 
            -
             | 
| 84 | 
            -
              
         | 
| 85 | 
            -
             | 
| 86 | 
            -
              def  | 
| 87 | 
            -
                 | 
| 88 | 
            -
             | 
| 89 | 
            -
                  snip = Snip.find(id) rescue nil
         | 
| 90 | 
            -
                  snips[snip.id] = snip if snip
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def destroy(name)
         | 
| 78 | 
            +
                File.delete(path_for(name))
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def all_snips
         | 
| 82 | 
            +
                Dir[path_for("*")].map do |path|
         | 
| 83 | 
            +
                  load_snip(File.basename(path, ".yml"))
         | 
| 91 84 | 
             
                end
         | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              private
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              def save_snip(attributes)
         | 
| 90 | 
            +
                attributes = symbolize_keys(attributes)
         | 
| 91 | 
            +
                File.open(path_for(attributes[:name]), 'w') do |f|
         | 
| 92 | 
            +
                  content = attributes.delete(:content)
         | 
| 93 | 
            +
                  f.write content
         | 
| 94 | 
            +
                  f.write attributes.to_yaml.gsub(/^---\s/, attribute_token)
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              def load_snip(name)
         | 
| 99 | 
            +
                path = path_for(name)
         | 
| 100 | 
            +
                if File.exist?(path)
         | 
| 101 | 
            +
                  file = File.read(path)
         | 
| 102 | 
            +
                  attribute_start = file.index(attribute_token)
         | 
| 103 | 
            +
                  content = file.slice(0...attribute_start)
         | 
| 104 | 
            +
                  attributes = YAML.load(file.slice(attribute_start..-1)).merge(:content => content)
         | 
| 105 | 
            +
                  Snip.new(attributes, self)
         | 
| 106 | 
            +
                else
         | 
| 107 | 
            +
                  nil
         | 
| 94 108 | 
             
                end
         | 
| 95 109 | 
             
              end
         | 
| 96 | 
            -
             | 
| 110 | 
            +
             | 
| 111 | 
            +
              def path_for(filename)
         | 
| 112 | 
            +
                File.join(base_path, filename + ".yml")
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              def attribute_token
         | 
| 116 | 
            +
                "--- # Soup attributes"
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
              def symbolize_keys(hash)
         | 
| 120 | 
            +
                hash.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
         | 
| 121 | 
            +
              end
         | 
| 122 | 
            +
            end
         | 
    
        data/lib/soup/empty_class.rb
    CHANGED
    
    
    
        data/lib/soup/snip.rb
    CHANGED
    
    | @@ -1,148 +1,38 @@ | |
| 1 | 
            -
            require 'rubygems'
         | 
| 2 1 | 
             
            require 'soup/empty_class'
         | 
| 3 2 |  | 
| 4 | 
            -
            # methods called on Tuple:
         | 
| 5 | 
            -
            # Tuple.for_snip(id)
         | 
| 6 | 
            -
            # Tuple.find_matching(tuple_name, tuple_value_conditions)
         | 
| 7 | 
            -
            # Tuple.find_matching_hash(key => value, key2 => value2, ...)
         | 
| 8 | 
            -
            # Tuple.next_snip_id
         | 
| 9 | 
            -
            # Tuple#save
         | 
| 10 | 
            -
            # Tuple#name
         | 
| 11 | 
            -
            # Tuple#value
         | 
| 12 | 
            -
            # Tuple#destroy
         | 
| 13 | 
            -
             | 
| 14 3 | 
             
            class Snip < Soup::EmptyClass
         | 
| 4 | 
            +
              attr_reader :attributes
         | 
| 15 5 |  | 
| 16 | 
            -
               | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
              #   Snip.sieve(:created_at, "> '2007-01-01'")
         | 
| 20 | 
            -
              #
         | 
| 21 | 
            -
              # should return all Snips who have a 'created_at' value greater than '2007-01-01'.
         | 
| 22 | 
            -
              #
         | 
| 23 | 
            -
              def self.sieve(*args)
         | 
| 24 | 
            -
                if args.length > 1
         | 
| 25 | 
            -
                  name = args[0]
         | 
| 26 | 
            -
                  tuple_value_conditions = args[1]
         | 
| 27 | 
            -
                  matching_tuples = Soup.tuple_class.find_matching(name, tuple_value_conditions)
         | 
| 28 | 
            -
                  matching_tuples.map { |t| t.snip_id }.uniq.map { |snip_id| find(snip_id) }
         | 
| 29 | 
            -
                else
         | 
| 30 | 
            -
                  pairs = args[0].inject([]) { |ary, (name, value)| ary << [name, value] }
         | 
| 31 | 
            -
                  matching_tuples = pairs.map { |(name, value)| Soup.tuple_class.find_matching(name, "='#{value}'") }.flatten
         | 
| 32 | 
            -
                  snips = matching_tuples.map { |t| t.snip_id }.uniq.map { |snip_id| find(snip_id) }
         | 
| 33 | 
            -
                  snips.reject { |s| pairs.map { |(name, value)| s.get_value(name) == value }.include?(false) }
         | 
| 34 | 
            -
                end
         | 
| 35 | 
            -
              end
         | 
| 36 | 
            -
              
         | 
| 37 | 
            -
              # Returns the snip with the given ID (i.e. the collection of all tuples
         | 
| 38 | 
            -
              # with the matching snip_id, gathered into a magical snip.)
         | 
| 39 | 
            -
              #
         | 
| 40 | 
            -
              def self.find(id)
         | 
| 41 | 
            -
                raise "not found" unless (tuples = Soup.tuple_class.for_snip(id)).any?
         | 
| 42 | 
            -
                snip = Snip.new(:__id => id)
         | 
| 43 | 
            -
                snip.replace_tuples(tuples)
         | 
| 44 | 
            -
                snip
         | 
| 45 | 
            -
              end
         | 
| 46 | 
            -
              
         | 
| 47 | 
            -
              attr_reader :tuples
         | 
| 48 | 
            -
              
         | 
| 49 | 
            -
              def initialize(attributes = {})
         | 
| 50 | 
            -
                set_id(attributes.delete(:__id))
         | 
| 51 | 
            -
                @tuples = {}
         | 
| 52 | 
            -
                attributes.each { |name, value| set_value(name, value) }
         | 
| 53 | 
            -
              end
         | 
| 54 | 
            -
              
         | 
| 55 | 
            -
              def respond_to?(method)
         | 
| 56 | 
            -
                attributes.keys.include?(method.to_s)
         | 
| 6 | 
            +
              def initialize(attributes = {}, soup = Soup)
         | 
| 7 | 
            +
                @attributes = attributes
         | 
| 8 | 
            +
                @soup = soup
         | 
| 57 9 | 
             
              end
         | 
| 58 | 
            -
             | 
| 10 | 
            +
             | 
| 59 11 | 
             
              def save
         | 
| 60 | 
            -
                 | 
| 61 | 
            -
                set_id_if_necessary
         | 
| 62 | 
            -
                each_tuple { |t| t.save }
         | 
| 12 | 
            +
                @soup << @attributes
         | 
| 63 13 | 
             
                self
         | 
| 64 14 | 
             
              end
         | 
| 65 | 
            -
             | 
| 15 | 
            +
             | 
| 66 16 | 
             
              def destroy
         | 
| 67 | 
            -
                 | 
| 68 | 
            -
              end
         | 
| 69 | 
            -
              
         | 
| 70 | 
            -
              def reload
         | 
| 71 | 
            -
                return self unless self.id
         | 
| 72 | 
            -
                replace_tuples(Soup.tuple_class.for_snip(id))
         | 
| 17 | 
            +
                @soup.destroy(self.name)
         | 
| 73 18 | 
             
                self
         | 
| 74 19 | 
             
              end
         | 
| 75 | 
            -
              
         | 
| 76 | 
            -
              def attributes
         | 
| 77 | 
            -
                @tuples.inject({}) { |h, (name, t)| h[name] = t.value; h }
         | 
| 78 | 
            -
              end
         | 
| 79 20 |  | 
| 80 | 
            -
              def replace_tuples(new_tuples)
         | 
| 81 | 
            -
                @tuples.clear
         | 
| 82 | 
            -
                new_tuples.each { |tuple| @tuples[tuple.name] = tuple }
         | 
| 83 | 
            -
              end
         | 
| 84 | 
            -
              
         | 
| 85 | 
            -
              def to_s
         | 
| 86 | 
            -
                "<Snip id:#{self.id || "unset"} #{tuples_as_string}>"
         | 
| 87 | 
            -
              end
         | 
| 88 | 
            -
              
         | 
| 89 21 | 
             
              def inspect
         | 
| 90 | 
            -
                "<Snip  | 
| 22 | 
            +
                "<Snip name:#{self.name}>"
         | 
| 91 23 | 
             
              end
         | 
| 92 | 
            -
             | 
| 93 | 
            -
              def  | 
| 94 | 
            -
                attributes. | 
| 24 | 
            +
             | 
| 25 | 
            +
              def respond_to?(method)
         | 
| 26 | 
            +
                @attributes.keys.include?(method.to_s)
         | 
| 95 27 | 
             
              end
         | 
| 96 | 
            -
             | 
| 28 | 
            +
             | 
| 97 29 | 
             
              def method_missing(method, *args)
         | 
| 98 30 | 
             
                value = args.length > 1 ? args : args.first
         | 
| 99 | 
            -
                if method.to_s =~ /(.*)=\Z/ | 
| 100 | 
            -
                   | 
| 101 | 
            -
                else
         | 
| 102 | 
            -
                  get_value(method.to_s)
         | 
| 103 | 
            -
                end
         | 
| 104 | 
            -
              end
         | 
| 105 | 
            -
              
         | 
| 106 | 
            -
              def id #:nodoc: why is ID special?
         | 
| 107 | 
            -
                @id
         | 
| 108 | 
            -
              end
         | 
| 109 | 
            -
              
         | 
| 110 | 
            -
              def get_value(name)
         | 
| 111 | 
            -
                @tuples[name.to_s] ? @tuples[name.to_s].value : nil
         | 
| 112 | 
            -
              end
         | 
| 113 | 
            -
              
         | 
| 114 | 
            -
              def set_value(name, value)
         | 
| 115 | 
            -
                tuple = @tuples[name.to_s] 
         | 
| 116 | 
            -
                if tuple
         | 
| 117 | 
            -
                  tuple.value = value
         | 
| 31 | 
            +
                if method.to_s =~ /(.*)=\Z/
         | 
| 32 | 
            +
                  @attributes[$1.to_sym] = value
         | 
| 118 33 | 
             
                else
         | 
| 119 | 
            -
                  attributes | 
| 120 | 
            -
                  tuple = @tuples[name.to_s] = Soup.tuple_class.new(attributes)
         | 
| 34 | 
            +
                  @attributes[method]
         | 
| 121 35 | 
             
                end
         | 
| 122 | 
            -
                tuple.value
         | 
| 123 | 
            -
              end
         | 
| 124 | 
            -
              
         | 
| 125 | 
            -
              
         | 
| 126 | 
            -
              private
         | 
| 127 | 
            -
              
         | 
| 128 | 
            -
              def each_tuple
         | 
| 129 | 
            -
                @tuples.values.each { |tuple| yield tuple }
         | 
| 130 | 
            -
              end
         | 
| 131 | 
            -
              
         | 
| 132 | 
            -
              def set_id(id)
         | 
| 133 | 
            -
                @id = id
         | 
| 134 | 
            -
                self
         | 
| 135 36 | 
             
              end
         | 
| 136 37 |  | 
| 137 | 
            -
             | 
| 138 | 
            -
                if self.id.nil?
         | 
| 139 | 
            -
                  set_id(Soup.tuple_class.next_snip_id)
         | 
| 140 | 
            -
                  @tuples.values.each { |tuple| tuple.snip_id = self.id }
         | 
| 141 | 
            -
                end
         | 
| 142 | 
            -
              end
         | 
| 143 | 
            -
              
         | 
| 144 | 
            -
              def tuples_as_string
         | 
| 145 | 
            -
                @tuples.inject("") { |hash, (name, tuple)| hash += " #{name}:'#{tuple.value}'" }.strip
         | 
| 146 | 
            -
              end
         | 
| 147 | 
            -
              
         | 
| 148 | 
            -
            end
         | 
| 38 | 
            +
            end
         | 
    
        data/test/soup_test.rb
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'test/unit'
         | 
| 2 | 
            +
            require 'shoulda'
         | 
| 3 | 
            +
            require 'soup'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class SoupTest < Test::Unit::TestCase
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context "Given a soup" do
         | 
| 8 | 
            +
                setup do
         | 
| 9 | 
            +
                  @soup = Soup.new(File.join(File.dirname(__FILE__), *%w[.. tmp soup]))
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                should "be able to store content" do
         | 
| 13 | 
            +
                  @soup << {:name => 'test', :content => "I like stuff, and things"}
         | 
| 14 | 
            +
                  assert_equal "I like stuff, and things", @soup['test'].content
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                context "when sieving the soup" do
         | 
| 18 | 
            +
                  setup do
         | 
| 19 | 
            +
                    @james = @soup << {:name => 'james', :spirit_guide => 'fox', :colour => 'blue', :powers => 'yes'}
         | 
| 20 | 
            +
                    @murray = @soup << {:name => 'murray', :spirit_guide => 'chaffinch', :colour => 'red', :powers => 'yes'}
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  should "find snips by name if the parameter is a string" do
         | 
| 24 | 
            +
                    assert_equal @james, @soup['james']
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  should "find snips using exact matching of keys and values if the parameter is a hash" do
         | 
| 28 | 
            +
                    assert_equal @murray, @soup[:name => 'murray']
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  should "match using all parameters" do
         | 
| 32 | 
            +
                    assert_equal @james, @soup[:powers => 'yes', :colour => 'red']
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  should "return an array if more than one snip matches" do
         | 
| 36 | 
            +
                    assert_equal [@james, @murray], @soup[:powers => 'yes']
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  should "return an empty array if no matching snips exist" do
         | 
| 40 | 
            +
                    assert_equal [], @soup[:powers => 'maybe']
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                context "when deleting snips" do
         | 
| 45 | 
            +
                  should "allow deletion of snips" do
         | 
| 46 | 
            +
                    snip = @soup << {:name => 'test', :content => 'content'}
         | 
| 47 | 
            +
                    assert_equal snip, @soup['test']
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    @soup['test'].destroy
         | 
| 50 | 
            +
                    assert @soup['test'].nil?
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,67 +1,62 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            -
            rubygems_version: 0.9.4
         | 
| 3 | 
            -
            specification_version: 1
         | 
| 4 2 | 
             
            name: soup
         | 
| 5 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 6 | 
            -
              version: 0. | 
| 7 | 
            -
            date: 2008-04-17 00:00:00 +01:00
         | 
| 8 | 
            -
            summary: Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,
         | 
| 9 | 
            -
            require_paths: 
         | 
| 10 | 
            -
            - lib
         | 
| 11 | 
            -
            email: 
         | 
| 12 | 
            -
            - james@lazyatom.com
         | 
| 13 | 
            -
            homepage: ""
         | 
| 14 | 
            -
            rubyforge_project: soup
         | 
| 15 | 
            -
            description: Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,
         | 
| 16 | 
            -
            autorequire: 
         | 
| 17 | 
            -
            default_executable: 
         | 
| 18 | 
            -
            bindir: bin
         | 
| 19 | 
            -
            has_rdoc: true
         | 
| 20 | 
            -
            required_ruby_version: !ruby/object:Gem::Version::Requirement 
         | 
| 21 | 
            -
              requirements: 
         | 
| 22 | 
            -
              - - ">"
         | 
| 23 | 
            -
                - !ruby/object:Gem::Version 
         | 
| 24 | 
            -
                  version: 0.0.0
         | 
| 25 | 
            -
              version: 
         | 
| 4 | 
            +
              version: 0.9.9
         | 
| 26 5 | 
             
            platform: ruby
         | 
| 27 | 
            -
            signing_key: 
         | 
| 28 | 
            -
            cert_chain: 
         | 
| 29 | 
            -
            post_install_message: 
         | 
| 30 6 | 
             
            authors: 
         | 
| 31 7 | 
             
            - James Adam
         | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
            - lib/soup/tuples/active_record_tuple.rb
         | 
| 36 | 
            -
            - lib/soup/tuples/data_mapper_tuple.rb
         | 
| 37 | 
            -
            - lib/soup/tuples/sequel_tuple.rb
         | 
| 38 | 
            -
            - lib/soup.rb
         | 
| 39 | 
            -
            - Manifest
         | 
| 40 | 
            -
            - README
         | 
| 41 | 
            -
            - spec/snip_spec.rb
         | 
| 42 | 
            -
            - spec/soup_spec.rb
         | 
| 43 | 
            -
            - spec/spec_helper.rb
         | 
| 44 | 
            -
            - spec/spec_runner.rb
         | 
| 45 | 
            -
            - soup.gemspec
         | 
| 46 | 
            -
            test_files: []
         | 
| 47 | 
            -
             | 
| 48 | 
            -
            rdoc_options: []
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 49 11 |  | 
| 50 | 
            -
             | 
| 12 | 
            +
            date: 2009-10-12 00:00:00 +01:00
         | 
| 13 | 
            +
            default_executable: 
         | 
| 14 | 
            +
            dependencies: []
         | 
| 51 15 |  | 
| 16 | 
            +
            description: 
         | 
| 17 | 
            +
            email: james@lazyatom.com
         | 
| 52 18 | 
             
            executables: []
         | 
| 53 19 |  | 
| 54 20 | 
             
            extensions: []
         | 
| 55 21 |  | 
| 22 | 
            +
            extra_rdoc_files: 
         | 
| 23 | 
            +
            - README
         | 
| 24 | 
            +
            files: 
         | 
| 25 | 
            +
            - Manifest
         | 
| 26 | 
            +
            - Rakefile
         | 
| 27 | 
            +
            - README
         | 
| 28 | 
            +
            - test/soup_test.rb
         | 
| 29 | 
            +
            - lib/soup/empty_class.rb
         | 
| 30 | 
            +
            - lib/soup/snip.rb
         | 
| 31 | 
            +
            - lib/soup.rb
         | 
| 32 | 
            +
            has_rdoc: true
         | 
| 33 | 
            +
            homepage: http://lazyatom.com
         | 
| 34 | 
            +
            licenses: []
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            post_install_message: 
         | 
| 37 | 
            +
            rdoc_options: 
         | 
| 38 | 
            +
            - --main
         | 
| 39 | 
            +
            - README
         | 
| 40 | 
            +
            require_paths: 
         | 
| 41 | 
            +
            - lib
         | 
| 42 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 43 | 
            +
              requirements: 
         | 
| 44 | 
            +
              - - ">="
         | 
| 45 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 46 | 
            +
                  version: "0"
         | 
| 47 | 
            +
              version: 
         | 
| 48 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 49 | 
            +
              requirements: 
         | 
| 50 | 
            +
              - - ">="
         | 
| 51 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 52 | 
            +
                  version: "0"
         | 
| 53 | 
            +
              version: 
         | 
| 56 54 | 
             
            requirements: []
         | 
| 57 55 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                  - !ruby/object:Gem::Version 
         | 
| 66 | 
            -
                    version: 2.0.2
         | 
| 67 | 
            -
                version: 
         | 
| 56 | 
            +
            rubyforge_project: soup
         | 
| 57 | 
            +
            rubygems_version: 1.3.3
         | 
| 58 | 
            +
            signing_key: 
         | 
| 59 | 
            +
            specification_version: 3
         | 
| 60 | 
            +
            summary: A super-simple data store
         | 
| 61 | 
            +
            test_files: []
         | 
| 62 | 
            +
             | 
| @@ -1,48 +0,0 @@ | |
| 1 | 
            -
            require 'rubygems'
         | 
| 2 | 
            -
            gem 'activerecord', '>=2.0.2'
         | 
| 3 | 
            -
            require 'activerecord'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module Soup
         | 
| 6 | 
            -
              module Tuples
         | 
| 7 | 
            -
                class ActiveRecordTuple < ActiveRecord::Base
         | 
| 8 | 
            -
                  set_table_name :tuples
         | 
| 9 | 
            -
              
         | 
| 10 | 
            -
                  def self.prepare_database(config)
         | 
| 11 | 
            -
                    ActiveRecord::Base.establish_connection(config)
         | 
| 12 | 
            -
                    return if connection.tables.include?("tuples")  # NOTE - this probably isn't good enough (what if the schema has changed?)
         | 
| 13 | 
            -
                    ActiveRecord::Migration.create_table :tuples, :force => true do |t|
         | 
| 14 | 
            -
                      t.column :snip_id, :integer
         | 
| 15 | 
            -
                      t.column :name, :string
         | 
| 16 | 
            -
                      t.column :value, :text
         | 
| 17 | 
            -
                      t.column :created_at, :datetime
         | 
| 18 | 
            -
                      t.column :updated_at, :datetime
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
              
         | 
| 22 | 
            -
                  def self.for_snip(id)
         | 
| 23 | 
            -
                    find_all_by_snip_id(id)
         | 
| 24 | 
            -
                  end
         | 
| 25 | 
            -
              
         | 
| 26 | 
            -
                  def self.find_matching(name, value_conditions=nil)
         | 
| 27 | 
            -
                    condition_sql = "name = '#{name}'"
         | 
| 28 | 
            -
                    condition_sql += " AND value #{value_conditions}" if value_conditions
         | 
| 29 | 
            -
                    find(:all, :conditions => condition_sql)
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
              
         | 
| 32 | 
            -
                  # TODO: *totally* not threadsafe.
         | 
| 33 | 
            -
                  def self.next_snip_id
         | 
| 34 | 
            -
                    maximum(:snip_id) + 1 rescue 1
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
              
         | 
| 37 | 
            -
                  def save
         | 
| 38 | 
            -
                    super if new_record? || dirty?
         | 
| 39 | 
            -
                  end
         | 
| 40 | 
            -
              
         | 
| 41 | 
            -
                  private 
         | 
| 42 | 
            -
              
         | 
| 43 | 
            -
                  def dirty?
         | 
| 44 | 
            -
                    true # hmm.
         | 
| 45 | 
            -
                  end
         | 
| 46 | 
            -
                end
         | 
| 47 | 
            -
              end
         | 
| 48 | 
            -
            end
         | 
| @@ -1,50 +0,0 @@ | |
| 1 | 
            -
            require 'rubygems'
         | 
| 2 | 
            -
            gem 'datamapper'
         | 
| 3 | 
            -
            require 'data_mapper'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module Soup
         | 
| 6 | 
            -
              module Tuples
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                # This tuple implementation is broken - there's a weird interaction
         | 
| 9 | 
            -
                # where values are not set within the web application.
         | 
| 10 | 
            -
                #
         | 
| 11 | 
            -
                class DataMapperTuple < DataMapper::Base  
         | 
| 12 | 
            -
                  def self.prepare_database(config)
         | 
| 13 | 
            -
                    DataMapper::Database.setup(config)
         | 
| 14 | 
            -
                    # NOTE: so um, this property stuff doesn't like it if you're not connected to the db
         | 
| 15 | 
            -
                    # lets only have it once we are?  Seems mental.
         | 
| 16 | 
            -
                    self.class_eval {
         | 
| 17 | 
            -
                      set_table_name 'tuples'
         | 
| 18 | 
            -
                  
         | 
| 19 | 
            -
                      property :snip_id, :integer
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                      property :name, :string
         | 
| 22 | 
            -
                      property :value, :text
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                      property :created_at, :datetime
         | 
| 25 | 
            -
                      property :updated_at, :datetime
         | 
| 26 | 
            -
                    }
         | 
| 27 | 
            -
                    return if self.table.exists? # NOTE - this probably isn't good enough (what if the schema has changed?)
         | 
| 28 | 
            -
                    DataMapper::Persistence.auto_migrate! 
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
              
         | 
| 31 | 
            -
                  def self.for_snip(id)
         | 
| 32 | 
            -
                    all(:snip_id => id)
         | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
              
         | 
| 35 | 
            -
                  # TODO: *totally* not threadsafe.
         | 
| 36 | 
            -
                  def self.next_snip_id
         | 
| 37 | 
            -
                    database.query("SELECT MAX(snip_id) + 1 FROM tuples")[0] || 1
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                  def save
         | 
| 41 | 
            -
                    if dirty? or new_record?
         | 
| 42 | 
            -
                      super
         | 
| 43 | 
            -
                    end
         | 
| 44 | 
            -
                  end
         | 
| 45 | 
            -
              
         | 
| 46 | 
            -
                  alias_method :destroy, :destroy!
         | 
| 47 | 
            -
                end
         | 
| 48 | 
            -
                
         | 
| 49 | 
            -
              end
         | 
| 50 | 
            -
            end
         | 
| @@ -1,36 +0,0 @@ | |
| 1 | 
            -
            require 'rubygems'
         | 
| 2 | 
            -
            gem 'sequel'
         | 
| 3 | 
            -
            require 'sequel'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            DB = Sequel.sqlite 'soup_development.db'
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            module Soup
         | 
| 8 | 
            -
              module Tuples
         | 
| 9 | 
            -
              
         | 
| 10 | 
            -
                class SequelTuple < Sequel::Model(:tuples)
         | 
| 11 | 
            -
                  set_schema do
         | 
| 12 | 
            -
                    primary_key :id
         | 
| 13 | 
            -
                    integer :snip_id
         | 
| 14 | 
            -
                    string :name
         | 
| 15 | 
            -
                    string :value
         | 
| 16 | 
            -
                    datetime :created_at
         | 
| 17 | 
            -
                    datetime :updated_at
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
              
         | 
| 20 | 
            -
                  def self.prepare_database(config)
         | 
| 21 | 
            -
                    # ummm... how to connect?
         | 
| 22 | 
            -
                    create_table # TODO: detect if the table already exists
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
              
         | 
| 25 | 
            -
                  def self.for_snip(id)
         | 
| 26 | 
            -
                    filter(:snip_id => id).to_a
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                  # TODO: *totally* not threadsafe.
         | 
| 30 | 
            -
                  def self.next_snip_id
         | 
| 31 | 
            -
                    max(:snip_id) + 1
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
                end
         | 
| 34 | 
            -
                
         | 
| 35 | 
            -
              end
         | 
| 36 | 
            -
            end
         | 
    
        data/soup.gemspec
    DELETED
    
    | @@ -1,68 +0,0 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            # Gem::Specification for Soup-0.2.1
         | 
| 3 | 
            -
            # Originally generated by Echoe
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            Gem::Specification.new do |s|
         | 
| 6 | 
            -
              s.name = %q{soup}
         | 
| 7 | 
            -
              s.version = "0.2.1"
         | 
| 8 | 
            -
              s.date = %q{2008-04-17}
         | 
| 9 | 
            -
              s.summary = %q{Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,}
         | 
| 10 | 
            -
              s.email = ["james@lazyatom.com"]
         | 
| 11 | 
            -
              s.homepage = %q{}
         | 
| 12 | 
            -
              s.rubyforge_project = %q{soup}
         | 
| 13 | 
            -
              s.description = %q{Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,}
         | 
| 14 | 
            -
              s.has_rdoc = true
         | 
| 15 | 
            -
              s.authors = ["James Adam"]
         | 
| 16 | 
            -
              s.files = ["lib/soup/empty_class.rb", "lib/soup/snip.rb", "lib/soup/tuples/active_record_tuple.rb", "lib/soup/tuples/data_mapper_tuple.rb", "lib/soup/tuples/sequel_tuple.rb", "lib/soup.rb", "Manifest", "README", "spec/snip_spec.rb", "spec/soup_spec.rb", "spec/spec_helper.rb", "spec/spec_runner.rb", "soup.gemspec"]
         | 
| 17 | 
            -
              s.add_dependency(%q<activerecord>, [">= 2.0.2"])
         | 
| 18 | 
            -
            end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
            # # Original Rakefile source (requires the Echoe gem):
         | 
| 22 | 
            -
            # 
         | 
| 23 | 
            -
            # require 'rubygems'
         | 
| 24 | 
            -
            # 
         | 
| 25 | 
            -
            # require 'lib/soup'
         | 
| 26 | 
            -
            # 
         | 
| 27 | 
            -
            # begin
         | 
| 28 | 
            -
            #   require 'echoe'
         | 
| 29 | 
            -
            # 
         | 
| 30 | 
            -
            #   Echoe.new("soup", Soup::VERSION) do |soup|
         | 
| 31 | 
            -
            #     soup.author = ["James Adam"]
         | 
| 32 | 
            -
            #     soup.email = ["james@lazyatom.com"]
         | 
| 33 | 
            -
            #     soup.description = File.readlines("README").first
         | 
| 34 | 
            -
            #     soup.dependencies = ["activerecord >=2.0.2"]
         | 
| 35 | 
            -
            #     soup.clean_pattern = ["*.db", "meta", "pkg"]
         | 
| 36 | 
            -
            #     soup.ignore_pattern = [".git", "*.db", "meta"]
         | 
| 37 | 
            -
            #   end
         | 
| 38 | 
            -
            #   
         | 
| 39 | 
            -
            # rescue LoadError
         | 
| 40 | 
            -
            #   puts "You need to install the echoe gem to perform meta operations on this gem"
         | 
| 41 | 
            -
            # end
         | 
| 42 | 
            -
            # 
         | 
| 43 | 
            -
            # begin
         | 
| 44 | 
            -
            #   require 'spec'
         | 
| 45 | 
            -
            #   require 'spec/rake/spectask'
         | 
| 46 | 
            -
            #   
         | 
| 47 | 
            -
            #   Spec::Rake::SpecTask.new do |t|
         | 
| 48 | 
            -
            #     t.spec_opts = ["--format", "specdoc", "--colour"]
         | 
| 49 | 
            -
            #     t.spec_files = Dir['spec/**/*_spec.rb'].sort
         | 
| 50 | 
            -
            #     t.libs = ['lib']
         | 
| 51 | 
            -
            #     #t.rcov = true
         | 
| 52 | 
            -
            #     #t.rcov_dir = 'meta/coverage'
         | 
| 53 | 
            -
            #   end
         | 
| 54 | 
            -
            #   
         | 
| 55 | 
            -
            #   task :show_rcov do
         | 
| 56 | 
            -
            #     system 'open meta/coverage/index.html' if PLATFORM['darwin']
         | 
| 57 | 
            -
            #   end
         | 
| 58 | 
            -
            #   
         | 
| 59 | 
            -
            # rescue LoadError
         | 
| 60 | 
            -
            #   puts "You need RSpec installed to run the spec (default) task on this gem"
         | 
| 61 | 
            -
            # end
         | 
| 62 | 
            -
            # 
         | 
| 63 | 
            -
            # desc "Open an irb session preloaded with this library"
         | 
| 64 | 
            -
            # task :console do
         | 
| 65 | 
            -
            #   sh "irb --prompt simple -rubygems -r ./lib/soup.rb"
         | 
| 66 | 
            -
            # end
         | 
| 67 | 
            -
            # 
         | 
| 68 | 
            -
            # task :default => [:spec, :show_rcov]
         | 
    
        data/spec/snip_spec.rb
    DELETED
    
    | @@ -1,102 +0,0 @@ | |
| 1 | 
            -
            require File.join(File.dirname(__FILE__), "spec_helper")
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe Snip do
         | 
| 4 | 
            -
              before(:each) do
         | 
| 5 | 
            -
                Soup.base = {:database => "soup_test.db"}
         | 
| 6 | 
            -
                Soup.prepare
         | 
| 7 | 
            -
                clear_database
         | 
| 8 | 
            -
              end
         | 
| 9 | 
            -
              
         | 
| 10 | 
            -
              describe "when newly created" do
         | 
| 11 | 
            -
                before(:each) { @snip = Snip.new }
         | 
| 12 | 
            -
              
         | 
| 13 | 
            -
                it "should not have a name" do
         | 
| 14 | 
            -
                  @snip.name.should be_nil
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
              
         | 
| 17 | 
            -
                it "should return nil for any attributes" do
         | 
| 18 | 
            -
                  @snip.other_attribute.should be_nil
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              
         | 
| 21 | 
            -
                it "should not have an id yet" do
         | 
| 22 | 
            -
                  @snip.id.should be_nil
         | 
| 23 | 
            -
                end
         | 
| 24 | 
            -
              end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              describe "when being created with attributes" do
         | 
| 27 | 
            -
              
         | 
| 28 | 
            -
                it "should set attributes as passed in" do
         | 
| 29 | 
            -
                  @snip = Snip.new(:beats => 'phat', :rhymes => 100)
         | 
| 30 | 
            -
                  @snip.beats.should == 'phat'
         | 
| 31 | 
            -
                  @snip.rhymes.should == 100
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
              
         | 
| 34 | 
            -
                it "should ignore any id passed in" do
         | 
| 35 | 
            -
                  @snip = Snip.new(:id => 1000) 
         | 
| 36 | 
            -
                  @snip.id.should be_nil
         | 
| 37 | 
            -
                end
         | 
| 38 | 
            -
              
         | 
| 39 | 
            -
                it "should not ignore the secret __id" do
         | 
| 40 | 
            -
                  @snip = Snip.new(:__id => 1000)
         | 
| 41 | 
            -
                  @snip.id.should == 1000
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
              end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
              describe "when setting attributes" do
         | 
| 46 | 
            -
                before(:each) { @snip = Snip.new }
         | 
| 47 | 
            -
              
         | 
| 48 | 
            -
                it "should allow setting attributes" do
         | 
| 49 | 
            -
                  @snip.something = "blah"
         | 
| 50 | 
            -
                  @snip.something.should == "blah"
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
              
         | 
| 53 | 
            -
                it "should not respond to attributes that have not been set" do
         | 
| 54 | 
            -
                  @snip.should_not respond_to(:monkey)
         | 
| 55 | 
            -
                end
         | 
| 56 | 
            -
              
         | 
| 57 | 
            -
                it "should respond to attributes that have been set" do
         | 
| 58 | 
            -
                  @snip.monkey = true
         | 
| 59 | 
            -
                  @snip.should respond_to(:monkey)
         | 
| 60 | 
            -
                end
         | 
| 61 | 
            -
              
         | 
| 62 | 
            -
                it "should not allow setting of the id" do
         | 
| 63 | 
            -
                  @snip.id = 100
         | 
| 64 | 
            -
                  @snip.id.should_not == 100
         | 
| 65 | 
            -
                  @snip.id.should be_nil
         | 
| 66 | 
            -
                end
         | 
| 67 | 
            -
              end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
              describe "when saving" do
         | 
| 70 | 
            -
                before(:each) { @snip = Snip.new }
         | 
| 71 | 
            -
              
         | 
| 72 | 
            -
                it "should not save if there's no data" do
         | 
| 73 | 
            -
                  lambda { @snip.save }.should raise_error
         | 
| 74 | 
            -
                end
         | 
| 75 | 
            -
              
         | 
| 76 | 
            -
                it "should return all attributes when reloading" do
         | 
| 77 | 
            -
                  @snip.name = "something"
         | 
| 78 | 
            -
                  @snip.jazz = "smooth"
         | 
| 79 | 
            -
                  @snip.save
         | 
| 80 | 
            -
                
         | 
| 81 | 
            -
                  other_snip = Soup['something']
         | 
| 82 | 
            -
                  other_snip.jazz.should == "smooth"
         | 
| 83 | 
            -
                end
         | 
| 84 | 
            -
              
         | 
| 85 | 
            -
                it "should generate an id" do
         | 
| 86 | 
            -
                  @snip.name = "something"
         | 
| 87 | 
            -
                  @snip.save
         | 
| 88 | 
            -
                
         | 
| 89 | 
            -
                  other_snip = Soup['something']
         | 
| 90 | 
            -
                  other_snip.id.should_not be_nil
         | 
| 91 | 
            -
                end
         | 
| 92 | 
            -
              
         | 
| 93 | 
            -
                it "should not overwrite an existing id created via __id" do
         | 
| 94 | 
            -
                  @snip = Snip.new(:__id => 100)
         | 
| 95 | 
            -
                  @snip.name = "something_else"
         | 
| 96 | 
            -
                  @snip.save
         | 
| 97 | 
            -
                
         | 
| 98 | 
            -
                  other_snip = Soup['something_else']
         | 
| 99 | 
            -
                  other_snip.id.should == 100
         | 
| 100 | 
            -
                end
         | 
| 101 | 
            -
              end
         | 
| 102 | 
            -
            end
         | 
    
        data/spec/soup_spec.rb
    DELETED
    
    | @@ -1,148 +0,0 @@ | |
| 1 | 
            -
            require 'soup'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe Soup do
         | 
| 4 | 
            -
             | 
| 5 | 
            -
              describe "when unflavoured or based" do
         | 
| 6 | 
            -
                before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
         | 
| 7 | 
            -
                it "should use the default database config" do
         | 
| 8 | 
            -
                  # I think this set of mock / expectations might be super wrong
         | 
| 9 | 
            -
                  Soup::DEFAULT_CONFIG.should_receive(:merge).with({}).and_return(Soup::DEFAULT_CONFIG)
         | 
| 10 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).with(Soup::DEFAULT_CONFIG)
         | 
| 11 | 
            -
                  Soup.prepare
         | 
| 12 | 
            -
                end
         | 
| 13 | 
            -
              
         | 
| 14 | 
            -
                it "should use the default tuple implementation" do
         | 
| 15 | 
            -
                  # No real idea how to mock the require, or use aught but Secret Knowledge that AR == Default
         | 
| 16 | 
            -
                  Soup.tuple_class.should == Soup::Tuples::ActiveRecordTuple
         | 
| 17 | 
            -
                  Soup::Tuples::ActiveRecordTuple.should_receive(:prepare_database)
         | 
| 18 | 
            -
                  Soup.prepare
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
              
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
              describe "when being based" do
         | 
| 24 | 
            -
                before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
         | 
| 25 | 
            -
              
         | 
| 26 | 
            -
                it "should allow the base of the soup to be set" do
         | 
| 27 | 
            -
                  Soup.should respond_to(:base=)
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
              
         | 
| 30 | 
            -
                it "should use the new base when preparing the soup" do
         | 
| 31 | 
            -
                  bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'} 
         | 
| 32 | 
            -
                  Soup.base = bouillabaisse
         | 
| 33 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).with(bouillabaisse)
         | 
| 34 | 
            -
                  Soup.prepare
         | 
| 35 | 
            -
                end
         | 
| 36 | 
            -
              
         | 
| 37 | 
            -
                it "should merge incomplete bases with the default" do
         | 
| 38 | 
            -
                  tasteless = {:database => 'water.db'}
         | 
| 39 | 
            -
                  Soup.base = tasteless
         | 
| 40 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).with(Soup::DEFAULT_CONFIG.merge(tasteless))
         | 
| 41 | 
            -
                  Soup.prepare
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
              
         | 
| 44 | 
            -
                it "should allow the base to be reset" do
         | 
| 45 | 
            -
                  bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'} 
         | 
| 46 | 
            -
                  Soup.base = bouillabaisse
         | 
| 47 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).once.with(bouillabaisse).ordered
         | 
| 48 | 
            -
                  Soup.prepare
         | 
| 49 | 
            -
                
         | 
| 50 | 
            -
                  gazpacho = {:database => 'tomato.db', :adapter => 'colddb'}
         | 
| 51 | 
            -
                  Soup.base = gazpacho
         | 
| 52 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).once.with(gazpacho).ordered
         | 
| 53 | 
            -
                  Soup.prepare
         | 
| 54 | 
            -
                end
         | 
| 55 | 
            -
              
         | 
| 56 | 
            -
                it "should not allow old bases to interfere with new ones" do
         | 
| 57 | 
            -
                  bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'} 
         | 
| 58 | 
            -
                  Soup.base = bouillabaisse
         | 
| 59 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).once.with(bouillabaisse).ordered
         | 
| 60 | 
            -
                  Soup.prepare
         | 
| 61 | 
            -
                
         | 
| 62 | 
            -
                  tasteless = {:database => 'water.db'}
         | 
| 63 | 
            -
                  Soup.base = tasteless
         | 
| 64 | 
            -
                  Soup.tuple_class.should_receive(:prepare_database).once.with(Soup::DEFAULT_CONFIG.merge(tasteless)).ordered
         | 
| 65 | 
            -
                  Soup.tuple_class.should_not_receive(:prepare_database).with(bouillabaisse.merge(tasteless))
         | 
| 66 | 
            -
                  Soup.prepare
         | 
| 67 | 
            -
                end
         | 
| 68 | 
            -
              end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
              describe "when being flavoured" do
         | 
| 71 | 
            -
                before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
         | 
| 72 | 
            -
             
         | 
| 73 | 
            -
                it "should allow the soup to be flavoured" do
         | 
| 74 | 
            -
                  Soup.should respond_to(:flavour=)
         | 
| 75 | 
            -
                end
         | 
| 76 | 
            -
              
         | 
| 77 | 
            -
                it "should determine the tuple class based on the flavour" do
         | 
| 78 | 
            -
                  require 'soup/tuples/data_mapper_tuple'
         | 
| 79 | 
            -
                  Soup.flavour = :data_mapper
         | 
| 80 | 
            -
                  Soup.tuple_class.should == Soup::Tuples::DataMapperTuple
         | 
| 81 | 
            -
                end
         | 
| 82 | 
            -
              
         | 
| 83 | 
            -
                it "should allow the flavour to be set multiple times" do
         | 
| 84 | 
            -
                  require 'soup/tuples/data_mapper_tuple'
         | 
| 85 | 
            -
                  Soup.flavour = :data_mapper
         | 
| 86 | 
            -
                  Soup.tuple_class.should == Soup::Tuples::DataMapperTuple
         | 
| 87 | 
            -
                
         | 
| 88 | 
            -
                  require 'soup/tuples/sequel_tuple'
         | 
| 89 | 
            -
                  Soup.flavour = :sequel
         | 
| 90 | 
            -
                  Soup.tuple_class.should_not == Soup::Tuples::DataMapperTuple
         | 
| 91 | 
            -
                  Soup.tuple_class.should == Soup::Tuples::SequelTuple
         | 
| 92 | 
            -
                end
         | 
| 93 | 
            -
              
         | 
| 94 | 
            -
                it "should use have no tuple class if the flavour is unknowable" do
         | 
| 95 | 
            -
                  Soup.flavour = :shoggoth
         | 
| 96 | 
            -
                  Soup.tuple_class.should == nil
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
              end
         | 
| 99 | 
            -
             | 
| 100 | 
            -
              describe "when adding data to the Soup directly" do
         | 
| 101 | 
            -
                before(:each) do
         | 
| 102 | 
            -
                  Soup.base = {:database => "soup_test.db"}
         | 
| 103 | 
            -
                  Soup.flavour = :active_record
         | 
| 104 | 
            -
                  Soup.prepare
         | 
| 105 | 
            -
                  clear_database
         | 
| 106 | 
            -
                end
         | 
| 107 | 
            -
              
         | 
| 108 | 
            -
                it "should create a new snip" do
         | 
| 109 | 
            -
                  attributes = {:name => 'monkey'}
         | 
| 110 | 
            -
                  Snip.should_receive(:new).with(attributes).and_return(mock('snip', :null_object => true))
         | 
| 111 | 
            -
                  Soup << attributes
         | 
| 112 | 
            -
                end
         | 
| 113 | 
            -
              
         | 
| 114 | 
            -
                it "should save the snip" do
         | 
| 115 | 
            -
                  attributes = {:name => 'monkey'}
         | 
| 116 | 
            -
                  Snip.should_receive(:new).with(attributes).and_return(snip = mock('snip'))
         | 
| 117 | 
            -
                  snip.should_receive(:save)
         | 
| 118 | 
            -
                  Soup << attributes    
         | 
| 119 | 
            -
                end
         | 
| 120 | 
            -
              end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
              describe "when sieving the soup" do
         | 
| 123 | 
            -
                before(:each) do
         | 
| 124 | 
            -
                  Soup.base = {:database => "soup_test.db"}
         | 
| 125 | 
            -
                  Soup.flavour = :active_record
         | 
| 126 | 
            -
                  Soup.prepare
         | 
| 127 | 
            -
                  clear_database
         | 
| 128 | 
            -
                  @james = Soup << {:name => 'james', :spirit_guide => 'fox', :colour => 'blue', :powers => 'yes'}
         | 
| 129 | 
            -
                  @murray = Soup << {:name => 'murray', :spirit_guide => 'chaffinch', :colour => 'red', :powers => 'yes'}
         | 
| 130 | 
            -
                end
         | 
| 131 | 
            -
                
         | 
| 132 | 
            -
                it "should find snips by name if the parameter is a string" do
         | 
| 133 | 
            -
                  Soup['james'].should == @james
         | 
| 134 | 
            -
                end
         | 
| 135 | 
            -
                
         | 
| 136 | 
            -
                it "should find snips using exact matching of keys and values if the parameter is a hash" do
         | 
| 137 | 
            -
                  Soup[:name => 'murray'].should == @murray
         | 
| 138 | 
            -
                end
         | 
| 139 | 
            -
                
         | 
| 140 | 
            -
                it "should match using all parameters" do
         | 
| 141 | 
            -
                  Soup[:powers => 'yes', :colour => 'red'].should == @james
         | 
| 142 | 
            -
                end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                it "should return an array if more than one snip matches" do
         | 
| 145 | 
            -
                  Soup[:powers => 'yes'].should == [@james, @murray]
         | 
| 146 | 
            -
                end
         | 
| 147 | 
            -
              end
         | 
| 148 | 
            -
            end
         | 
    
        data/spec/spec_helper.rb
    DELETED
    
    
    
        data/spec/spec_runner.rb
    DELETED
    
    | @@ -1,12 +0,0 @@ | |
| 1 | 
            -
            #!/usr/bin/env ruby
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            # We need to do a bit of extra work here, because Snip will undefine a 
         | 
| 4 | 
            -
            # whole bunch of stuff that spec adds, like the Object#should method.
         | 
| 5 | 
            -
            $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
         | 
| 6 | 
            -
            require 'soup'
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            # This is basically the contents of rspec's own spec runner. It's just
         | 
| 9 | 
            -
            # that we needed to require 'soup' before it.
         | 
| 10 | 
            -
            require 'rubygems'
         | 
| 11 | 
            -
            require 'spec'
         | 
| 12 | 
            -
            exit ::Spec::Runner::CommandLine.run(rspec_options)
         |