chronicle 0.0.6 → 0.1.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/lib/chronicle.rb +45 -44
- data/lib/chronicle/version.rb +1 -1
- data/spec/chronicle_spec.rb +94 -69
- metadata +4 -4
    
        data/lib/chronicle.rb
    CHANGED
    
    | @@ -51,7 +51,7 @@ module Chronicle | |
| 51 51 | 
             
                  "3 minutes ago",
         | 
| 52 52 | 
             
                  "2 minutes ago",
         | 
| 53 53 | 
             
                  "1 minute ago",
         | 
| 54 | 
            -
                  " | 
| 54 | 
            +
                  "right now",
         | 
| 55 55 | 
             
                  "1 minute from now",
         | 
| 56 56 | 
             
                  "2 minutes from now",
         | 
| 57 57 | 
             
                  "3 minutes from now",
         | 
| @@ -98,59 +98,60 @@ module Chronicle | |
| 98 98 | 
             
                def initialize(collection, options)
         | 
| 99 99 |  | 
| 100 100 | 
             
                  eras = options[:eras]
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                  # Sort order, defaut is new to old
         | 
| 103 | 
            -
                  order = options[:order] || :desc
         | 
| 104 | 
            -
                  
         | 
| 101 | 
            +
                        
         | 
| 105 102 | 
             
                  # Remove objects with nil timestamps
         | 
| 106 | 
            -
                  collection | 
| 103 | 
            +
                  collection.reject! {|obj| obj.send(options[:date_attr]).nil? }
         | 
| 104 | 
            +
                  
         | 
| 105 | 
            +
                  # Determine whether collection contains future or past timestamps
         | 
| 106 | 
            +
                  if collection.all? { |obj| obj.send(options[:date_attr]) < Time.now }
         | 
| 107 | 
            +
                    order = :past
         | 
| 108 | 
            +
                  elsif collection.all? { |obj| obj.send(options[:date_attr]) > Time.now }
         | 
| 109 | 
            +
                    order = :future
         | 
| 110 | 
            +
                  else
         | 
| 111 | 
            +
                    raise "Chronicle collections must be entirely in the past or the future."
         | 
| 112 | 
            +
                  end
         | 
| 107 113 |  | 
| 108 114 | 
             
                  # Sort collection by date
         | 
| 109 | 
            -
                   | 
| 110 | 
            -
                   | 
| 115 | 
            +
                  # For past collections, newest objects come first 
         | 
| 116 | 
            +
                  # For future collections, oldest objects come first
         | 
| 117 | 
            +
                  collection = collection.sort_by {|obj| obj.send(options[:date_attr]) }
         | 
| 118 | 
            +
                  collection.reverse! if order == :past
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  # Force inclusion of 'now' era in case it's missing.
         | 
| 121 | 
            +
                  eras.push('right now').uniq!
         | 
| 111 122 |  | 
| 123 | 
            +
                  # Parse era strings using Chronic
         | 
| 112 124 | 
             
                  # Ensure all eras can be parsed
         | 
| 113 | 
            -
                   | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 125 | 
            +
                  # { "7 years ago" => 2006-01-02 23:29:05 -0800, ... }
         | 
| 126 | 
            +
                  era_date_pairs = eras.inject({}) {|h,era|
         | 
| 127 | 
            +
                    h[era] = Chronic.parse(era)
         | 
| 128 | 
            +
                    raise "Could not parse era: #{era}" if h[era].nil?
         | 
| 129 | 
            +
                    h
         | 
| 130 | 
            +
                  }
         | 
| 116 131 |  | 
| 117 | 
            -
                   | 
| 118 | 
            -
             | 
| 119 | 
            -
                   | 
| 120 | 
            -
                  
         | 
| 121 | 
            -
                   | 
| 122 | 
            -
                  # { "7 years ago"=>2006-01-02 23:29:05 -0800, ... }
         | 
| 123 | 
            -
                  era_date_pairs = eras.inject({}) {|h,e| h[e] = Chronic.parse(e); h }
         | 
| 124 | 
            -
                      
         | 
| 125 | 
            -
                  # Sort eras oldest to newest
         | 
| 126 | 
            -
                  eras = eras.sort_by {|era| Chronic.parse(era) }
         | 
| 127 | 
            -
                  # .. or newest to oldest
         | 
| 128 | 
            -
                  eras = eras.reverse unless order == :desc
         | 
| 132 | 
            +
                  # Sort eras by date
         | 
| 133 | 
            +
                  # For past collections, newest eras come first
         | 
| 134 | 
            +
                  # For future collections, oldest eras come first
         | 
| 135 | 
            +
                  eras = eras.sort_by {|era| era_date_pairs[era] }
         | 
