dm-is-temporal 0.0.1 → 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/README.md +109 -32
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/dm-is-temporal/is/temporal.rb +38 -9
- data/spec/temporal_spec.rb +244 -4
- metadata +3 -4
- data/Gemfile +0 -0
    
        data/README.md
    CHANGED
    
    | @@ -6,12 +6,13 @@ DataMapper plugin implementing temporal patterns on DataMapper models. | |
| 6 6 | 
             
            These patterns are based on research by Martin Fowler, Richard Snodgrass and others.  For more information follow these links:
         | 
| 7 7 |  | 
| 8 8 | 
             
            +  [Temporal Patterns](http://martinfowler.com/eaaDev/timeNarrative.html)
         | 
| 9 | 
            -
             | 
| 10 9 | 
             
            +  [Developing Time-Oriented Database Applications in SQL](http://www.cs.arizona.edu/people/rts/publications.html)
         | 
| 11 10 |  | 
| 12 11 | 
             
            Examples
         | 
| 13 12 | 
             
            ---------
         | 
| 14 13 |  | 
| 14 | 
            +
            So lets assume you have a simple class. The plugin will automatically create some auxillary tables when you auto_migrate.
         | 
| 15 | 
            +
             | 
| 15 16 | 
             
                require 'rubygems'
         | 
| 16 17 | 
             
                require 'dm-core'
         | 
| 17 18 | 
             
                require 'dm-migrations'
         | 
| @@ -32,38 +33,114 @@ Examples | |
| 32 33 | 
             
                end
         | 
| 33 34 |  | 
| 34 35 | 
             
                DataMapper.auto_migrate!
         | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
                m.foo
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                m. | 
| 41 | 
            -
                 | 
| 42 | 
            -
                
         | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
                m.name  
         | 
| 47 | 
            -
                #=> 'hello'
         | 
| 48 | 
            -
                
         | 
| 49 | 
            -
                m.foo
         | 
| 50 | 
            -
                #= 2
         | 
| 51 | 
            -
                
         | 
| 52 | 
            -
                m.foo = 42
         | 
| 53 | 
            -
                    
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            You can create, modify and access it as normal:
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                m = MyModel.create(:name => 'start', :foo => 42)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                m.bar = 'hello'
         | 
| 42 | 
            +
                m.foo              #= 42
         | 
| 43 | 
            +
                m.name             #=> 'start'
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            Or you can access it at different times (future or past):
         | 
| 46 | 
            +
             | 
| 54 47 | 
             
                old = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 55 | 
            -
                 | 
| 56 | 
            -
             | 
| 57 | 
            -
                m.foo
         | 
| 58 | 
            -
                #=> 42
         | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
                 | 
| 63 | 
            -
                 | 
| 64 | 
            -
                 | 
| 65 | 
            -
             | 
| 66 | 
            -
                
         | 
| 48 | 
            +
                now = DateTime.now
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                m.at(old).foo         #=> nil
         | 
| 51 | 
            +
                m.at(now).foo         #=> 42
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            But it really gets interesting when you modify it `at` different `DateTime`s
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 56 | 
            +
                nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 57 | 
            +
                future = DateTime.parse('4712-01-01T00:00:00+00:00')
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                m.at(oldish).foo = 1
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                m.at(oldish).foo       #=> 1
         | 
| 62 | 
            +
                m.at(nowish).foo       #=> 42
         | 
| 63 | 
            +
                m.at(future).foo       #=> 42
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                m.at(nowish).foo = 1024
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                m.at(oldish).foo        #=> 1
         | 
| 68 | 
            +
                m.at(nowish).foo        #=> 1024
         | 
| 69 | 
            +
                m.at(future).foo        #=> 1024
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                m.at(future).foo = 3
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                m.at(oldish).foo        #=> 1
         | 
| 74 | 
            +
                m.at(nowish).foo        #=> 1024
         | 
| 75 | 
            +
                m.at(future).foo        #=> 3
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            Remember that properties outside of the `is_temporal` block are not versioned.  But you can read and write them though the `at(time)` method if you want:
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                m.at(now).name = "finished"
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                m.at(old).name        #=> 'finished'
         | 
| 82 | 
            +
                m.at(now).name        #=> 'finished'
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            If you try to set a value at the same time as one you already set, it will overwrite the previous value (like non-temporal models).  In future versions of dm-is-temporal you will be able to configure if this works or causes an error.
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                m.at(nowish).foo = 11
         | 
| 87 | 
            +
                m.at(nowish).foo         #=> 11
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                m.at(nowish).foo = 22
         | 
| 90 | 
            +
                m.at(nowish).foo         #=> 22
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            You can also update several properties with the same time in a block (I use `v` for "version" here)
         | 
| 93 | 
            +
             | 
| 94 | 
            +
             | 
| 95 | 
            +
                m.at(nowish) do |v|
         | 
| 96 | 
            +
                  v.foo = 42
         | 
| 97 | 
            +
                  v.bar = "cat"
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                m.at(nowish).foo           #=> 42
         | 
| 101 | 
            +
                m.at(nowish).bar           #=> 'cat'
         | 
| 102 | 
            +
             | 
| 103 | 
            +
             | 
| 104 | 
            +
            How it works
         | 
| 105 | 
            +
            -------------
         | 
| 106 | 
            +
            Temporal patterns differ from versioning patterns (such as [dm-is-versioned](https://github.com/datamapper/dm-is-versioned))
         | 
| 107 | 
            +
            in that every version of the temporal properties is a peer (even the most recent).  Accessing temporal properties without the `at(time)` method
         | 
| 108 | 
            +
            is just a convinience for `at(DateTime.now)`.
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            In addition, you have the ability inject versions at previous time-states (modifying history).
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            When you use the `is_temporal` form, the plugin will dynamically create a temporal version table.  In the example above,
         | 
| 113 | 
            +
            these two tables would be created:
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                # db.my_models table
         | 
| 116 | 
            +
                ---------------------------------------------
         | 
| 117 | 
            +
                | id | name                                 |
         | 
| 118 | 
            +
                ---------------------------------------------
         | 
| 119 | 
            +
                | 1  | 'start'                              |
         | 
| 120 | 
            +
             | 
| 121 | 
            +
             | 
| 122 | 
            +
                # db.my_model_temporal_versions table
         | 
| 123 | 
            +
                -----------------------------------------------------------------------
         | 
| 124 | 
            +
                | id | foo          | bar            | updated_at     | my_model_id   |
         | 
| 125 | 
            +
                -----------------------------------------------------------------------
         | 
| 126 | 
            +
                | 1  | '42'         | null           | DateTime       | 1             |
         | 
| 127 | 
            +
                | 2  | '1024'       | null           | DateTime       | 1             |
         | 
| 128 | 
            +
                | 3  | '1024'       | 'hello'        | DateTime       | 1             |
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            Thanks
         | 
| 131 | 
            +
            ------
         | 
| 132 | 
            +
            Thanks to the [dm-is-versioned](https://github.com/datamapper/dm-is-versioned) folks!  I based a lot of my infrastructure
         | 
| 133 | 
            +
            on that project.
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            TODO
         | 
| 136 | 
            +
            ------
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            +  MyClass.update (update all records for a model) doesn't work
         | 
| 139 | 
            +
            +  Temporal Associations
         | 
| 140 | 
            +
            +  Temporal Property pattern (i.e. multiple independent temporal properties per class)
         | 
| 141 | 
            +
            +  Bi-temporality
         | 
| 142 | 
            +
            +  Add a config flag that enables an error to be raised when attempting to rewrite existing versions
         | 
| 143 | 
            +
             | 
| 67 144 |  | 
| 68 145 | 
             
            Copyright
         | 
| 69 146 | 
             
            ----------
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.0 | 
| 1 | 
            +
            0.2.0
         | 
| @@ -41,7 +41,7 @@ module DataMapper | |
| 41 41 |  | 
| 42 42 | 
             
                      version_model.property(:id, DataMapper::Property::Serial)
         | 
| 43 43 | 
             
                      version_model.property(:updated_at, DataMapper::Property::DateTime)
         | 
| 44 | 
            -
                      version_model.before(:save) { self.updated_at  | 
| 44 | 
            +
                      version_model.before(:save) { self.updated_at ||= DateTime.now }
         | 
| 45 45 | 
             
                      version_model.instance_eval(&block)
         | 
| 46 46 |  | 
| 47 47 | 
             
                      const_set(:TemporalVersion, version_model)
         | 
| @@ -70,11 +70,16 @@ module DataMapper | |
| 70 70 | 
             
                    class_eval <<-RUBY
         | 
| 71 71 |  | 
| 72 72 | 
             
                      def self.update(options={})
         | 
| 73 | 
            -
                        raise ' | 
| 73 | 
            +
                        raise "Updating all doesn't work yet"
         | 
| 74 | 
            +
            #            t_opts = __select_temporal_options__(options)
         | 
| 75 | 
            +
            #            raise "Can't update at temporal properties from class level yet." if !t_opts.empty?
         | 
| 76 | 
            +
            #            super.update(options)
         | 
| 74 77 | 
             
                      end
         | 
| 75 78 |  | 
| 76 | 
            -
                      def self.all( | 
| 77 | 
            -
                         | 
| 79 | 
            +
                      def self.all(options={})
         | 
| 80 | 
            +
                        t_opts = __select_temporal_options__(options)
         | 
| 81 | 
            +
                        raise "Can't select all by temporal properties from class level yet." if !t_opts.empty?             
         | 
| 82 | 
            +
                        super.all(options)
         | 
| 78 83 | 
             
                      end
         | 
| 79 84 |  | 
| 80 85 | 
             
                      def self.create(options={})
         | 
| @@ -87,8 +92,13 @@ module DataMapper | |
| 87 92 | 
             
                        base
         | 
| 88 93 | 
             
                      end
         | 
| 89 94 |  | 
| 90 | 
            -
                      def at(context)
         | 
| 91 | 
            -
                         | 
| 95 | 
            +
                      def at(context=DateTime.now, &block)
         | 
| 96 | 
            +
                        if block_given?
         | 
| 97 | 
            +
                          yield TemporalProxy.new(self, context)
         | 
| 98 | 
            +
                        else
         | 
| 99 | 
            +
                          # this is hokie.  need to do better
         | 
| 100 | 
            +
                          @__at__ = context
         | 
| 101 | 
            +
                        end
         | 
| 92 102 | 
             
                        self
         | 
| 93 103 | 
             
                      end
         | 
| 94 104 |  | 
| @@ -141,10 +151,15 @@ module DataMapper | |
| 141 151 | 
             
                  def create_temporal_writer(name)
         | 
| 142 152 | 
             
                    class_eval <<-RUBY
         | 
| 143 153 | 
             
                      def #{name}=(x)
         | 
| 154 | 
            +
                        at = @__at__
         | 
| 144 155 | 
             
                        t = __version_for_context__
         | 
| 145 | 
            -
                         | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 156 | 
            +
                        if t.nil?
         | 
| 157 | 
            +
                          t = TemporalVersion.create(:updated_at => at)
         | 
| 158 | 
            +
                          temporal_versions << t
         | 
| 159 | 
            +
                        elsif t.updated_at != at
         | 
| 160 | 
            +
                          t = TemporalVersion.create(t.attributes.merge(:id => nil, :updated_at => at))
         | 
| 161 | 
            +
                          temporal_versions << t
         | 
| 162 | 
            +
                        end
         | 
| 148 163 | 
             
                        t.#{name} = x
         | 
| 149 164 | 
             
                        self.save
         | 
| 150 165 | 
             
                        #{name}
         | 
| @@ -152,6 +167,20 @@ module DataMapper | |
| 152 167 | 
             
                    RUBY
         | 
| 153 168 | 
             
                  end
         | 
| 154 169 |  | 
| 170 | 
            +
                  class TemporalProxy
         | 
| 171 | 
            +
                    # make this a blank slate
         | 
| 172 | 
            +
                    instance_methods.each { |m| undef_method m unless m =~ /^__/ }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    def initialize(proxied_object, context)
         | 
| 175 | 
            +
                      @proxied_object = proxied_object
         | 
| 176 | 
            +
                      @context = context
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    def method_missing(sym, *args, &block)
         | 
| 180 | 
            +
                      @proxied_object.at(@context).__send__(sym, *args, &block)
         | 
| 181 | 
            +
                    end
         | 
| 182 | 
            +
                  end
         | 
| 183 | 
            +
             | 
| 155 184 | 
             
                  module Migration
         | 
| 156 185 |  | 
| 157 186 | 
             
                    def auto_migrate!(repository_name = self.repository_name)
         | 
    
        data/spec/temporal_spec.rb
    CHANGED
    
    | @@ -4,10 +4,6 @@ require 'spec_helper' | |
| 4 4 | 
             
            class MyModel
         | 
| 5 5 | 
             
              include DataMapper::Resource
         | 
| 6 6 |  | 
| 7 | 
            -
            #  def self.default_repository_name
         | 
| 8 | 
            -
            #    :test
         | 
| 9 | 
            -
            #  end
         | 
| 10 | 
            -
             | 
| 11 7 | 
             
              property :id, Serial
         | 
| 12 8 | 
             
              property :name, String
         | 
| 13 9 |  | 
| @@ -47,6 +43,98 @@ describe DataMapper::Is::Temporal do | |
| 47 43 | 
             
                  MyModel.create
         | 
| 48 44 | 
             
                end
         | 
| 49 45 |  | 
| 46 | 
            +
                it "version has the right parent" do
         | 
| 47 | 
            +
                  subject.foo = 42
         | 
| 48 | 
            +
                  subject.instance_eval { puts self.temporal_versions[0].my_model_id.should == self.id}
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                it "update all still works for non-temporal properties" do
         | 
| 52 | 
            +
                  pending
         | 
| 53 | 
            +
                  MyModel.update(:name => 'all the same')
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  subject.name.should == 'all the same'
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it "select all still works for non-temporal properties" do
         | 
| 59 | 
            +
                  subject.name = 'looking for me!'
         | 
| 60 | 
            +
                  subject.save
         | 
| 61 | 
            +
                  all = MyModel.all(:name => 'looking for me!')
         | 
| 62 | 
            +
                  all.size.should == 1
         | 
| 63 | 
            +
                  all[0].name.should == 'looking for me!'
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                context "non-temporal properties" do
         | 
| 67 | 
            +
                  it "should work as normal" do
         | 
| 68 | 
            +
                    subject.name = 'foo'
         | 
| 69 | 
            +
                    subject.name.should == 'foo'
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    subject.name = 'bar'
         | 
| 72 | 
            +
                    subject.name.should == 'bar'
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  it "should work when accessed via at(time)" do
         | 
| 76 | 
            +
                    oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 77 | 
            +
                    nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 78 | 
            +
                    future = DateTime.parse('4712-01-01T00:00:00+00:00')
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    subject.at(oldish).name = 'foo'
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    subject.at(oldish).name.should == 'foo'
         | 
| 83 | 
            +
                    subject.at(nowish).name.should == 'foo'
         | 
| 84 | 
            +
                    subject.at(future).name.should == 'foo'
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    subject.at(nowish).name = 'bar'
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    subject.at(oldish).name.should == 'bar'
         | 
| 89 | 
            +
                    subject.at(nowish).name.should == 'bar'
         | 
| 90 | 
            +
                    subject.at(future).name.should == 'bar'
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    subject.name = 'rat'
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    subject.at(oldish).name.should == 'rat'
         | 
| 95 | 
            +
                    subject.at(nowish).name.should == 'rat'
         | 
| 96 | 
            +
                    subject.at(future).name.should == 'rat'    
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                context "setting temporal properties" do
         | 
| 101 | 
            +
                  it "works" do
         | 
| 102 | 
            +
                    oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 103 | 
            +
                    nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 104 | 
            +
                    future = DateTime.parse('4712-01-01T00:00:00+00:00')
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    subject.at(oldish).foo = 42
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 109 | 
            +
                    subject.at(nowish).foo.should == 42
         | 
| 110 | 
            +
                    subject.at(future).foo.should == 42
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    subject.at(nowish).foo = 1024
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 115 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 116 | 
            +
                    subject.at(future).foo.should == 1024
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    subject.at(future).foo = 3
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 121 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 122 | 
            +
                    subject.at(future).foo.should == 3
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    subject.instance_eval { puts self.temporal_versions.size.should == 3}
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  it "and rewriting works" do
         | 
| 128 | 
            +
                    now = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    subject.at(now).foo = 42
         | 
| 131 | 
            +
                    subject.at(now).foo.should == 42
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    subject.at(now).foo = 1
         | 
| 134 | 
            +
                    subject.at(now).foo.should == 1
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
             | 
| 50 138 | 
             
                context "when at context" do
         | 
| 51 139 | 
             
                  it "returns old values" do
         | 
| 52 140 | 
             
                    subject.foo.should == nil
         | 
| @@ -58,6 +146,12 @@ describe DataMapper::Is::Temporal do | |
| 58 146 |  | 
| 59 147 | 
             
                    subject.at(old).foo.should == nil
         | 
| 60 148 | 
             
                    subject.at(now).foo.should == 42
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    subject.foo = 1024
         | 
| 151 | 
            +
                    subject.foo.should == 1024
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    subject.at(old).foo.should == nil
         | 
| 154 | 
            +
                    subject.at(now).foo.should == 42
         | 
| 61 155 | 
             
                  end
         | 
| 62 156 | 
             
                end
         | 
| 63 157 |  | 
| @@ -67,6 +161,15 @@ describe DataMapper::Is::Temporal do | |
| 67 161 | 
             
                    subject.foo = 42
         | 
| 68 162 | 
             
                    subject.foo.should == 42
         | 
| 69 163 | 
             
                  end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  it "returns 'same' for bar" do
         | 
| 166 | 
            +
                    subject.bar = 'same'
         | 
| 167 | 
            +
                    subject.bar.should == 'same'
         | 
| 168 | 
            +
                    subject.foo.should == nil
         | 
| 169 | 
            +
                    subject.foo = 42
         | 
| 170 | 
            +
                    subject.foo.should == 42
         | 
| 171 | 
            +
                    subject.bar.should == 'same'
         | 
| 172 | 
            +
                  end
         | 
| 70 173 | 
             
                end
         | 
| 71 174 |  | 
| 72 175 | 
             
                context "when bar is 'hello'" do
         | 
| @@ -166,5 +269,142 @@ describe DataMapper::Is::Temporal do | |
| 166 269 | 
             
                    end
         | 
| 167 270 | 
             
                  end
         | 
| 168 271 | 
             
                end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                context "when multi setting" do
         | 
| 274 | 
            +
                  it "works with different times" do
         | 
| 275 | 
            +
                    oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 276 | 
            +
                    nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 277 | 
            +
                    future = DateTime.parse('4712-01-01T00:00:00+00:00')
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                    subject.at(oldish) do |s|
         | 
| 280 | 
            +
                      s.foo = 42
         | 
| 281 | 
            +
                      s.bar = "cat"
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 285 | 
            +
                    subject.at(nowish).foo.should == 42
         | 
| 286 | 
            +
                    subject.at(future).foo.should == 42
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 289 | 
            +
                    subject.at(nowish).bar.should == "cat"
         | 
| 290 | 
            +
                    subject.at(future).bar.should == "cat"
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                    subject.at(nowish) do |s|
         | 
| 293 | 
            +
                      s.foo = 1024
         | 
| 294 | 
            +
                      s.bar = "dog"
         | 
| 295 | 
            +
                    end
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 298 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 299 | 
            +
                    subject.at(future).foo.should == 1024
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 302 | 
            +
                    subject.at(nowish).bar.should == "dog"
         | 
| 303 | 
            +
                    subject.at(future).bar.should == "dog"
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                    subject.at(future) do |s|
         | 
| 306 | 
            +
                      s.foo = 3
         | 
| 307 | 
            +
                      s.bar = "rat"
         | 
| 308 | 
            +
                    end
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 311 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 312 | 
            +
                    subject.at(future).foo.should == 3
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 315 | 
            +
                    subject.at(nowish).bar.should == "dog"
         | 
| 316 | 
            +
                    subject.at(future).bar.should == "rat"
         | 
| 317 | 
            +
             | 
| 318 | 
            +
                    subject.instance_eval { puts self.temporal_versions.size.should == 3}
         | 
| 319 | 
            +
                  end
         | 
| 320 | 
            +
             | 
| 321 | 
            +
                  it "works with different times and non-temporal properties" do
         | 
| 322 | 
            +
                    oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
         | 
| 323 | 
            +
                    nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
         | 
| 324 | 
            +
                    future = DateTime.parse('4712-01-01T00:00:00+00:00')
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                    subject.at(oldish) do |s|
         | 
| 327 | 
            +
                      s.name = "same"
         | 
| 328 | 
            +
                      s.foo = 42
         | 
| 329 | 
            +
                      s.bar = "cat"
         | 
| 330 | 
            +
                    end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 333 | 
            +
                    subject.at(nowish).foo.should == 42
         | 
| 334 | 
            +
                    subject.at(future).foo.should == 42
         | 
| 335 | 
            +
             | 
| 336 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 337 | 
            +
                    subject.at(nowish).bar.should == "cat"
         | 
| 338 | 
            +
                    subject.at(future).bar.should == "cat"
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                    subject.at(oldish).name.should == "same"
         | 
| 341 | 
            +
                    subject.at(nowish).name.should == "same"
         | 
| 342 | 
            +
                    subject.at(future).name.should == "same"
         | 
| 343 | 
            +
             | 
| 344 | 
            +
                    subject.at(nowish) do |s|
         | 
| 345 | 
            +
                      s.name = "every"
         | 
| 346 | 
            +
                      s.foo = 1024
         | 
| 347 | 
            +
                      s.bar = "dog"
         | 
| 348 | 
            +
                    end
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 351 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 352 | 
            +
                    subject.at(future).foo.should == 1024
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 355 | 
            +
                    subject.at(nowish).bar.should == "dog"
         | 
| 356 | 
            +
                    subject.at(future).bar.should == "dog"
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                    subject.at(oldish).name.should == "every"
         | 
| 359 | 
            +
                    subject.at(nowish).name.should == "every"
         | 
| 360 | 
            +
                    subject.at(future).name.should == "every"
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    subject.at(future) do |s|
         | 
| 363 | 
            +
                      s.name = "time"
         | 
| 364 | 
            +
                      s.foo = 3
         | 
| 365 | 
            +
                      s.bar = "rat"
         | 
| 366 | 
            +
                    end
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                    subject.at(oldish).foo.should == 42
         | 
| 369 | 
            +
                    subject.at(nowish).foo.should == 1024
         | 
| 370 | 
            +
                    subject.at(future).foo.should == 3
         | 
| 371 | 
            +
             | 
| 372 | 
            +
                    subject.at(oldish).bar.should == "cat"
         | 
| 373 | 
            +
                    subject.at(nowish).bar.should == "dog"
         | 
| 374 | 
            +
                    subject.at(future).bar.should == "rat"
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                    subject.at(oldish).name.should == "time"
         | 
| 377 | 
            +
                    subject.at(nowish).name.should == "time"
         | 
| 378 | 
            +
                    subject.at(future).name.should == "time"
         | 
| 379 | 
            +
             | 
| 380 | 
            +
                    subject.instance_eval { puts self.temporal_versions.size.should == 3}
         | 
| 381 | 
            +
                  end
         | 
| 382 | 
            +
             | 
| 383 | 
            +
                  it "works with no time (i.e. now)" do
         | 
| 384 | 
            +
                    subject.at do |s|
         | 
| 385 | 
            +
                      s.foo = 42
         | 
| 386 | 
            +
                      s.bar = "cat"
         | 
| 387 | 
            +
                    end
         | 
| 388 | 
            +
             | 
| 389 | 
            +
                    subject.foo.should == 42
         | 
| 390 | 
            +
                    subject.bar.should == "cat"
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                    subject.instance_eval { puts self.temporal_versions.size.should == 1}
         | 
| 393 | 
            +
                  end
         | 
| 394 | 
            +
             | 
| 395 | 
            +
                  it "works with no time (i.e. now)" do
         | 
| 396 | 
            +
                    subject.at do |s|
         | 
| 397 | 
            +
                      s.name = "foobar"
         | 
| 398 | 
            +
                      s.foo = 42
         | 
| 399 | 
            +
                      s.bar = "cat"
         | 
| 400 | 
            +
                    end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                    subject.foo.should == 42
         | 
| 403 | 
            +
                    subject.bar.should == "cat"
         | 
| 404 | 
            +
                    subject.name.should == "foobar"
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                    subject.instance_eval { puts self.temporal_versions.size.should == 1}
         | 
| 407 | 
            +
                  end
         | 
| 408 | 
            +
                end
         | 
| 169 409 | 
             
              end
         | 
| 170 410 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version | |
| 4 4 | 
             
              prerelease: false
         | 
| 5 5 | 
             
              segments: 
         | 
| 6 6 | 
             
                - 0
         | 
| 7 | 
            +
                - 2
         | 
| 7 8 | 
             
                - 0
         | 
| 8 | 
            -
             | 
| 9 | 
            -
              version: 0.0.1
         | 
| 9 | 
            +
              version: 0.2.0
         | 
| 10 10 | 
             
            platform: ruby
         | 
| 11 11 | 
             
            authors: 
         | 
| 12 12 | 
             
              - Joe Kutner
         | 
| @@ -14,7 +14,7 @@ autorequire: | |
| 14 14 | 
             
            bindir: bin
         | 
| 15 15 | 
             
            cert_chain: []
         | 
| 16 16 |  | 
| 17 | 
            -
            date: 2011-03- | 
| 17 | 
            +
            date: 2011-03-22 00:00:00 -05:00
         | 
| 18 18 | 
             
            default_executable: 
         | 
| 19 19 | 
             
            dependencies: []
         | 
| 20 20 |  | 
| @@ -28,7 +28,6 @@ extra_rdoc_files: | |
| 28 28 | 
             
              - LICENSE.txt
         | 
| 29 29 | 
             
              - README.md
         | 
| 30 30 | 
             
            files: 
         | 
| 31 | 
            -
              - Gemfile
         | 
| 32 31 | 
             
              - LICENSE.txt
         | 
| 33 32 | 
             
              - README.md
         | 
| 34 33 | 
             
              - Rakefile
         | 
    
        data/Gemfile
    DELETED
    
    | 
            File without changes
         |