chronicle 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in chronicle.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ Chronicle
2
+ =========
3
+
4
+ Chronicle groups collections of ActiveRecord objects into chronologically ordered hashes.
5
+ It uses [Chronic](https://github.com/mojombo/chronic/) to parse natural language dates
6
+ and [ActiveSupport::OrderedHash](http://apidock.com/rails/ActiveSupport/OrderedHash)
7
+ to keep the hash order consistent.
8
+
9
+ ```ruby
10
+ # Before...
11
+ [
12
+ object_1,
13
+ object_2,
14
+ object_3,
15
+ object_4,
16
+ object_5,
17
+ object_6,
18
+ object_7,
19
+ object_8
20
+ ]
21
+
22
+ # After...
23
+ {
24
+ "just now": [object_10, object_9],
25
+ "two hours ago": [object_8, object_7, object_6],
26
+ "yesterday": [object_5, object_4, object_3],
27
+ "6 months ago": [object_2],
28
+ "1 year ago": [object_1]
29
+ }
30
+ ```
31
+
32
+ Installation
33
+ ------------
34
+
35
+ ```ruby
36
+ # Put this in your Gemfile and smoke it.
37
+ gem 'chronicle'
38
+ ```
39
+
40
+ Usage
41
+ -----
42
+
43
+ ```ruby
44
+ # Fetch some objects (presumably ActiveRecord)
45
+ things = Thing.all
46
+
47
+ # Put them into buckets, using Chronicle's default eras
48
+ chronicle = Chronicle.new(things)
49
+
50
+ # To deviate from the default eras...
51
+ chronicle = Chronicle.new(things, :eras => ["5 minutes ago", "2 hours ago", "three weeks ago"])
52
+
53
+ # To sort based on an attribute other than :created_at
54
+ chronicle = Chronicle.new(things, :date_attr => :updated_at)
55
+ ```
56
+
57
+ License
58
+ -------
59
+
60
+ MIT License. Do whatever you want.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+ task :default => :spec
data/chronicle.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "chronicle/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "chronicle"
7
+ s.version = Chronicle::VERSION
8
+ s.authors = ["Zeke Sikelianos"]
9
+ s.email = ["zeke@sikelianos.com"]
10
+ s.homepage = "http://zeke.sikelianos.com"
11
+ s.summary = %q{Chronicle groups collections of ActiveRecord objects into chronologically ordered hashes.}
12
+ s.description = %q{Chronicle groups collections of ActiveRecord objects into chronologically ordered hashes.}
13
+
14
+ s.rubyforge_project = "chronicle"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "hoe"
23
+ s.add_development_dependency 'rspec', '~> 2.8.0'
24
+ s.add_development_dependency 'autotest'
25
+
26
+ s.add_runtime_dependency "chronic"
27
+ s.add_dependency 'rails'
28
+ end
data/lib/chronicle.rb ADDED
@@ -0,0 +1,82 @@
1
+ require 'rubygems'
2
+ require 'chronic'
3
+ require 'active_support/all' # OrderedHash
4
+ require 'chronicle'
5
+
6
+ module Chronicle
7
+
8
+ def self.new(collection, options={})
9
+ defaults = {
10
+ :eras => Chronicle.default_eras,
11
+ :date_attr => :created_at
12
+ }
13
+ ChronicleHash.new(collection, defaults.merge(options))
14
+ end
15
+
16
+ def self.default_eras
17
+ [
18
+ "3 years ago",
19
+ "2 years ago",
20
+ "one year ago",
21
+ "6 months ago",
22
+ "3 months ago",
23
+ "2 months ago",
24
+ "one month ago",
25
+ "3 weeks ago",
26
+ "2 weeks ago",
27
+ "one week ago",
28
+ "6 days ago",
29
+ "4 days ago",
30
+ "3 days ago",
31
+ "2 days ago",
32
+ "yesterday",
33
+ "8 hours ago",
34
+ "4 hours ago",
35
+ "2 hours ago",
36
+ "1 hour ago",
37
+ "30 minutes ago",
38
+ "20 minutes ago",
39
+ "10 minutes ago",
40
+ "5 minutes ago",
41
+ "3 minutes ago",
42
+ "2 minutes ago",
43
+ "one minute ago",
44
+ "just now"
45
+ ]
46
+ end
47
+
48
+ class ChronicleHash < ActiveSupport::OrderedHash
49
+
50
+ def initialize(collection, options)
51
+
52
+ eras = options[:eras]
53
+
54
+ # Make sure 'just now' is included, so no objects fall through the cracks
55
+ eras << "just now" unless eras.any? {|era| era =~ /now/i }
56
+
57
+ # Ensure all eras can be parsed
58
+ eras.each do |era|
59
+ raise "Could not parse era: #{era}" if Chronic.parse(era).nil?
60
+ end
61
+
62
+ # Sort eras oldest to newest
63
+ eras = eras.sort_by {|era| Chronic.parse(era) }
64
+
65
+ # Initialize all OrderedHash keys chronologically (newest to oldest)
66
+ eras.reverse.each {|era| self[era] = [] }
67
+
68
+ # Find the oldest era in which each object was created
69
+ collection.sort_by {|obj| obj.send(options[:date_attr])}.reverse.each do |obj|
70
+ era = eras.find {|era| obj.send(options[:date_attr]) < Chronic.parse(era) }
71
+ self[era] << obj
72
+ end
73
+
74
+ # Remove keys for empty eras
75
+ self.keys.each {|k| self.delete(k) if self[k].blank? }
76
+
77
+ self
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,3 @@
1
+ module Chronicle
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chronicle do
4
+
5
+ before(:each) do
6
+ @things = [
7
+ double("thing", :created_at => 39.days.ago),
8
+ double("thing", :created_at => 8.days.ago),
9
+ double("thing", :created_at => 9.days.ago),
10
+ double("thing", :created_at => 3.days.ago),
11
+ double("thing", :created_at => 10.minutes.ago),
12
+ double("thing", :created_at => Time.now)
13
+ ]
14
+
15
+ @chronicle = Chronicle.new(@things)
16
+ end
17
+
18
+ it "doesn't have any empty eras" do
19
+ @chronicle.values.all? {|v| v.present? }.should == true
20
+ end
21
+
22
+ it "sorts era keys from newest to oldest" do
23
+ @chronicle.keys.first.should == 'just now'
24
+ @chronicle.keys.last.should == 'one month ago'
25
+ end
26
+
27
+ it "sorts objects in eras from newest to oldest" do
28
+ era = @chronicle['one week ago']
29
+ era.size.should == 2
30
+ era.last.created_at.should be < era.first.created_at
31
+ end
32
+
33
+ it "doesn't lose any items during processing" do
34
+ @chronicle.values.flatten.size.should == @things.size
35
+ end
36
+
37
+ it "accounts for objects that were just created" do
38
+ now = @chronicle['just now']
39
+ now.should_not be_blank
40
+ now.should be_an(Array)
41
+ now.first.should == @things.last
42
+ end
43
+
44
+ context "custom eras" do
45
+
46
+ it "add 'just now' to the list of eras if it's missing, to keep from losing very new objects" do
47
+ @chronicle = Chronicle.new(@things, :eras => ['3 minutes ago', '35 days ago'])
48
+ @chronicle.keys.first.should == 'just now'
49
+ end
50
+
51
+ it "allows custom eras to be set in any order" do
52
+ @chronicle = Chronicle.new(@things, :eras => ['just now', '35 days ago', '3 minutes ago'])
53
+ @chronicle.keys.should == ['just now', '3 minutes ago', '35 days ago']
54
+ end
55
+
56
+ it "raises an exception for eras that cannot be parse" do
57
+ expect { Chronicle.new(@things, :eras => ['63 eons hence']) }.to raise_error("Could not parse era: 63 eons hence")
58
+ end
59
+
60
+ end
61
+
62
+ context "custom date attribute" do
63
+
64
+ before(:each) do
65
+ @things = [
66
+ double("thing", :updated_at => 369.days.ago, :created_at => nil),
67
+ double("thing", :updated_at => 9.days.ago, :created_at => nil),
68
+ double("thing", :updated_at => 8.days.ago, :created_at => nil),
69
+ double("thing", :updated_at => 3.days.ago, :created_at => nil),
70
+ double("thing", :updated_at => 10.minutes.ago, :created_at => nil),
71
+ double("thing", :updated_at => Time.now, :created_at => nil)
72
+ ]
73
+ end
74
+
75
+ it "allows an alternative to created_at" do
76
+ @chronicle = Chronicle.new(@things, :date_attr => :updated_at)
77
+ @chronicle.values.flatten.size.should == @things.size
78
+ @chronicle.keys.last.should == 'one year ago'
79
+ @chronicle.keys.first.should == 'just now'
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'chronicle'
5
+ include Chronicle
6
+
7
+ require 'active_support/all' # to get methods like blank? and starts_with?
8
+
9
+ # include ActionView::Helpers
10
+ include ActiveSupport
11
+
12
+ RSpec.configure do |config|
13
+ # some (optional) config here
14
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chronicle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Zeke Sikelianos
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hoe
16
+ requirement: &70361455414560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70361455414560
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70361455413780 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70361455413780
36
+ - !ruby/object:Gem::Dependency
37
+ name: autotest
38
+ requirement: &70361455412920 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70361455412920
47
+ - !ruby/object:Gem::Dependency
48
+ name: chronic
49
+ requirement: &70361455411820 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70361455411820
58
+ - !ruby/object:Gem::Dependency
59
+ name: rails
60
+ requirement: &70361455410980 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70361455410980
69
+ description: Chronicle groups collections of ActiveRecord objects into chronologically
70
+ ordered hashes.
71
+ email:
72
+ - zeke@sikelianos.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - .rspec
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - chronicle.gemspec
83
+ - lib/chronicle.rb
84
+ - lib/chronicle/version.rb
85
+ - spec/chronicle_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: http://zeke.sikelianos.com
88
+ licenses: []
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project: chronicle
107
+ rubygems_version: 1.8.10
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Chronicle groups collections of ActiveRecord objects into chronologically
111
+ ordered hashes.
112
+ test_files: []