| 136 | 
            +
                  eras.reverse! if order == :future
         | 
| 129 137 |  | 
| 130 | 
            -
                   | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
                    
         | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
                       | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
                    
         | 
| 143 | 
            -
                    # Initialize all hash keys chronologically (oldest to newest)
         | 
| 144 | 
            -
                    eras.reverse.each {|era| self[era] = [] }
         | 
| 145 | 
            -
                    
         | 
| 146 | 
            -
                    # Find the newest era in which each object was created
         | 
| 147 | 
            -
                    collection.each do |obj|
         | 
| 148 | 
            -
                      era = eras.find {|era| obj.send(options[:date_attr]) > era_date_pairs[era]  }
         | 
| 149 | 
            -
                      self[era] << obj
         | 
| 138 | 
            +
                  # Initialize all hash keys chronologically
         | 
| 139 | 
            +
                  eras.reverse.each {|era| self[era] = [] }
         | 
| 140 | 
            +
                  
         | 
| 141 | 
            +
                  collection.each do |obj|
         | 
| 142 | 
            +
                    era = eras.find do |era|
         | 
| 143 | 
            +
                      if order == :future
         | 
| 144 | 
            +
                        # Find newest possible era for the object
         | 
| 145 | 
            +
                        obj.send(options[:date_attr]) > era_date_pairs[era]
         | 
| 146 | 
            +
                      else
         | 
| 147 | 
            +
                        # Find oldest possible era for the object
         | 
| 148 | 
            +
                        obj.send(options[:date_attr]) < era_date_pairs[era]
         | 
| 149 | 
            +
                      end
         | 
| 150 150 | 
             
                    end
         | 
| 151 | 
            +
                    self[era] << obj
         | 
| 151 152 | 
             
                  end
         | 
| 152 153 |  | 
| 153 | 
            -
                  # Remove  | 
| 154 | 
            +
                  # Remove empty eras
         | 
| 154 155 | 
             
                  self.keys.each {|k| self.delete(k) if self[k].empty? }
         | 
| 155 156 |  | 
| 156 157 | 
             
                  self
         | 
    
        data/lib/chronicle/version.rb
    CHANGED
    
    
    
        data/spec/chronicle_spec.rb
    CHANGED
    
    | @@ -6,54 +6,114 @@ describe Chronicle do | |
| 6 6 | 
             
                @minute = 60
         | 
| 7 7 | 
             
                @hour = @minute*60
         | 
| 8 8 | 
             
                @day = @hour*24
         | 
| 9 | 
            -
                @things = [
         | 
| 10 | 
            -
                  double("thing", :created_at => Time.now - 39*@day),
         | 
| 11 | 
            -
                  double("thing", :created_at => Time.now - 8*@day),
         | 
| 12 | 
            -
                  double("thing", :created_at => Time.now - 9*@day),
         | 
| 13 | 
            -
                  double("thing", :created_at => Time.now - 3*@day),
         | 
| 14 | 
            -
                  double("thing", :created_at => Time.now - 10*@minute),
         | 
| 15 | 
            -
                  double("thing", :created_at => Time.now)
         | 
| 16 | 
            -
                ]
         | 
| 17 | 
            -
                
         | 
| 18 | 
            -
                @chronicle = Chronicle.new(@things)
         | 
| 19 9 | 
             
              end
         | 
| 10 | 
            +
              
         | 
| 11 | 
            +
              context "dates in the past and the future" do
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                before do
         | 
| 14 | 
            +
                  offsets = [@day, -@day]
         | 
| 15 | 
            +
                  @things = offsets.map do |offset|
         | 
| 16 | 
            +
                    double("thing", :created_at => Time.now+offset)
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 20 19 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 20 | 
            +
                it "raises an error" do
         | 
| 21 | 
            +
                  expect { Chronicle.new(@things) }.to raise_error("Chronicle collections must be entirely in the past or the future.")
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
                
         | 
| 23 24 | 
             
              end
         | 
| 24 25 |  | 
| 25 | 
            -
               | 
| 26 | 
            -
                 | 
| 27 | 
            -
                 | 
| 28 | 
            -
             | 
| 26 | 
            +
              context "dates in the past" do
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                before do
         | 
| 29 | 
            +
                  offsets = [-39*@day, -8*@day, -9*@day, -3*@day, -10*@minute, 0]
         | 
| 30 | 
            +
                  @things = offsets.map do |offset|
         | 
| 31 | 
            +
                    double("thing", :created_at => Time.now+offset)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  @chronicle = Chronicle.new(@things)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                it "doesn't have any empty eras" do
         | 
| 37 | 
            +
                  @chronicle.values.all? {|v| !v.empty? }.should == true
         | 
| 38 | 
            +
                end
         | 
