streakable 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2117453eb59848b55a11e27607df503b4a3e8c32
4
+ data.tar.gz: b88852d4a47c1f5b20290d0706e2f5284e108bf8
5
+ SHA512:
6
+ metadata.gz: 92cc3cdf95b76c1df41c4d49f469ec0703cda4d96cd62c373c390de98fc96d939666b13a11edce31fd5d7bf2ab078c044fa3bca8388bbf1f0c5a22471ca47e0b
7
+ data.tar.gz: 438bcda9fd55f5470cb9d973547fcfabdbc11a6f48d88fae8ad392f464bd8b0a1ac1f047357aaadcbf15fcc0b42013652de96624b12d3ab8c08dbbdd40d11ee1
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ cache: bundler
3
+ matrix:
4
+ fast_finish: true
5
+ branches:
6
+ only: master
7
+ rvm:
8
+ - 2.0.0
9
+ - 2.1.0
10
+ - 2.2.0
11
+ - 2.3.0
12
+ - 2.4.0
13
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in streakable.gemspec
4
+ gemspec
@@ -0,0 +1,46 @@
1
+ Copyright (c) 2018 szTheory
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+
25
+ Copyright (c) 2014 Garrett Martin
26
+
27
+ MIT License
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining
30
+ a copy of this software and associated documentation files (the
31
+ "Software"), to deal in the Software without restriction, including
32
+ without limitation the rights to use, copy, modify, merge, publish,
33
+ distribute, sublicense, and/or sell copies of the Software, and to
34
+ permit persons to whom the Software is furnished to do so, subject to
35
+ the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be
38
+ included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
43
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
44
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
45
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
46
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ [![Build Status](https://travis-ci.org/szTheory/streakable.svg?branch=master)](https://travis-ci.org/szTheory/streakable)
2
+
3
+ # Streakable
4
+
5
+ Track consecutive day streaks on your ActiveRecord models. Fork of [has_streak](https://github.com/garrettqmartin8/has_streak) by Garrett Martin. This version has a different include interface and more options.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'streakable'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install streakable
20
+
21
+ ## Usage
22
+
23
+ Let's say I have a <code>User</code> that <code>has_many</code> posts:
24
+
25
+ ```ruby
26
+ class User < ActiveRecord::Base
27
+ has_many :posts
28
+ end
29
+ ```
30
+
31
+ I want to track how many days in a row that each user wrote a post. I just have to add <code>streakable</code> to the model:
32
+
33
+ ```ruby
34
+ class User < ActiveRecord::Base
35
+ # ...
36
+
37
+ include Streakable
38
+ end
39
+ ```
40
+
41
+ Now I can display the user's streak:
42
+
43
+ ```ruby
44
+ user.streak(:posts) # => number of days in a row that this user wrote a post (as determined by the created_at column, by default)
45
+ ```
46
+
47
+ The <code>streak</code> instance method can be called with any association:
48
+
49
+ ```ruby
50
+ user.streak(:other_association)
51
+ ```
52
+
53
+ And you can change the column the streak is calculated on
54
+
55
+ ```ruby
56
+ user.streak(:posts, :updated_at)
57
+ ```
58
+
59
+ Don't penalize the current day being absent when determining streaks
60
+
61
+ ```ruby
62
+ user.streak(:posts, except_today: true)
63
+ ```
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,16 @@
1
+ require "streakable/streak"
2
+ require "active_record"
3
+
4
+ module Streakable
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ include InstanceMethods
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+ def streak(association, column=:created_at, except_today: false)
13
+ Streak.new(self, association, column, except_today).length
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,48 @@
1
+ class Streak
2
+ attr_reader :instance, :association, :column, :except_today
3
+ def initialize(instance, association, column, except_today)
4
+ @instance = instance
5
+ @association = association
6
+ @column = column
7
+ # Don't penalize the current day being absent when determining streaks
8
+ @except_today = except_today
9
+ end
10
+
11
+ def length
12
+ @length ||= begin
13
+ val = 0
14
+ streak_map.each do |map_bool|
15
+ break if !map_bool
16
+ val += 1
17
+ end
18
+
19
+ val
20
+ end
21
+ end
22
+
23
+ private
24
+ def days
25
+ @days ||= instance.send(association).order(column => :desc).pluck(column).map(&:to_date).uniq
26
+ end
27
+
28
+ def streak_map
29
+ @streak_map || begin
30
+ days.map.with_index do |day, i|
31
+ if i == 0
32
+ streak_day(day)
33
+ else
34
+ days[i-1] == day.tomorrow
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def streak_day(day)
41
+ if !except_today
42
+ day == Date.current
43
+ else
44
+ day == Date.current ||
45
+ day == Date.yesterday
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ require "streakable"
2
+ require "database_cleaner"
3
+ require "support/database_cleaner"
4
+ require "support/user"
5
+ require "support/post"
6
+ require "timecop"
7
+
8
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
9
+
10
+ ActiveRecord::Schema.define do
11
+ self.verbose = true
12
+
13
+ create_table :users, force: true do |t|
14
+ t.string :name
15
+ end
16
+
17
+ create_table :posts, force: true do |t|
18
+ t.string :content
19
+ t.integer :user_id
20
+ t.timestamps
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Streakable do
4
+ describe "#streak" do
5
+ subject { user.streak(:posts) }
6
+
7
+ let(:user) { User.create }
8
+
9
+ context "when a user has no posts" do
10
+ before { expect(user.posts).to be_empty }
11
+ it { is_expected.to be 0 }
12
+ end
13
+
14
+ let!(:posts) do
15
+ post_dates.each do |date|
16
+ user.posts.create(created_at: date)
17
+ end
18
+ end
19
+ let(:post_dates) { [] }
20
+
21
+ context "when a user posted on each of the last three days" do
22
+ let(:post_dates) { [2.days.ago, 1.day.ago, DateTime.current] }
23
+
24
+ it "returns the streak" do
25
+ expect(subject).to eq(posts.size)
26
+ end
27
+ end
28
+
29
+ context "when a user didn't post today" do
30
+ let(:post_dates) { [3.days.ago, 2.day.ago, 1.day.ago] }
31
+
32
+ it "returns streak of zero" do
33
+ expect(subject).to eq(0)
34
+ end
35
+
36
+ context "except today" do
37
+ subject { user.streak(:posts, except_today: true) }
38
+
39
+ it "returns the streak" do
40
+ expect(subject).to eq(post_dates.size)
41
+ end
42
+ end
43
+ end
44
+
45
+ context "spanning two months" do
46
+ around do |example|
47
+ Timecop.freeze(DateTime.current.beginning_of_month) do
48
+ example.run
49
+ end
50
+ end
51
+ let(:post_dates) { [1.day.ago, DateTime.current] }
52
+
53
+ it "returns a streak of 2" do
54
+ expect(subject).to eq(2)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ DatabaseCleaner.clean_with(:truncation)
4
+ end
5
+
6
+ config.before(:each) do
7
+ DatabaseCleaner.strategy = :transaction
8
+ end
9
+
10
+ config.before(:each) do
11
+ DatabaseCleaner.start
12
+ end
13
+
14
+ config.after(:each) do
15
+ DatabaseCleaner.clean
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ belongs_to :user
3
+ end
@@ -0,0 +1,5 @@
1
+ class User < ActiveRecord::Base
2
+ has_many :posts
3
+
4
+ include Streakable
5
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'streakable'
5
+ spec.version = '0.1.0'
6
+ spec.authors = ['szTheory', 'Garrett Martin']
7
+ spec.email = ['szTheory@github.com']
8
+ spec.description = %q{Track consecutive day streaks on ActiveRecord models.}
9
+ spec.summary = %q{Easily track consecutive day streaks on your Rails/ActiveRecord models.}
10
+ spec.homepage = 'https://github.com/szTheory/streakable'
11
+ spec.license = 'MIT'
12
+
13
+ spec.files = `git ls-files`.split($/)
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.add_runtime_dependency 'activerecord', '>= 3.0'
19
+
20
+ spec.add_development_dependency 'sqlite3', '~> 1.3', '>= 1.3.13'
21
+ spec.add_development_dependency 'rspec', '~> 3.8', '>= 3.8.0'
22
+ spec.add_development_dependency 'database_cleaner', '~> 1.7.0', '>= 1.7.0'
23
+ spec.add_development_dependency 'bundler', '~> 1.3', '>= 1.3'
24
+ spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.1'
25
+ spec.add_development_dependency 'pry', '~> 0.11', '>= 0.11.3'
26
+ spec.add_development_dependency 'timecop', '~> 0.9', '>= 0.9.1'
27
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: streakable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - szTheory
8
+ - Garrett Martin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-11-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '3.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '3.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 1.3.13
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: '1.3'
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.13
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.8'
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.8.0
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '3.8'
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 3.8.0
68
+ - !ruby/object:Gem::Dependency
69
+ name: database_cleaner
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 1.7.0
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 1.7.0
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: 1.7.0
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 1.7.0
88
+ - !ruby/object:Gem::Dependency
89
+ name: bundler
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '1.3'
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '1.3'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.3'
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '1.3'
108
+ - !ruby/object:Gem::Dependency
109
+ name: rake
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: '12.3'
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 12.3.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '12.3'
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: 12.3.1
128
+ - !ruby/object:Gem::Dependency
129
+ name: pry
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '0.11'
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 0.11.3
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.11'
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 0.11.3
148
+ - !ruby/object:Gem::Dependency
149
+ name: timecop
150
+ requirement: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: '0.9'
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 0.9.1
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.9'
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: 0.9.1
168
+ description: Track consecutive day streaks on ActiveRecord models.
169
+ email:
170
+ - szTheory@github.com
171
+ executables: []
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".gitignore"
176
+ - ".travis.yml"
177
+ - Gemfile
178
+ - LICENSE.txt
179
+ - README.md
180
+ - Rakefile
181
+ - lib/streakable.rb
182
+ - lib/streakable/streak.rb
183
+ - spec/spec_helper.rb
184
+ - spec/streakable_spec.rb
185
+ - spec/support/database_cleaner.rb
186
+ - spec/support/post.rb
187
+ - spec/support/user.rb
188
+ - streakable.gemspec
189
+ homepage: https://github.com/szTheory/streakable
190
+ licenses:
191
+ - MIT
192
+ metadata: {}
193
+ post_install_message:
194
+ rdoc_options: []
195
+ require_paths:
196
+ - lib
197
+ required_ruby_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ required_rubygems_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ requirements: []
208
+ rubyforge_project:
209
+ rubygems_version: 2.6.11
210
+ signing_key:
211
+ specification_version: 4
212
+ summary: Easily track consecutive day streaks on your Rails/ActiveRecord models.
213
+ test_files:
214
+ - spec/spec_helper.rb
215
+ - spec/streakable_spec.rb
216
+ - spec/support/database_cleaner.rb
217
+ - spec/support/post.rb
218
+ - spec/support/user.rb