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 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,8 @@
1
+ module ArchiveTree
2
+
3
+ module ActionViewExtensions
4
+ require 'archive_tree/action_view_extensions/draw_archive_tree'
5
+ ::ActionView::Base.send :include, DrawArchiveTree
6
+ end # ActionViewExtensions
7
+
8
+ 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
@@ -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
+