| 29 39 |  | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                 | 
| 34 | 
            -
              end
         | 
| 40 | 
            +
                it "sorts era keys from newest to oldest" do
         | 
| 41 | 
            +
                  @chronicle.keys.first.should == 'right now'
         | 
| 42 | 
            +
                  @chronicle.keys.last.should == '1 month ago'
         | 
| 43 | 
            +
                end
         | 
| 35 44 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 45 | 
            +
                it "sorts objects in eras from newest to oldest" do
         | 
| 46 | 
            +
                  era = @chronicle['1 week ago']
         | 
| 47 | 
            +
                  era.size.should == 2
         | 
| 48 | 
            +
                  era.last.created_at.should be < era.first.created_at
         | 
| 49 | 
            +
                end
         | 
| 39 50 |  | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
                 | 
| 43 | 
            -
             | 
| 44 | 
            -
                 | 
| 51 | 
            +
                it "doesn't lose any items during processing" do
         | 
| 52 | 
            +
                  @chronicle.values.flatten.size.should == @things.size
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              
         | 
| 55 | 
            +
                it "accounts for objects that were just created" do
         | 
| 56 | 
            +
                  now = @chronicle['right now']
         | 
| 57 | 
            +
                  now.should_not be_empty
         | 
| 58 | 
            +
                  now.should be_an(Array)
         | 
| 59 | 
            +
                  now.first.should == @things.last
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
                
         | 
| 45 62 | 
             
              end
         | 
| 46 63 |  | 
| 64 | 
            +
              context "dates in the future" do
         | 
| 65 | 
            +
                
         | 
| 66 | 
            +
                before(:each) do
         | 
| 67 | 
            +
                  @things = [
         | 
| 68 | 
            +
                    double("thing", :created_at => Time.now + 39*@day),
         | 
| 69 | 
            +
                    double("thing", :created_at => Time.now + 9*@day),
         | 
| 70 | 
            +
                    double("thing", :created_at => Time.now + 8*@day),
         | 
| 71 | 
            +
                    double("thing", :created_at => Time.now + 3*@day),
         | 
| 72 | 
            +
                    double("thing", :created_at => Time.now + 9*@minute),
         | 
| 73 | 
            +
                    double("thing", :created_at => Time.now + 2),
         | 
| 74 | 
            +
                  ]
         | 
| 75 | 
            +
                  @chronicle = Chronicle.new(@things, order: :asc)
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                it "doesn't have any empty eras" do
         | 
| 79 | 
            +
                  @chronicle.values.all? {|v| !v.empty? }.should == true
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
                  
         | 
| 82 | 
            +
                it "sorts era keys from oldest to newest" do
         | 
| 83 | 
            +
                  @chronicle.keys.first.should == 'right now'
         | 
| 84 | 
            +
                  @chronicle.keys.last.should == '1 month from now'
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
                  
         | 
| 87 | 
            +
                it "sorts objects in eras from oldest to newest" do
         | 
| 88 | 
            +
                  era = @chronicle['1 week from now']
         | 
| 89 | 
            +
                  era.size.should == 2
         | 
| 90 | 
            +
                  era.last.created_at.should be > era.first.created_at
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
                  
         | 
| 93 | 
            +
                it "doesn't lose any items during processing" do
         | 
| 94 | 
            +
                  @chronicle.values.flatten.size.should == @things.size
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
                
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 47 99 | 
             
              context "custom eras" do
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                before do
         | 
| 102 | 
            +
                  offsets = [-39*@day, -8*@day, -9*@day, -3*@day, -10*@minute, 0]
         | 
| 103 | 
            +
                  @things = offsets.map do |offset|
         | 
| 104 | 
            +
                    double("thing", :created_at => Time.now+offset)
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                  @chronicle = Chronicle.new(@things)
         | 
| 107 | 
            +
                end
         | 
| 48 108 |  | 
| 49 | 
            -
                it "add ' | 
| 109 | 
            +
                it "add 'right now' to the list of eras if it's missing, to keep from losing very new objects" do
         | 
| 50 110 | 
             
                  @chronicle = Chronicle.new(@things, :eras => ['3 minutes ago', '35 days ago'])
         | 
| 51 | 
            -
                  @chronicle.keys.first.should == ' | 
| 111 | 
            +
                  @chronicle.keys.first.should == 'right now'
         | 
| 52 112 | 
             
                end
         | 
| 53 113 |  | 
| 54 114 | 
             
                it "allows custom eras to be set in any order" do
         | 
