archive_tree 1.0.0.rc
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/MIT-LICENSE +20 -0
- data/README.md +77 -0
- data/lib/archive_tree/action_view_extensions/draw_archive_tree.rb +68 -0
- data/lib/archive_tree/action_view_extensions.rb +8 -0
- data/lib/archive_tree/core.rb +153 -0
- data/lib/archive_tree.rb +37 -0
- metadata +114 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Diogo Almeida
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
3
|
+
ArchiveTree is a plugin for your Ruby on Rails application that makes it easy for you to generate structured trees of your records.
|
4
|
+
|
5
|
+
Since it makes use of your model's _created_at_ column, ArchiveTree is ultimately compatible with most ActiveRecord Models out there.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
You can install ArchiveTree can be installed as a Ruby gem. This is specially made easy with Bundler. Just add this to your Gemfile
|
11
|
+
|
12
|
+
<pre>
|
13
|
+
gem 'archive_tree'
|
14
|
+
</pre>
|
15
|
+
|
16
|
+
It's also possible to install ArchiveTree as a Rails plugin
|
17
|
+
|
18
|
+
<pre>
|
19
|
+
rails plugin install git://github.com/GnomesLab/archive_tree.git
|
20
|
+
</pre>
|
21
|
+
|
22
|
+
|
23
|
+
## Examples
|
24
|
+
|
25
|
+
Imagine that you have a blog application with Posts. Great!
|
26
|
+
|
27
|
+
Now let's say that you wish to allow your users to sweep through your posts in a chronologically accurate tree view. Enters ArchiveTree!
|
28
|
+
|
29
|
+
### API
|
30
|
+
|
31
|
+
#### Default usage:
|
32
|
+
|
33
|
+
<pre>
|
34
|
+
Post.archive_tree #=> { 2010 => { 1 => [Post], 2 => [Post] },
|
35
|
+
2011 => { 1 => [Post], 4 => [Post], 8 => [Post] } }
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
#### Sweep all months of the current year:
|
39
|
+
|
40
|
+
<pre>
|
41
|
+
Post.archive_tree(:years => [Time.now.year]) #=> { 2010 => { 1 => [Post], 2 => [Post] } }
|
42
|
+
</pre>
|
43
|
+
|
44
|
+
#### Skip all months other than January (1):
|
45
|
+
|
46
|
+
<pre>
|
47
|
+
Post.archive_tree(:months => [1]) #=> { 2010 => { 1 => [Post] },
|
48
|
+
{ 2011 => { 1 => [Post] } } }
|
49
|
+
</pre>
|
50
|
+
|
51
|
+
#### Only sweep January 2010:
|
52
|
+
|
53
|
+
<pre>
|
54
|
+
Post.archive_tree(:years_and_months => { 2010 => [1] }) #=> { 2010 => { 1 => [Post] } }
|
55
|
+
</pre>
|
56
|
+
|
57
|
+
### Views
|
58
|
+
|
59
|
+
TODO: add view examples
|
60
|
+
|
61
|
+
## Documentation
|
62
|
+
|
63
|
+
This gem's documentation documentation is available at http://yardoc.org/docs/GnomesLab-archive_tree
|
64
|
+
|
65
|
+
|
66
|
+
## License
|
67
|
+
|
68
|
+
Copyright (c) 2010 Diogo Almeida, released under the MIT license. For more information regarding MIT license, please check our [MIT license file](http://github.com/GnomesLab/archive_tree/blob/master/MIT-LICENSE)
|
69
|
+
|
70
|
+
|
71
|
+
## Feedback, issues and contributions
|
72
|
+
|
73
|
+
If you have an issue with ArchiveTree please create a ticket in our [issue tracker](http://gnomeslab.lighthouseapp.com/projects/57307-archive_tree/overview).
|
74
|
+
|
75
|
+
Feel free to fork this project at any time and submit your changes (along with their respective tests).
|
76
|
+
|
77
|
+
Should you just wish to provide feedback or say hi, you can always contact us directly through diogo (dot) almeida (at) gnomeslab (dot) com
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ArchiveTree
|
2
|
+
module ActionViewExtensions
|
3
|
+
|
4
|
+
# Defines the helper methods that will be included in the ActionView::Base in order to return the html representation of the archive tree
|
5
|
+
module DrawArchiveTree
|
6
|
+
|
7
|
+
# In the presence of records for a given model it draws the archive tree. Otherwise, returns an empty string
|
8
|
+
#
|
9
|
+
# This method relies on the following private methods:
|
10
|
+
# * +draw_years+
|
11
|
+
# * +draw_months+
|
12
|
+
#
|
13
|
+
# Default behavior
|
14
|
+
# * It will attempt to create a tree of your Post model
|
15
|
+
# * Will use the post_published_at_path route
|
16
|
+
# * Will display a toggle link
|
17
|
+
# * Will use "[ + ]" as the link text
|
18
|
+
#
|
19
|
+
# Example using the default settings:
|
20
|
+
# <%= draw_archive_tree %>
|
21
|
+
#
|
22
|
+
# Overriding the defaults example:
|
23
|
+
# <%= draw_archive_tree :archive, :archive_published_at_path %>
|
24
|
+
|
25
|
+
def draw_archive_tree(model_sym = :post, route = :posts_path, toggle = true, toggle_text = '[ + ]')
|
26
|
+
model = model_sym.to_s.capitalize.constantize
|
27
|
+
|
28
|
+
raw model.count > 0 ? draw_years(model_sym, route, toggle, toggle_text) : ""
|
29
|
+
end # draw_archive_tree
|
30
|
+
|
31
|
+
private
|
32
|
+
def draw_years(model_sym, route, toggle, toggle_text) # :nodoc:
|
33
|
+
model = model_sym.to_s.capitalize.constantize
|
34
|
+
route = :posts_path unless self.respond_to? route
|
35
|
+
|
36
|
+
content_tag :ul do
|
37
|
+
ul_body = ""
|
38
|
+
|
39
|
+
model.archived_years.each_key do |year, count|
|
40
|
+
ul_body << content_tag(:li, :class => year == Time.now.year ? 'active' : 'inactive') do
|
41
|
+
(toggle ? link_to(toggle_text, "#", :class => "toggle") : '') + " " +
|
42
|
+
link_to(year, self.send(route, year)) +
|
43
|
+
draw_months(model_sym, route, year)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ul_body
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def draw_months(model_sym, route, year) # :nodoc:
|
52
|
+
model = model_sym.to_s.capitalize.constantize
|
53
|
+
|
54
|
+
content_tag :ul do
|
55
|
+
ul_body = ""
|
56
|
+
|
57
|
+
model.archived_months(:year => year).each_pair do |month, count|
|
58
|
+
ul_body << content_tag(:li, link_to("#{Date::MONTHNAMES[month]} (#{count})",
|
59
|
+
self.send(route, year, month < 10 ? "0#{month}" : month)))
|
60
|
+
end
|
61
|
+
|
62
|
+
ul_body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # DrawArchiveTree
|
66
|
+
|
67
|
+
end # ActionViewExtensions
|
68
|
+
end # ArchiveTree
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module ArchiveTree
|
2
|
+
|
3
|
+
module Core #:nodoc:
|
4
|
+
|
5
|
+
# Named scope that retrieves the records for a given year and month.
|
6
|
+
#
|
7
|
+
# Note: This scope can be chained, e.g. Post.archive_node.where('id > 100')
|
8
|
+
#
|
9
|
+
# Default behaviors
|
10
|
+
# * :year #=> defaults to the current year (e.g. 2010)
|
11
|
+
# * :month #=> all
|
12
|
+
#
|
13
|
+
# Example using default values:
|
14
|
+
# Post.archive_node
|
15
|
+
#
|
16
|
+
# Example overridding values:
|
17
|
+
# Post.archive_node(:year => 2010, :month => 1)
|
18
|
+
def archive_node(options={})
|
19
|
+
options.reverse_merge! ({ :year => Time.now.year })
|
20
|
+
|
21
|
+
where("#{date_field} IS NOT NULL").
|
22
|
+
where("YEAR(#{date_field}) = ?", options[:year]).
|
23
|
+
where("MONTH(#{date_field}) = :month OR :month IS NULL", :month => options[:month])
|
24
|
+
end
|
25
|
+
|
26
|
+
# Constructs a single-level hash of years using the defined +date_field+ column.
|
27
|
+
#
|
28
|
+
# The returned hash is a key-value-pair of integers. The key represents the year (in integer) and the value
|
29
|
+
# represents the number of records for that year (also in integer).
|
30
|
+
#
|
31
|
+
# This method executes a SQL query of type COUNT, grouping the results by year.
|
32
|
+
#
|
33
|
+
# Note: the query makes use of the YEAR sql command, which might not be supported by all RDBMs.
|
34
|
+
#
|
35
|
+
# Exampe:
|
36
|
+
# Post.years_hash #=> { 2009 => 8, 2010 => 30 }
|
37
|
+
def archived_years
|
38
|
+
years = {}
|
39
|
+
where("#{date_field} IS NOT NULL").
|
40
|
+
group("YEAR(#{date_field})").size.each { |year, count| years[year.to_i] = count }
|
41
|
+
|
42
|
+
years
|
43
|
+
end # archived_years
|
44
|
+
|
45
|
+
# For a given year, constructs a single-level hash of months using the defined +date_field+ column.
|
46
|
+
#
|
47
|
+
# The returned hash is a key-value-pair representing the number of records for a given month, within a given years.
|
48
|
+
# This hash can have string or integer keys, depending on the value of :month_names option,
|
49
|
+
# which can be passed in the options hash.
|
50
|
+
#
|
51
|
+
# Default behaviors
|
52
|
+
# * By default the months are returned in their integer representation (eg.: 1 represents January)
|
53
|
+
# * The current year is assumed to be the default scope of the query
|
54
|
+
#
|
55
|
+
# Options
|
56
|
+
# * :year #=> Integer representing the year to sweep. Defaults to the current year.
|
57
|
+
# * :month_names #=> Null, or absent will result in months represented as integer (default). Also accepts :long
|
58
|
+
# and :short, depending on the desired lenght length for the month name.
|
59
|
+
#
|
60
|
+
# *Considerations*
|
61
|
+
# Given the way the queries are currently constructed and executed this method suffers from poor performance.
|
62
|
+
#
|
63
|
+
# TODO: Optimize the queries.
|
64
|
+
def archived_months(options = {})
|
65
|
+
months = {}
|
66
|
+
month_format = options.delete(:month_names) || :int
|
67
|
+
|
68
|
+
where("YEAR(#{date_field}) = #{options[:year] || Time.now.year}").
|
69
|
+
group("MONTH(#{date_field})").size.each do |month, c|
|
70
|
+
key = case month_format
|
71
|
+
when :long
|
72
|
+
Date::MONTHNAMES[month.to_i]
|
73
|
+
when :short
|
74
|
+
Date::ABBR_MONTHNAMES[month.to_i]
|
75
|
+
else
|
76
|
+
month.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
months[key] = c
|
80
|
+
end
|
81
|
+
|
82
|
+
months
|
83
|
+
end #archived_months
|
84
|
+
|
85
|
+
# Constructs an archive tree in the form of a nested Hash.
|
86
|
+
#
|
87
|
+
# Hash levels
|
88
|
+
# 1. Years (integer)
|
89
|
+
# 2. Months (integer)
|
90
|
+
# 3. Your records (ActiveRecord::Relation)
|
91
|
+
#
|
92
|
+
# Default behaviors to take note:
|
93
|
+
# * All records are sweeped by default based on their +date_field+ column
|
94
|
+
# * Years without records are not returned
|
95
|
+
# * Months without records are not returned
|
96
|
+
# * The keys are integers, thus they will likely require conversion
|
97
|
+
#
|
98
|
+
# Options
|
99
|
+
# * :years => Array of years to sweep
|
100
|
+
# * :months => Array of months to sweep of each year
|
101
|
+
# * :years_and_months => Hash of years, each containing an Array of months to sweep
|
102
|
+
# * :month_names => Accepts one of two symbols :long and :short. Please note that this overrides the default value
|
103
|
+
#
|
104
|
+
# Examples context
|
105
|
+
# Please note that for the sake of simplicity these examples assume that any given year has only one +Post+
|
106
|
+
# record for any given month and will be represented in the following format:
|
107
|
+
# { 2010 => {1 => [post]} }
|
108
|
+
#
|
109
|
+
# Default usage:
|
110
|
+
# Post.archive_tree #=> { 2010 => { 1 => [Post], 2 => [Post] },
|
111
|
+
# 2011 => { 1 => [Post], 4 => [Post], 8 => [Post] } }
|
112
|
+
#
|
113
|
+
# Sweep all months of the current year:
|
114
|
+
# Post.archive_tree(:years => [Time.now.year]) #=> { 2010 => { 1 => [Post], 2 => [Post] } }
|
115
|
+
#
|
116
|
+
# Skip all months other than January (1):
|
117
|
+
# Post.archive_tree(:months => [1]) #=> { 2010 => { 1 => [Post] },
|
118
|
+
# { 2011 => { 1 => [Post] } } }
|
119
|
+
#
|
120
|
+
# Only sweep January 2010:
|
121
|
+
# Post.archive_tree(:years_and_months => { 2010 => [1] }) #=> { 2010 => { 1 => [Post] } }
|
122
|
+
#
|
123
|
+
# *Considerations*
|
124
|
+
# This method has poor performance due to the 'linear' way in which the queries are being constructed and executed.
|
125
|
+
#
|
126
|
+
# TODO: Optimize the queries.
|
127
|
+
def archive_tree(options = {})
|
128
|
+
tree = {}
|
129
|
+
years = options[:years_and_months] ? options[:years_and_months].keys : nil || options[:years] ||
|
130
|
+
archived_years.keys || []
|
131
|
+
|
132
|
+
years.each do |year|
|
133
|
+
tree[year] = {}
|
134
|
+
months = archived_months(:year => year).keys
|
135
|
+
|
136
|
+
if options[:years_and_months]
|
137
|
+
months.reject! { |m| !options[:years_and_months][year].include?(m) }
|
138
|
+
elsif options[:months]
|
139
|
+
months.reject! { |m| !options[:months].include?(m) }
|
140
|
+
end
|
141
|
+
|
142
|
+
months.each do |month|
|
143
|
+
tree[year][month] = {}
|
144
|
+
tree[year][month] = archive_node year, month
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
tree
|
149
|
+
end # archive_tree
|
150
|
+
|
151
|
+
end # Core
|
152
|
+
|
153
|
+
end # ArchiveTree
|
data/lib/archive_tree.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# +ArchiveTree+ is responsible for the creation of hashes that cronologically represent your model
|
2
|
+
# based on a provided field
|
3
|
+
#
|
4
|
+
# If you wish to take advantage of its functionalities, please use the acts_as_archive method in your ActiveRecord Model.
|
5
|
+
#
|
6
|
+
# Examples
|
7
|
+
# class Post < ActiveRecord::Base
|
8
|
+
# acts_as_archive # uses +created_at+ by default
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# class Post < ActiveRecord::Base
|
12
|
+
# acts_as_archive :published_at # uses +published_at+ instead of +created_at+ (default)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Post.archive_tree(:years_and_months => { 2010 => [1] }) #=> { 2010 => { 1 => [Post] } }
|
16
|
+
#
|
17
|
+
# TODO: This module should undergo a query optimization. Furthermore, an ORM abstraction.
|
18
|
+
module ArchiveTree
|
19
|
+
|
20
|
+
require 'archive_tree/action_view_extensions'
|
21
|
+
autoload :Core, 'archive_tree/core'
|
22
|
+
|
23
|
+
def acts_as_archive(date_field = :created_at)
|
24
|
+
raise ::ArgumentError, "undefined parameter #{date_field.to_s}" unless column = columns_hash[date_field.to_s]
|
25
|
+
raise ::ArgumentError, "invalid parameter #{date_field.to_s}" unless column.type == :datetime
|
26
|
+
|
27
|
+
self.date_field = date_field # Stores the date column
|
28
|
+
|
29
|
+
extend Core
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
attr_accessor :date_field
|
34
|
+
|
35
|
+
end # ArchiveTree
|
36
|
+
|
37
|
+
ActiveRecord::Base.send :extend, ArchiveTree if defined?(ActiveRecord::Base)
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: archive_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- rc
|
10
|
+
version: 1.0.0.rc
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Diogo Almeida
|
14
|
+
- Miguel Teixeira
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2010-10-30 00:00:00 +01:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: activerecord
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 3.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: database_cleaner
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: ArchiveTree is a Ruby Gem that makes it easy for you to create beautiful chronological archive trees of your models. For instance, you can create a tree for your blog posts.
|
64
|
+
email:
|
65
|
+
- mail@gnomeslab.com
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
extra_rdoc_files: []
|
71
|
+
|
72
|
+
files:
|
73
|
+
- lib/archive_tree/action_view_extensions/draw_archive_tree.rb
|
74
|
+
- lib/archive_tree/action_view_extensions.rb
|
75
|
+
- lib/archive_tree/core.rb
|
76
|
+
- lib/archive_tree.rb
|
77
|
+
- MIT-LICENSE
|
78
|
+
- README.md
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: http://github.com/GnomesLab/archive_tree/
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 1
|
103
|
+
- 3
|
104
|
+
- 7
|
105
|
+
version: 1.3.7
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.3.7
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Creates chronological trees of your models based on their created_at column value.
|
113
|
+
test_files: []
|
114
|
+
|