chronicle 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore 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: []