| 55 | 
            -
                  @chronicle = Chronicle.new(@things, :eras => [' | 
| 56 | 
            -
                  @chronicle.keys.should == [' | 
| 115 | 
            +
                  @chronicle = Chronicle.new(@things, :eras => ['right now', '35 days ago', '3 minutes ago'])
         | 
| 116 | 
            +
                  @chronicle.keys.should == ['right now', '3 minutes ago', '35 days ago']
         | 
| 57 117 | 
             
                end
         | 
| 58 118 |  | 
| 59 119 | 
             
                it "raises an exception for eras that cannot be parsed" do
         | 
| @@ -79,7 +139,7 @@ describe Chronicle do | |
| 79 139 | 
             
                  @chronicle = Chronicle.new(@things, :date_attr => :updated_at)
         | 
| 80 140 | 
             
                  @chronicle.values.flatten.size.should == @things.size
         | 
| 81 141 | 
             
                  @chronicle.keys.last.should == '1 year ago'
         | 
| 82 | 
            -
                  @chronicle.keys.first.should == ' | 
| 142 | 
            +
                  @chronicle.keys.first.should == 'right now'
         | 
| 83 143 | 
             
                end
         | 
| 84 144 |  | 
| 85 145 | 
             
                it "gracefully ignores objects with nil timestamps" do
         | 
| @@ -93,40 +153,5 @@ describe Chronicle do | |
| 93 153 | 
             
                end
         | 
| 94 154 |  | 
| 95 155 | 
             
              end
         | 
| 96 | 
            -
              
         | 
| 97 | 
            -
              context "future dates" do
         | 
| 98 | 
            -
                
         | 
| 99 | 
            -
                before(:each) do
         | 
| 100 | 
            -
                  @things = [
         | 
| 101 | 
            -
                    double("thing", :created_at => Time.now + 39*@day),
         | 
| 102 | 
            -
                    double("thing", :created_at => Time.now + 8*@day),
         | 
| 103 | 
            -
                    double("thing", :created_at => Time.now + 9*@day),
         | 
| 104 | 
            -
                    double("thing", :created_at => Time.now + 3*@day),
         | 
| 105 | 
            -
                    double("thing", :created_at => Time.now + 9*@minute),
         | 
| 106 | 
            -
                  ]
         | 
| 107 | 
            -
                  @chronicle = Chronicle.new(@things, order: :asc)
         | 
| 108 | 
            -
                end
         | 
| 109 | 
            -
             | 
| 110 | 
            -
                it "doesn't have any empty eras" do
         | 
| 111 | 
            -
                  @chronicle.values.all? {|v| !v.empty? }.should == true
         | 
| 112 | 
            -
                end
         | 
| 113 | 
            -
                  
         | 
| 114 | 
            -
                it "sorts era keys from oldest to newest" do
         | 
| 115 | 
            -
                  @chronicle.keys.first.should == '5 minutes from now'
         | 
| 116 | 
            -
                  @chronicle.keys.last.should == '1 month from now'
         | 
| 117 | 
            -
                end
         | 
| 118 | 
            -
                  
         | 
| 119 | 
            -
                it "sorts objects in eras from oldest to newest" do
         | 
| 120 | 
            -
                  era = @chronicle['1 week from now']
         | 
| 121 | 
            -
                  era.size.should == 2
         | 
| 122 | 
            -
                  era.last.created_at.should be > era.first.created_at
         | 
| 123 | 
            -
                end
         | 
| 124 | 
            -
                  
         | 
| 125 | 
            -
                it "doesn't lose any items during processing" do
         | 
| 126 | 
            -
                  @chronicle.values.flatten.size.should == @things.size
         | 
| 127 | 
            -
                end
         | 
| 128 | 
            -
                
         | 
| 129 | 
            -
              end
         | 
| 130 | 
            -
             | 
| 131 156 |  | 
| 132 157 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: chronicle
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0 | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2013-01- | 
| 12 | 
            +
            date: 2013-01-07 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: hoe
         | 
| @@ -106,7 +106,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 106 106 | 
             
                  version: '0'
         | 
| 107 107 | 
             
                  segments:
         | 
| 108 108 | 
             
                  - 0
         | 
| 109 | 
            -
                  hash:  | 
| 109 | 
            +
                  hash: 408063275270650171
         | 
| 110 110 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 111 111 | 
             
              none: false
         | 
| 112 112 | 
             
              requirements:
         | 
| @@ -115,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 115 115 | 
             
                  version: '0'
         | 
| 116 116 | 
             
                  segments:
         | 
| 117 117 | 
             
                  - 0
         | 
| 118 | 
            -
                  hash:  | 
| 118 | 
            +
                  hash: 408063275270650171
         | 
| 119 119 | 
             
            requirements: []
         | 
| 120 120 | 
             
            rubyforge_project: chronicle
         | 
| 121 121 | 
             
            rubygems_version: 1.8.24
         |