archive_tree 1.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|