about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock357
-rw-r--r--app/assets/stylesheets/main/entries.scss54
-rw-r--r--app/assets/stylesheets/main/layout.scss11
-rw-r--r--app/controllers/application_controller.rb10
-rw-r--r--app/controllers/blogs_controller.rb11
-rw-r--r--app/controllers/streams_controller.rb19
-rw-r--r--app/helpers/application_helper.rb11
-rw-r--r--app/models/scrobble.rb2
-rw-r--r--app/models/stream.rb11
-rw-r--r--app/models/update.rb8
-rw-r--r--app/views/admin/streams/edit.html.haml2
-rw-r--r--app/views/admin/streams/index.html.haml4
-rw-r--r--app/views/admin/updates/edit.html.haml2
-rw-r--r--app/views/blogs/_blog.html.haml2
-rw-r--r--app/views/layouts/application.html.haml10
-rw-r--r--app/views/layouts/quotes.html.haml5
-rw-r--r--app/views/streams/_stream.html.haml8
-rw-r--r--app/views/streams/index.html.haml19
-rw-r--r--app/views/streams/show.html.haml2
-rw-r--r--app/views/updates/_update.html.haml7
-rw-r--r--config/credentials.yml.enc2
-rw-r--r--config/credentials/production.yml.enc2
-rw-r--r--config/deploy.rb2
-rw-r--r--config/mail.yml8
-rw-r--r--config/routes.rb1
-rw-r--r--config/schedule.rb4
-rw-r--r--db/migrate/20250512181245_add_latest_post_at_to_stream.rb21
-rw-r--r--db/migrate/20251122012500_create_scrobbles.rb12
-rw-r--r--db/schema.rb12
-rw-r--r--lib/tasks/tasks.rake21
-rw-r--r--public/robots.txt6
-rw-r--r--test/fixtures/scrobbles.yml13
-rw-r--r--test/models/scrobble_test.rb7
33 files changed, 471 insertions, 195 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 6b672bc..e2e85e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock
@@ -1,111 +1,118 @@
1GEM 1GEM
2 remote: https://rubygems.org/ 2 remote: https://rubygems.org/
3 specs: 3 specs:
4 actioncable (7.1.1) 4 actioncable (7.1.5.1)
5 actionpack (= 7.1.1) 5 actionpack (= 7.1.5.1)
6 activesupport (= 7.1.1) 6 activesupport (= 7.1.5.1)
7 nio4r (~> 2.0) 7 nio4r (~> 2.0)
8 websocket-driver (>= 0.6.1) 8 websocket-driver (>= 0.6.1)
9 zeitwerk (~> 2.6) 9 zeitwerk (~> 2.6)
10 actionmailbox (7.1.1) 10 actionmailbox (7.1.5.1)
11 actionpack (= 7.1.1) 11 actionpack (= 7.1.5.1)
12 activejob (= 7.1.1) 12 activejob (= 7.1.5.1)
13 activerecord (= 7.1.1) 13 activerecord (= 7.1.5.1)
14 activestorage (= 7.1.1) 14 activestorage (= 7.1.5.1)
15 activesupport (= 7.1.1) 15 activesupport (= 7.1.5.1)
16 mail (>= 2.7.1) 16 mail (>= 2.7.1)
17 net-imap 17 net-imap
18 net-pop 18 net-pop
19 net-smtp 19 net-smtp
20 actionmailer (7.1.1) 20 actionmailer (7.1.5.1)
21 actionpack (= 7.1.1) 21 actionpack (= 7.1.5.1)
22 actionview (= 7.1.1) 22 actionview (= 7.1.5.1)
23 activejob (= 7.1.1) 23 activejob (= 7.1.5.1)
24 activesupport (= 7.1.1) 24 activesupport (= 7.1.5.1)
25 mail (~> 2.5, >= 2.5.4) 25 mail (~> 2.5, >= 2.5.4)
26 net-imap 26 net-imap
27 net-pop 27 net-pop
28 net-smtp 28 net-smtp
29 rails-dom-testing (~> 2.2) 29 rails-dom-testing (~> 2.2)
30 actionpack (7.1.1) 30 actionpack (7.1.5.1)
31 actionview (= 7.1.1) 31 actionview (= 7.1.5.1)
32 activesupport (= 7.1.1) 32 activesupport (= 7.1.5.1)
33 nokogiri (>= 1.8.5) 33 nokogiri (>= 1.8.5)
34 racc
34 rack (>= 2.2.4) 35 rack (>= 2.2.4)
35 rack-session (>= 1.0.1) 36 rack-session (>= 1.0.1)
36 rack-test (>= 0.6.3) 37 rack-test (>= 0.6.3)
37 rails-dom-testing (~> 2.2) 38 rails-dom-testing (~> 2.2)
38 rails-html-sanitizer (~> 1.6) 39 rails-html-sanitizer (~> 1.6)
39 actiontext (7.1.1) 40 actiontext (7.1.5.1)
40 actionpack (= 7.1.1) 41 actionpack (= 7.1.5.1)
41 activerecord (= 7.1.1) 42 activerecord (= 7.1.5.1)
42 activestorage (= 7.1.1) 43 activestorage (= 7.1.5.1)
43 activesupport (= 7.1.1) 44 activesupport (= 7.1.5.1)
44 globalid (>= 0.6.0) 45 globalid (>= 0.6.0)
45 nokogiri (>= 1.8.5) 46 nokogiri (>= 1.8.5)
46 actionview (7.1.1) 47 actionview (7.1.5.1)
47 activesupport (= 7.1.1) 48 activesupport (= 7.1.5.1)
48 builder (~> 3.1) 49 builder (~> 3.1)
49 erubi (~> 1.11) 50 erubi (~> 1.11)
50 rails-dom-testing (~> 2.2) 51 rails-dom-testing (~> 2.2)
51 rails-html-sanitizer (~> 1.6) 52 rails-html-sanitizer (~> 1.6)
52 active_storage_validations (1.1.1) 53 active_storage_validations (2.0.3)
53 activejob (>= 5.2.0) 54 activejob (>= 6.1.4)
54 activemodel (>= 5.2.0) 55 activemodel (>= 6.1.4)
55 activestorage (>= 5.2.0) 56 activestorage (>= 6.1.4)
56 activesupport (>= 5.2.0) 57 activesupport (>= 6.1.4)
57 activejob (7.1.1) 58 marcel (>= 1.0.3)
58 activesupport (= 7.1.1) 59 activejob (7.1.5.1)
60 activesupport (= 7.1.5.1)
59 globalid (>= 0.3.6) 61 globalid (>= 0.3.6)
60 activemodel (7.1.1) 62 activemodel (7.1.5.1)
61 activesupport (= 7.1.1) 63 activesupport (= 7.1.5.1)
62 activerecord (7.1.1) 64 activerecord (7.1.5.1)
63 activemodel (= 7.1.1) 65 activemodel (= 7.1.5.1)
64 activesupport (= 7.1.1) 66 activesupport (= 7.1.5.1)
65 timeout (>= 0.4.0) 67 timeout (>= 0.4.0)
66 activestorage (7.1.1) 68 activestorage (7.1.5.1)
67 actionpack (= 7.1.1) 69 actionpack (= 7.1.5.1)
68 activejob (= 7.1.1) 70 activejob (= 7.1.5.1)
69 activerecord (= 7.1.1) 71 activerecord (= 7.1.5.1)
70 activesupport (= 7.1.1) 72 activesupport (= 7.1.5.1)
71 marcel (~> 1.0) 73 marcel (~> 1.0)
72 activesupport (7.1.1) 74 activesupport (7.1.5.1)
73 base64 75 base64
76 benchmark (>= 0.3)
74 bigdecimal 77 bigdecimal
75 concurrent-ruby (~> 1.0, >= 1.0.2) 78 concurrent-ruby (~> 1.0, >= 1.0.2)
76 connection_pool (>= 2.2.5) 79 connection_pool (>= 2.2.5)
77 drb 80 drb
78 i18n (>= 1.6, < 2) 81 i18n (>= 1.6, < 2)
82 logger (>= 1.4.2)
79 minitest (>= 5.1) 83 minitest (>= 5.1)
80 mutex_m 84 mutex_m
85 securerandom (>= 0.3)
81 tzinfo (~> 2.0) 86 tzinfo (~> 2.0)
82 acts-as-taggable-on (10.0.0) 87 acts-as-taggable-on (12.0.0)
83 activerecord (>= 6.1, < 7.2) 88 activerecord (>= 7.1, < 8.1)
84 addressable (2.8.5) 89 zeitwerk (>= 2.4, < 3.0)
85 public_suffix (>= 2.0.2, < 6.0) 90 addressable (2.8.7)
86 airbrussh (1.5.0) 91 public_suffix (>= 2.0.2, < 7.0)
92 airbrussh (1.5.3)
87 sshkit (>= 1.6.1, != 1.7.0) 93 sshkit (>= 1.6.1, != 1.7.0)
88 akismet (3.0.0) 94 akismet (3.0.0)
89 audited (5.4.0) 95 audited (5.8.0)
90 activerecord (>= 5.0, < 7.2) 96 activerecord (>= 5.2, < 8.2)
91 request_store (~> 1.2) 97 activesupport (>= 5.2, < 8.2)
92 base64 (0.2.0) 98 base64 (0.2.0)
93 bcrypt (3.1.19) 99 bcrypt (3.1.20)
94 bcrypt_pbkdf (1.1.1) 100 bcrypt_pbkdf (1.1.1)
95 bigdecimal (3.1.4) 101 benchmark (0.4.0)
102 bigdecimal (3.1.9)
96 bindex (0.8.1) 103 bindex (0.8.1)
97 builder (3.2.4) 104 builder (3.3.0)
98 byebug (11.1.3) 105 byebug (12.0.0)
99 capistrano (3.18.0) 106 capistrano (3.19.2)
100 airbrussh (>= 1.0.0) 107 airbrussh (>= 1.0.0)
101 i18n 108 i18n
102 rake (>= 10.0.0) 109 rake (>= 10.0.0)
103 sshkit (>= 1.9.0) 110 sshkit (>= 1.9.0)
104 capistrano-bundler (2.1.0) 111 capistrano-bundler (2.1.1)
105 capistrano (~> 3.1) 112 capistrano (~> 3.1)
106 capistrano-passenger (0.2.1) 113 capistrano-passenger (0.2.1)
107 capistrano (~> 3.0) 114 capistrano (~> 3.0)
108 capistrano-rails (1.6.3) 115 capistrano-rails (1.7.0)
109 capistrano (~> 3.1) 116 capistrano (~> 3.1)
110 capistrano-bundler (>= 1.1, < 3) 117 capistrano-bundler (>= 1.1, < 3)
111 capistrano-rvm (0.1.2) 118 capistrano-rvm (0.1.2)
@@ -127,35 +134,35 @@ GEM
127 coffee-script-source 134 coffee-script-source
128 execjs 135 execjs
129 coffee-script-source (1.12.2) 136 coffee-script-source (1.12.2)
130 concurrent-ruby (1.2.2) 137 concurrent-ruby (1.3.5)
131 connection_pool (2.4.1) 138 connection_pool (2.5.3)
132 crass (1.0.6) 139 crass (1.0.6)
133 date (3.3.4) 140 date (3.4.1)
134 devise (4.9.3) 141 devise (4.9.4)
135 bcrypt (~> 3.0) 142 bcrypt (~> 3.0)
136 orm_adapter (~> 0.1) 143 orm_adapter (~> 0.1)
137 railties (>= 4.1.0) 144 railties (>= 4.1.0)
138 responders 145 responders
139 warden (~> 1.2.3) 146 warden (~> 1.2.3)
140 domain_name (0.6.20240107) 147 domain_name (0.6.20240107)
141 drb (2.2.0) 148 drb (2.2.1)
142 ruby2_keywords 149 ed25519 (1.4.0)
143 ed25519 (1.3.0) 150 enumerize (2.8.1)
144 enumerize (2.7.0)
145 activesupport (>= 3.2) 151 activesupport (>= 3.2)
146 erubi (1.12.0) 152 erubi (1.13.1)
147 execjs (2.9.1) 153 execjs (2.10.0)
148 ffi (1.16.3) 154 ffi (1.17.2)
149 ffi-compiler (1.3.2) 155 ffi-compiler (1.3.2)
150 ffi (>= 1.15.5) 156 ffi (>= 1.15.5)
151 rake 157 rake
152 globalid (1.2.1) 158 globalid (1.2.1)
153 activesupport (>= 6.1) 159 activesupport (>= 6.1)
154 haml (6.2.3) 160 haml (6.3.0)
155 temple (>= 0.8.2) 161 temple (>= 0.8.2)
156 thor 162 thor
157 tilt 163 tilt
158 highline (2.1.0) 164 highline (3.1.2)
165 reline
159 http (5.2.0) 166 http (5.2.0)
160 addressable (~> 2.8) 167 addressable (~> 2.8)
161 base64 (~> 0.1) 168 base64 (~> 0.1)
@@ -165,40 +172,43 @@ GEM
165 http-cookie (1.0.8) 172 http-cookie (1.0.8)
166 domain_name (~> 0.5) 173 domain_name (~> 0.5)
167 http-form_data (2.3.0) 174 http-form_data (2.3.0)
168 i18n (1.14.1) 175 i18n (1.14.7)
169 concurrent-ruby (~> 1.0) 176 concurrent-ruby (~> 1.0)
170 image_processing (1.12.2) 177 image_processing (1.14.0)
171 mini_magick (>= 4.9.5, < 5) 178 mini_magick (>= 4.9.5, < 6)
172 ruby-vips (>= 2.0.17, < 3) 179 ruby-vips (>= 2.0.17, < 3)
173 indieweb-endpoints (8.0.0) 180 indieweb-endpoints (9.1.0)
174 http (~> 5.0) 181 http (~> 5.2)
175 link-header-parser (~> 5.0) 182 link-header-parser (~> 6.1)
176 nokogiri (>= 1.13) 183 nokogiri (>= 1.13)
177 io-console (0.6.0) 184 io-console (0.8.0)
178 irb (1.8.3) 185 irb (1.15.2)
179 rdoc 186 pp (>= 0.6.0)
180 reline (>= 0.3.8) 187 rdoc (>= 4.0.0)
181 jbuilder (2.11.5) 188 reline (>= 0.4.2)
189 jbuilder (2.13.0)
182 actionview (>= 5.0.0) 190 actionview (>= 5.0.0)
183 activesupport (>= 5.0.0) 191 activesupport (>= 5.0.0)
184 jquery-rails (4.6.0) 192 jquery-rails (4.6.0)
185 rails-dom-testing (>= 1, < 3) 193 rails-dom-testing (>= 1, < 3)
186 railties (>= 4.2.0) 194 railties (>= 4.2.0)
187 thor (>= 0.14, < 2.0) 195 thor (>= 0.14, < 2.0)
188 jquery-ui-rails (6.0.1) 196 jquery-ui-rails (8.0.0)
189 railties (>= 3.2.16) 197 railties (>= 3.2.16)
190 js-routes (2.2.7) 198 js-routes (2.3.5)
191 railties (>= 4) 199 railties (>= 5)
192 json (2.9.0) 200 sorbet-runtime
193 libv8-node (18.16.0.0) 201 json (2.12.0)
194 link-header-parser (5.1.1) 202 libv8-node (23.6.1.0)
203 link-header-parser (6.1.1)
195 listen (3.0.8) 204 listen (3.0.8)
196 rb-fsevent (~> 0.9, >= 0.9.4) 205 rb-fsevent (~> 0.9, >= 0.9.4)
197 rb-inotify (~> 0.9, >= 0.9.7) 206 rb-inotify (~> 0.9, >= 0.9.7)
198 llhttp-ffi (0.5.0) 207 llhttp-ffi (0.5.1)
199 ffi-compiler (~> 1.0) 208 ffi-compiler (~> 1.0)
200 rake (~> 13.0) 209 rake (~> 13.0)
201 loofah (2.21.4) 210 logger (1.7.0)
211 loofah (2.24.1)
202 crass (~> 1.0.2) 212 crass (~> 1.0.2)
203 nokogiri (>= 1.12.0) 213 nokogiri (>= 1.12.0)
204 mail (2.8.1) 214 mail (2.8.1)
@@ -206,120 +216,128 @@ GEM
206 net-imap 216 net-imap
207 net-pop 217 net-pop
208 net-smtp 218 net-smtp
209 marcel (1.0.2) 219 marcel (1.0.4)
210 meta-tags (2.19.0) 220 meta-tags (2.22.1)
211 actionpack (>= 3.2.0, < 7.2) 221 actionpack (>= 6.0.0, < 8.1)
212 microformats (4.5.0) 222 microformats (4.5.0)
213 json 223 json
214 nokogiri 224 nokogiri
215 mime-types (3.5.1) 225 mime-types (3.7.0)
216 mime-types-data (~> 3.2015) 226 logger
217 mime-types-data (3.2023.1003) 227 mime-types-data (~> 3.2025, >= 3.2025.0507)
228 mime-types-data (3.2025.0507)
218 mimemagic (0.3.10) 229 mimemagic (0.3.10)
219 nokogiri (~> 1) 230 nokogiri (~> 1)
220 rake 231 rake
221 mini_magick (4.12.0) 232 mini_magick (5.2.0)
233 benchmark
234 logger
222 mini_mime (1.1.5) 235 mini_mime (1.1.5)
223 mini_portile2 (2.8.5) 236 mini_portile2 (2.8.9)
224 mini_racer (0.8.0) 237 mini_racer (0.18.1)
225 libv8-node (~> 18.16.0.0) 238 libv8-node (~> 23.6.1.0)
226 minitest (5.20.0) 239 minitest (5.25.5)
227 mutex_m (0.2.0) 240 mutex_m (0.3.0)
228 mysql2 (0.5.5) 241 mysql2 (0.5.6)
229 net-imap (0.4.4) 242 net-imap (0.5.8)
230 date 243 date
231 net-protocol 244 net-protocol
232 net-pop (0.1.2) 245 net-pop (0.1.2)
233 net-protocol 246 net-protocol
234 net-protocol (0.2.2) 247 net-protocol (0.2.2)
235 timeout 248 timeout
236 net-scp (4.0.0) 249 net-scp (4.1.0)
237 net-ssh (>= 2.6.5, < 8.0.0) 250 net-ssh (>= 2.6.5, < 8.0.0)
238 net-smtp (0.4.0) 251 net-sftp (4.0.0)
252 net-ssh (>= 5.0.0, < 8.0.0)
253 net-smtp (0.5.1)
239 net-protocol 254 net-protocol
240 net-ssh (7.2.0) 255 net-ssh (7.3.0)
241 nio4r (2.5.9) 256 nio4r (2.7.4)
242 nokogiri (1.15.4) 257 nokogiri (1.18.8)
243 mini_portile2 (~> 2.8.2) 258 mini_portile2 (~> 2.8.2)
244 racc (~> 1.4) 259 racc (~> 1.4)
245 normalize-rails (8.0.1) 260 normalize-rails (8.0.1)
246 orm_adapter (0.5.0) 261 orm_adapter (0.5.0)
262 ostruct (0.6.1)
247 paperclip (6.1.0) 263 paperclip (6.1.0)
248 activemodel (>= 4.2.0) 264 activemodel (>= 4.2.0)
249 activesupport (>= 4.2.0) 265 activesupport (>= 4.2.0)
250 mime-types 266 mime-types
251 mimemagic (~> 0.3.0) 267 mimemagic (~> 0.3.0)
252 terrapin (~> 0.6.0) 268 terrapin (~> 0.6.0)
253 psych (5.1.1.1) 269 pp (0.6.2)
270 prettyprint
271 prettyprint (0.2.0)
272 psych (5.2.6)
273 date
254 stringio 274 stringio
255 public_suffix (5.0.3) 275 public_suffix (6.0.2)
256 racc (1.7.3) 276 racc (1.8.1)
257 rack (2.2.8) 277 rack (2.2.14)
258 rack-session (1.0.1) 278 rack-session (1.0.2)
259 rack (< 3) 279 rack (< 3)
260 rack-test (2.1.0) 280 rack-test (2.2.0)
261 rack (>= 1.3) 281 rack (>= 1.3)
262 rackup (1.0.0) 282 rackup (1.0.1)
263 rack (< 3) 283 rack (< 3)
264 webrick 284 webrick
265 rails (7.1.1) 285 rails (7.1.5.1)
266 actioncable (= 7.1.1) 286 actioncable (= 7.1.5.1)
267 actionmailbox (= 7.1.1) 287 actionmailbox (= 7.1.5.1)
268 actionmailer (= 7.1.1) 288 actionmailer (= 7.1.5.1)
269 actionpack (= 7.1.1) 289 actionpack (= 7.1.5.1)
270 actiontext (= 7.1.1) 290 actiontext (= 7.1.5.1)
271 actionview (= 7.1.1) 291 actionview (= 7.1.5.1)
272 activejob (= 7.1.1) 292 activejob (= 7.1.5.1)
273 activemodel (= 7.1.1) 293 activemodel (= 7.1.5.1)
274 activerecord (= 7.1.1) 294 activerecord (= 7.1.5.1)
275 activestorage (= 7.1.1) 295 activestorage (= 7.1.5.1)
276 activesupport (= 7.1.1) 296 activesupport (= 7.1.5.1)
277 bundler (>= 1.15.0) 297 bundler (>= 1.15.0)
278 railties (= 7.1.1) 298 railties (= 7.1.5.1)
279 rails-dom-testing (2.2.0) 299 rails-dom-testing (2.2.0)
280 activesupport (>= 5.0.0) 300 activesupport (>= 5.0.0)
281 minitest 301 minitest
282 nokogiri (>= 1.6) 302 nokogiri (>= 1.6)
283 rails-html-sanitizer (1.6.0) 303 rails-html-sanitizer (1.6.2)
284 loofah (~> 2.21) 304 loofah (~> 2.21)
285 nokogiri (~> 1.14) 305 nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
286 rails_autolink (1.1.8) 306 rails_autolink (1.1.8)
287 actionview (> 3.1) 307 actionview (> 3.1)
288 activesupport (> 3.1) 308 activesupport (> 3.1)
289 railties (> 3.1) 309 railties (> 3.1)
290 railties (7.1.1) 310 railties (7.1.5.1)
291 actionpack (= 7.1.1) 311 actionpack (= 7.1.5.1)
292 activesupport (= 7.1.1) 312 activesupport (= 7.1.5.1)
293 irb 313 irb
294 rackup (>= 1.0.0) 314 rackup (>= 1.0.0)
295 rake (>= 12.2) 315 rake (>= 12.2)
296 thor (~> 1.0, >= 1.2.2) 316 thor (~> 1.0, >= 1.2.2)
297 zeitwerk (~> 2.6) 317 zeitwerk (~> 2.6)
298 rake (13.1.0) 318 rake (13.2.1)
299 ransack (4.3.0) 319 ransack (4.3.0)
300 activerecord (>= 6.1.5) 320 activerecord (>= 6.1.5)
301 activesupport (>= 6.1.5) 321 activesupport (>= 6.1.5)
302 i18n 322 i18n
303 rb-fsevent (0.11.2) 323 rb-fsevent (0.11.2)
304 rb-inotify (0.10.1) 324 rb-inotify (0.11.1)
305 ffi (~> 1.0) 325 ffi (~> 1.0)
306 rdoc (6.6.0) 326 rdoc (6.13.1)
307 psych (>= 4.0.0) 327 psych (>= 4.0.0)
308 recaptcha (5.19.0) 328 recaptcha (5.19.0)
309 redcarpet (3.6.0) 329 redcarpet (3.6.1)
310 reline (0.3.9) 330 reline (0.6.1)
311 io-console (~> 0.5) 331 io-console (~> 0.5)
312 request_store (1.5.1)
313 rack (>= 1.4)
314 responders (3.1.1) 332 responders (3.1.1)
315 actionpack (>= 5.2) 333 actionpack (>= 5.2)
316 railties (>= 5.2) 334 railties (>= 5.2)
317 rexml (3.2.6) 335 rexml (3.4.1)
318 rouge (4.2.0) 336 rouge (4.5.2)
319 ruby-vips (2.2.0) 337 ruby-vips (2.2.3)
320 ffi (~> 1.12) 338 ffi (~> 1.12)
321 ruby2_keywords (0.0.5) 339 logger
322 rubyzip (2.3.2) 340 rubyzip (2.4.1)
323 sassc (2.4.0) 341 sassc (2.4.0)
324 ffi (~> 1.9) 342 ffi (~> 1.9)
325 sassc-rails (2.1.2) 343 sassc-rails (2.1.2)
@@ -328,10 +346,14 @@ GEM
328 sprockets (> 3.0) 346 sprockets (> 3.0)
329 sprockets-rails 347 sprockets-rails
330 tilt 348 tilt
331 selenium-webdriver (4.14.0) 349 securerandom (0.4.1)
350 selenium-webdriver (4.32.0)
351 base64 (~> 0.2)
352 logger (~> 1.4)
332 rexml (~> 3.2, >= 3.2.5) 353 rexml (~> 3.2, >= 3.2.5)
333 rubyzip (>= 1.2.2, < 3.0) 354 rubyzip (>= 1.2.2, < 3.0)
334 websocket (~> 1.0) 355 websocket (~> 1.0)
356 sorbet-runtime (0.5.12087)
335 spring (2.1.1) 357 spring (2.1.1)
336 spring-watcher-listen (2.0.1) 358 spring-watcher-listen (2.0.1)
337 listen (>= 2.7, < 4.0) 359 listen (>= 2.7, < 4.0)
@@ -339,24 +361,28 @@ GEM
339 sprockets (3.7.2) 361 sprockets (3.7.2)
340 concurrent-ruby (~> 1.0) 362 concurrent-ruby (~> 1.0)
341 rack (> 1, < 3) 363 rack (> 1, < 3)
342 sprockets-rails (3.4.2) 364 sprockets-rails (3.5.2)
343 actionpack (>= 5.2) 365 actionpack (>= 6.1)
344 activesupport (>= 5.2) 366 activesupport (>= 6.1)
345 sprockets (>= 3.0.0) 367 sprockets (>= 3.0.0)
346 sqlite3 (1.6.7) 368 sqlite3 (2.6.0)
347 mini_portile2 (~> 2.8.0) 369 mini_portile2 (~> 2.8.0)
348 sshkit (1.21.5) 370 sshkit (1.24.0)
371 base64
372 logger
349 net-scp (>= 1.1.2) 373 net-scp (>= 1.1.2)
374 net-sftp (>= 2.1.2)
350 net-ssh (>= 2.8.0) 375 net-ssh (>= 2.8.0)
351 stringio (3.0.8) 376 ostruct
377 stringio (3.1.7)
352 temple (0.10.3) 378 temple (0.10.3)
353 terrapin (0.6.0) 379 terrapin (0.6.0)
354 climate_control (>= 0.0.3, < 1.0) 380 climate_control (>= 0.0.3, < 1.0)
355 terser (1.1.19) 381 terser (1.1.20)
356 execjs (>= 0.3.0, < 3) 382 execjs (>= 0.3.0, < 3)
357 thor (1.3.0) 383 thor (1.3.2)
358 tilt (2.3.0) 384 tilt (2.6.0)
359 timeout (0.4.1) 385 timeout (0.4.3)
360 turbolinks (5.2.1) 386 turbolinks (5.2.1)
361 turbolinks-source (~> 5.2) 387 turbolinks-source (~> 5.2)
362 turbolinks-source (5.2.0) 388 turbolinks-source (5.2.0)
@@ -369,21 +395,22 @@ GEM
369 activemodel (>= 6.0.0) 395 activemodel (>= 6.0.0)
370 bindex (>= 0.4.0) 396 bindex (>= 0.4.0)
371 railties (>= 6.0.0) 397 railties (>= 6.0.0)
372 webmention (7.0.0) 398 webmention (8.0.0)
373 http (~> 5.0) 399 http (~> 5.2)
374 indieweb-endpoints (~> 8.0) 400 indieweb-endpoints (~> 9.0)
375 nokogiri (>= 1.13) 401 nokogiri (>= 1.16)
376 webrick (1.8.1) 402 webrick (1.9.1)
377 websocket (1.2.10) 403 websocket (1.2.11)
378 websocket-driver (0.7.6) 404 websocket-driver (0.7.7)
405 base64
379 websocket-extensions (>= 0.1.0) 406 websocket-extensions (>= 0.1.0)
380 websocket-extensions (0.1.5) 407 websocket-extensions (0.1.5)
381 whenever (1.0.0) 408 whenever (1.0.0)
382 chronic (>= 0.6.3) 409 chronic (>= 0.6.3)
383 will_paginate (4.0.0) 410 will_paginate (4.0.1)
384 xpath (3.2.0) 411 xpath (3.2.0)
385 nokogiri (~> 1.8) 412 nokogiri (~> 1.8)
386 zeitwerk (2.6.12) 413 zeitwerk (2.7.2)
387 414
388PLATFORMS 415PLATFORMS
389 ruby 416 ruby
diff --git a/app/assets/stylesheets/main/entries.scss b/app/assets/stylesheets/main/entries.scss index 4afd15d..d67ae67 100644 --- a/app/assets/stylesheets/main/entries.scss +++ b/app/assets/stylesheets/main/entries.scss
@@ -153,7 +153,6 @@
153 153
154 .update-posted { 154 .update-posted {
155 display: block; 155 display: block;
156 font-style: italic;
157 background-color: #EAADEA; 156 background-color: #EAADEA;
158 font-size: 16px; 157 font-size: 16px;
159 margin: .5em -20px; 158 margin: .5em -20px;
@@ -161,6 +160,16 @@
161 border-width: 1px 0 1px 0; 160 border-width: 1px 0 1px 0;
162 border-style: solid; 161 border-style: solid;
163 border-color: #DB70DB; 162 border-color: #DB70DB;
163
164 time {
165 font-style: italic;
166 }
167
168 .update-edit-link {
169 float: right;
170 color: blue;
171 font-weight: normal;
172 }
164 } 173 }
165 } 174 }
166} 175}
@@ -463,3 +472,46 @@
463.next-post { 472.next-post {
464 float: right; 473 float: right;
465} 474}
475
476#streams-index {
477 width: 100%;
478 border-collapse: collapse;
479
480 #streams-index-header-row {
481 th {
482 background-color: #bdbdbd;
483 padding-left: 0.5em;
484 border-top: 1px solid #848484;
485 border-bottom: 1px solid #848484;
486 }
487 }
488
489 th {
490 padding-bottom: 0.5em;
491 padding-top: 0.5em;
492 text-align: left;
493 }
494
495 td {
496 padding-top: 1em;
497 padding-left: 0.5em;
498
499 time {
500 font-size: .75em;
501 }
502
503 .stream-link {
504 font-weight: bold;
505 text-decoration: none;
506
507 &, &:visited {
508 color: #ee2c2c;
509 }
510
511 &:hover {
512 text-decoration: underline;
513 color: #9ea1ad;
514 }
515 }
516 }
517}
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss index ef31ada..d5fe7a7 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/main/layout.scss
@@ -228,3 +228,14 @@ h2.centered {
228 text-decoration: none; 228 text-decoration: none;
229 } 229 }
230} 230}
231
232#scrobble-box {
233 img {
234 margin: 0 auto;
235 display: block;
236 }
237
238 p {
239 text-align: center;
240 }
241}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0174cae..ad46fb9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb
@@ -1,9 +1,19 @@
1class ApplicationController < ActionController::Base 1class ApplicationController < ActionController::Base
2 protect_from_forgery with: :exception 2 protect_from_forgery with: :exception
3 before_action :choose_random_song
3 4
4 private 5 private
5 6
6 def after_sign_out_path_for(resource) 7 def after_sign_out_path_for(resource)
7 new_session_path(resource) 8 new_session_path(resource)
8 end 9 end
10
11 def choose_random_song
12 ids = Scrobble.ids
13 if ids.empty?
14 @random_song = nil
15 else
16 @random_song = Scrobble.find(ids.sample)
17 end
18 end
9end 19end
diff --git a/app/controllers/blogs_controller.rb b/app/controllers/blogs_controller.rb index 4f6d9ce..6e80754 100644 --- a/app/controllers/blogs_controller.rb +++ b/app/controllers/blogs_controller.rb
@@ -1,12 +1,7 @@
1require 'redcarpet/render_strip'
2 1
3class StrippedSummary < Redcarpet::Render::StripDown
4 def block_html(raw_html)
5 nil
6 end
7end
8 2
9class BlogsController < ApplicationController 3class BlogsController < ApplicationController
4 include ApplicationHelper
10 5
11 def summary 6 def summary
12 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10) 7 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10)
@@ -33,12 +28,12 @@ class BlogsController < ApplicationController
33 @prev = @blog.prev 28 @prev = @blog.prev
34 @next = @blog.next 29 @next = @blog.next
35 30
36 body = Redcarpet::Markdown.new(StrippedSummary).render(@blog.body) 31 body = stripped_markdown(@blog.body)
37 32
38 set_meta_tags(og: { 33 set_meta_tags(og: {
39 title: @blog.title, 34 title: @blog.title,
40 type: "article", 35 type: "article",
41 description: (body.length <= 300 ? body : body[0..299]), 36 description: body[0, 299],
42 url: blog_url(@blog, host: "www.fourisland.com"), 37 url: blog_url(@blog, host: "www.fourisland.com"),
43 article: { 38 article: {
44 published_time: @blog.published_at.iso8601, 39 published_time: @blog.published_at.iso8601,
diff --git a/app/controllers/streams_controller.rb b/app/controllers/streams_controller.rb index 664f533..ec4cee8 100644 --- a/app/controllers/streams_controller.rb +++ b/app/controllers/streams_controller.rb
@@ -1,7 +1,26 @@
1class StreamsController < ApplicationController 1class StreamsController < ApplicationController
2 include ApplicationHelper
3
4 def index
5 @streams = Stream.order(latest_post_at: :desc).paginate(page: params[:page], per_page: 10)
6 end
2 7
3 def show 8 def show
4 @stream = Stream.find_by_slug(params[:slug]) 9 @stream = Stream.find_by_slug(params[:slug])
10 @updates = @stream.updates.paginate(page: params[:page], per_page: 10)
11
12 body = stripped_markdown(@stream.body)
13
14 set_meta_tags(og: {
15 title: @stream.title,
16 type: "article",
17 description: body[0, 299],
18 url: stream_url(@stream, host: "www.fourisland.com"),
19 article: {
20 published_time: @stream.created_at.iso8601,
21 modified_time: @stream.latest_post_at.iso8601
22 }
23 })
5 end 24 end
6 25
7end 26end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 97a897f..a51f1a1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb
@@ -1,6 +1,7 @@
1require 'redcarpet' 1require 'redcarpet'
2require 'rouge' 2require 'rouge'
3require 'rouge/plugins/redcarpet' 3require 'rouge/plugins/redcarpet'
4require 'redcarpet/render_strip'
4 5
5module ApplicationHelper 6module ApplicationHelper
6 7
@@ -45,4 +46,14 @@ module ApplicationHelper
45 ] 46 ]
46 end 47 end
47 48
49 class StrippedSummary < Redcarpet::Render::StripDown
50 def block_html(raw_html)
51 nil
52 end
53 end
54
55 def stripped_markdown(text)
56 Redcarpet::Markdown.new(StrippedSummary).render(text)
57 end
58
48end 59end
diff --git a/app/models/scrobble.rb b/app/models/scrobble.rb new file mode 100644 index 0000000..f527612 --- /dev/null +++ b/app/models/scrobble.rb
@@ -0,0 +1,2 @@
1class Scrobble < ApplicationRecord
2end
diff --git a/app/models/stream.rb b/app/models/stream.rb index 0773143..6a738e2 100644 --- a/app/models/stream.rb +++ b/app/models/stream.rb
@@ -8,11 +8,22 @@ class Stream < ApplicationRecord
8 validates :title, presence: true 8 validates :title, presence: true
9 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/ 9 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/
10 10
11 before_create :set_post_timestamp
12
11 def path 13 def path
12 "/thinks/#{slug}" 14 "/thinks/#{slug}"
13 end 15 end
14 16
17 def to_param
18 slug
19 end
20
15 def taggable 21 def taggable
16 self 22 self
17 end 23 end
24
25 private
26 def set_post_timestamp
27 self.latest_post_at = self.created_at
28 end
18end 29end
diff --git a/app/models/update.rb b/app/models/update.rb index 01907d8..a98a5d4 100644 --- a/app/models/update.rb +++ b/app/models/update.rb
@@ -5,6 +5,8 @@ class Update < ApplicationRecord
5 5
6 validates :stream, :body, presence: true 6 validates :stream, :body, presence: true
7 7
8 after_create :set_latest_timestamp
9
8 def path 10 def path
9 "/thinks/#{stream.slug}\#update-#{id}" 11 "/thinks/#{stream.slug}\#update-#{id}"
10 end 12 end
@@ -12,4 +14,10 @@ class Update < ApplicationRecord
12 def taggable 14 def taggable
13 stream 15 stream
14 end 16 end
17
18 private
19 def set_latest_timestamp
20 self.stream.latest_post_at = self.created_at
21 self.stream.save!
22 end
15end 23end
diff --git a/app/views/admin/streams/edit.html.haml b/app/views/admin/streams/edit.html.haml index 8d910fe..8c250e2 100644 --- a/app/views/admin/streams/edit.html.haml +++ b/app/views/admin/streams/edit.html.haml
@@ -1,3 +1,3 @@
1- title "Editing #{@stream.title}" 1- title "Editing #{@stream.title}"
2= form_for @stream, url: admin_stream_url(@stream), html: { id: "entry-form" } do |f| 2= form_for @stream, url: admin_stream_url(@stream.id), html: { id: "entry-form" } do |f|
3 = render partial: "form", locals: { f: f } 3 = render partial: "form", locals: { f: f }
diff --git a/app/views/admin/streams/index.html.haml b/app/views/admin/streams/index.html.haml index 6903fd1..a2a25d7 100644 --- a/app/views/admin/streams/index.html.haml +++ b/app/views/admin/streams/index.html.haml
@@ -10,5 +10,5 @@
10 %td= stream.created_at.strftime("%B %d, %Y, %l:%M%P") 10 %td= stream.created_at.strftime("%B %d, %Y, %l:%M%P")
11 %td 11 %td
12 %ul.admin-actions 12 %ul.admin-actions
13 %li= link_to "Edit", edit_admin_stream_url(stream) 13 %li= link_to "Edit", edit_admin_stream_url(stream.id)
14 %li= link_to "Add Update", new_admin_stream_update_url(stream) 14 %li= link_to "Add Update", new_admin_stream_update_url(stream.id)
diff --git a/app/views/admin/updates/edit.html.haml b/app/views/admin/updates/edit.html.haml index 75a9957..3cab0b6 100644 --- a/app/views/admin/updates/edit.html.haml +++ b/app/views/admin/updates/edit.html.haml
@@ -1,3 +1,3 @@
1- title "Editing stream update" 1- title "Editing stream update"
2= form_for @update, url: admin_stream_update_url(@stream, @update), html: { id: "entry-form" } do |f| 2= form_for @update, url: admin_stream_update_url(@stream.id, @update), html: { id: "entry-form" } do |f|
3 = render partial: "form", locals: { f: f } 3 = render partial: "form", locals: { f: f }
diff --git a/app/views/blogs/_blog.html.haml b/app/views/blogs/_blog.html.haml index 878b1a2..34b343f 100644 --- a/app/views/blogs/_blog.html.haml +++ b/app/views/blogs/_blog.html.haml
@@ -24,5 +24,5 @@
24 %time.dt-published{ datetime: blog.visible_date.strftime("%Y-%m-%dT%H:%M:%SZ%z") }= blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P") 24 %time.dt-published{ datetime: blog.visible_date.strftime("%Y-%m-%dT%H:%M:%SZ%z") }= blog.visible_date.strftime("%B #{blog.visible_date.day.ordinalize}, %Y at %-I:%M:%S%P")
25 .post-vote{ id: "blog-vote-section-#{blog.id}" } 25 .post-vote{ id: "blog-vote-section-#{blog.id}" }
26 %span.vote-link{ id: "blog-upvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_upvoted?(request.remote_ip)), "👍", upvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-upvote-link", method: :post 26 %span.vote-link{ id: "blog-upvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_upvoted?(request.remote_ip)), "👍", upvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-upvote-link", method: :post
27 %span.post-rating{ id: "blog-rating-#{blog.id}" }= blog.upvotes - blog.downvotes 27 %span.post-rating{ id: "blog-rating-#{blog.id}" }= blog.upvotes
28 %span.vote-link{ id: "blog-downvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_downvoted?(request.remote_ip)), "👎", downvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-downvote-link", method: :post 28 %span.vote-link{ id: "blog-downvote-link-#{blog.id}" }= link_to_unless (not blog.published or blog.already_downvoted?(request.remote_ip)), "👎", downvote_blog_path(blog), remote: true, rel: "nofollow", class: "blog-downvote-link", method: :post
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1d0bea3..6bc7041 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml
@@ -73,6 +73,16 @@
73 %li 73 %li
74 = image_tag "feed.png" 74 = image_tag "feed.png"
75 = link_to "Atom feed", blogs_url(format: :atom) 75 = link_to "Atom feed", blogs_url(format: :atom)
76 - unless @random_song.nil?
77 .sidebar-module#scrobble-box
78 .bubble.rounded
79 %h2 Listening to
80 = image_tag @random_song.image, width: "174px"
81 %p
82 %strong= @random_song.title
83 by
84 = @random_song.artist
85 %p= @random_song.album
76 .sidebar-module 86 .sidebar-module
77 .bubble.rounded 87 .bubble.rounded
78 %h2 Meta 88 %h2 Meta
diff --git a/app/views/layouts/quotes.html.haml b/app/views/layouts/quotes.html.haml index 4a7681e..040fdfe 100644 --- a/app/views/layouts/quotes.html.haml +++ b/app/views/layouts/quotes.html.haml
@@ -21,11 +21,12 @@
21 %li= link_to_unless_current "Home", quotes_url 21 %li= link_to_unless_current "Home", quotes_url
22 %li= link_to_unless_current "Latest", latest_quotes_url 22 %li= link_to_unless_current "Latest", latest_quotes_url
23 %li= link_to_unless_current "Top", top_quotes_url 23 %li= link_to_unless_current "Top", top_quotes_url
24 %li= link_to "Random", random_quotes_url 24 %li{"data-turbolinks" => "false"}= link_to "Random", random_quotes_url
25 - if user_signed_in? 25 - if user_signed_in?
26 %li= link_to_unless_current "Submit", new_quote_url 26 %li= link_to_unless_current "Submit", new_quote_url
27 %li= link_to_unless_current "Tags", tags_quotes_url 27 %li= link_to_unless_current "Tags", tags_quotes_url
28 %li= link_to_unless_current "Stats", stats_quotes_url 28 %li= link_to_unless_current "Search", search_form_quotes_url
29 %li{"data-turbolinks" => "false"}= link_to_unless_current "Stats", stats_quotes_url
29 %li= link_to_unless_current "Feed", latest_quotes_url(:atom) 30 %li= link_to_unless_current "Feed", latest_quotes_url(:atom)
30 - if user_signed_in? 31 - if user_signed_in?
31 %li= link_to_unless_current "Admin", admin_url 32 %li= link_to_unless_current "Admin", admin_url
diff --git a/app/views/streams/_stream.html.haml b/app/views/streams/_stream.html.haml index 84a6478..97d4813 100644 --- a/app/views/streams/_stream.html.haml +++ b/app/views/streams/_stream.html.haml
@@ -1,6 +1,8 @@
1%article#stream-post 1%article#stream-post
2 %h2#stream-title= stream.title 2 %h2#stream-title= stream.title
3 - unless stream.body.blank? 3 - unless stream.body.blank?
4 %header#stream-intro.entry-content= stream.body.html_safe 4 %header#stream-intro.entry-content= markdown(stream.body)
5 - unless stream.updates.empty? 5 = will_paginate @updates
6 = render stream.updates 6 - unless @updates.empty?
7 = render @updates
8 = will_paginate @updates
diff --git a/app/views/streams/index.html.haml b/app/views/streams/index.html.haml new file mode 100644 index 0000000..bf5073f --- /dev/null +++ b/app/views/streams/index.html.haml
@@ -0,0 +1,19 @@
1%table#streams-index
2 %tr#streams-index-header-row
3 %th Stream Topic
4 %th Updates
5 %th Last Post
6 - @streams.each do |stream|
7 %tr
8 %td
9 = link_to stream.title, stream, class: "stream-link"
10 %br
11 %time= stream.created_at.strftime("%B #{stream.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
12 %td= stream.updates.size
13 %td
14 - unless stream.updates.empty?
15 - latest_post = stream.updates.order(created_at: :desc).first
16 = link_to (stripped_markdown(latest_post.body)[0, 30] + "..."), stream
17 %br
18 %time= latest_post.created_at.strftime("%B #{latest_post.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
19= will_paginate @streams
diff --git a/app/views/streams/show.html.haml b/app/views/streams/show.html.haml index 8365556..8d58f62 100644 --- a/app/views/streams/show.html.haml +++ b/app/views/streams/show.html.haml
@@ -1,3 +1,3 @@
1- title @stream.title 1- title @stream.title
2.breadcrumb= link_to "← Back to home page", root_path 2.breadcrumb= link_to "← Back to streams", streams_path
3= render @stream 3= render @stream
diff --git a/app/views/updates/_update.html.haml b/app/views/updates/_update.html.haml index 57f4158..ceb7d8f 100644 --- a/app/views/updates/_update.html.haml +++ b/app/views/updates/_update.html.haml
@@ -1,3 +1,6 @@
1%section.stream-update.entry-content{ id: "update-#{update.id}" } 1%section.stream-update.entry-content{ id: "update-#{update.id}" }
2 %time.update-posted= update.created_at.strftime("%B #{update.created_at.day.ordinalize}, %Y at %-I:%M:%S%P") 2 %header.update-posted
3 = update.body.html_safe 3 %time= update.created_at.strftime("%B #{update.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
4 - if user_signed_in? and !update.new_record?
5 = link_to "Edit", edit_admin_stream_update_path(update.stream.id, update), :class => "update-edit-link"
6 = markdown(update.body)
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index cd9cac5..e08912f 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc
@@ -1 +1 @@
TMbFARKxiuopoIWE/n0a8KMCh28dFjTGFpfXoXHUQmDlVhA40doz/aLWCYDE00djC70eM4BwXZmaDFC7tly1miU0OymvYijYKNb5Kv9bu2rihSprleyjnBrCF4qykWpDh8XfzXGfiDegSuZzw5RUGKkEU+EGJNIPTnPKAm2MSSnPStVFt1gGxGuYRd0pXCgFUW/USVYnbJEPv6DOxqh3HmKmneP29fcx1V+N/RccyzHsjL2S5lej5s8Q4oAy+SZ4+xLK9Pdh6nA3dZ82fNTLffqmnFxEovuW5n6Cjt0Ka2MZRvYg6sBNDvUVvQSw4u1BRXx7JOHcIWiKdykF/uHdrSwmIPepwd+zs/HCxrthdMSry+yrLwJpZ67JiMA/VKIMYzgLy8yk2JEblBXq3FGo44TWwumIkfTn4H/6UZNb4jGJ4dH+LAFiE7il2X3BbvBb4EkYB3xbu+zqvnohOzuVKkJtjILV6RBrEFzbCijwi3PBOoX8O9fwW8B0BduSp4AhFa+dyenOkD2HjtyEMttJAPuuw3neQKhkv/4SaPaugcD4vKONSwU3F/7nB7yMVQqofVLIc2a1tJaTjWIlNIwVssZS2HS5OZFTpv1XJpCyMcLO/EAe9poqiPieE8WwpdXntRs9O/QQqPWyt2ZdHD8RMKerZjbU1wV58h4rJmmAKUkTCuo=--Jo9AwrM/hr9nWWoS--iHpkNsZwBWB0lySmzkcLdA== \ No newline at end of file MTHDW1ZOa9otC2lb9iG3bKlhzM8WgEoJddmcsv22b7TEv+ZxlnjBvql1MGJjfo+tNl9dT4gqUOH41Q2WfRhwIokibD6cLMKl8z3VPLHdARR3HW4OzEJoLYXYB5ycLtSFmL3jq/TkMBkctRvPo7jeGGBwtEHvh1tBx6cGr/fEvzWMKYMUPiugaMN3OPExZ5VaD1sCHTMm7erwP06+8KTABwGfF6quthHctj/39RusmAWaObVsUwm70nqij+/qekJEZS3xYEuCDZlKqQvwC7Z+qJlmO8vj8CYNFq80bD9MiuEL0kiTVp22ZyMj2TY6YXy2jabkVx5c7FVytx/+kbaNdGtLYqvvuqDXodcai18OWUhR3r+tzPqcinggWmzZeJ0O54dE7Srpe3wGFH3xmgXHRa1jczmCDwlGRTqmjX7E6CBiP7u5NAKLeALvADWGocJwUi0r8r6E3/a8rpsaL97JCjL1GNCDlRSAapqHafzszDWN31yG5mLc9OUoE2RJjU7bgohrhoAPyHpetRxO7fBpZ0quNAh1awiTXVTcNUlDT+UPkQi1mvQo47G3CM9OG8hlgKtfkMMKsl9D92k/g7zIyJzLsPKqYnxHj+qcnU0939xJ0x4MnUPam1oq7OF2FtBgU/OCDxMuM//qSI2Q4+RRKRHs/NLdADxRgO3WJZ+oe2r9YBfTYTlbdOEKmCxSrQMzpB/1jq0jSTRYynn/GO0Z1Pjx0wVzH8vjVosXfh6/PaKslMdHbOM=--A5y7uJSVaL0539+f--FP9Oigql2/EdcYszZ6hj/A== \ No newline at end of file
diff --git a/config/credentials/production.yml.enc b/config/credentials/production.yml.enc index af415a7..6359cc9 100644 --- a/config/credentials/production.yml.enc +++ b/config/credentials/production.yml.enc
@@ -1 +1 @@
V//BL1LV+HIfxWVXgfAHG5TrUakYlbjN5V3gkDi9StysvSQ0rM3Opo2/Vt0KbU4FHhjiHoGAOZRPdf08j5k3zS3i4YroBfR3HwOj7snvq2mdBleL9D1UqkBLldjdaco1MnSLSfS2yJe8F3VhtVxesz/Sh4LXoZWemsWGJLu0GfRxPDwswTvdWBgyASnKUOlFyQNypgRTWGMqCe99Z5pw8co8JqZho29Dk6Tp7UJzoim8j5YU+tfgBZ4GF81c0eT8SvBJhHHbcTm/DPUMGxvUJAT9adjQnjPq0tferHziTGl54/ROQCXqrJHKLUOH5JRqh2lAkY3EK3SvTezuYSoV0MwsOxntxC54bQYznN5Dz6/I31UbWpydPGY8P+/Sjdh4QInD51VcDeuJ/QaDSeqJFxHUWDJ0Da55rwsZFr6jmpgbqcmunXLl2B+VDH7sSQOn71IEyNikq5l/9m2dYEdXZpWFsujkyVXzg4O5WFkLtmqoe4wudN7kk8nEZffd6xPtKNpl51Ml1BpSoNMNwGAf72Xxq5/5Z9QFK7mtyPhBVYqalR0kEZPbxmeZGmedcE/fgQXlZYNw4tzBy2/24hJokMLRoX5prmOGMf+6f62wEbBozyaDU2eAMC0HCwg/rb22hDPvzs1XKu2TTEGK7iT3BvdsUHIVnHdUd//hLEeNHeI43FLxPKgyaG7Ovnn8DmNrvUuapaFHjykVsQ2GOLQ9a7GGrwObqr0w33HG/UAwhzEE8s9KwF6qMdqPCCPuYOsbj5xgTyv5X5s0oIgCinvkfAIyFV7WXwLbbE9yxc6UumXbLLTSQE2s8mZ2yL+gdE+iYkSOhz8Z3bj9agOQD5zHMu/NxzTdpNwH6rsLLXq5EfToIWNXEl9kTU8=--x7Ic40D296IOMLm6--VBDaN0LLlyO8zeyfWW+UBA== \ No newline at end of file uzTsEcFmu5QyOod1jjNQT8pqdQ9Tq8+OuH5qOulURR+Ew3+DBAN9/kYZ0b5OBcxnNGlN3suMC6fpgev4fYoaucCufXgNVAhhl1Lch9SJ2jo/1jCIS1U6mTPmwbF1aWM/P7jJhw8535RdlzohVStrIO0Rn3ygshVFFPODSccqyIhDbiiK5j0bvErzt5fDN091SIB0pdtxzy/XBwwfcQ6EaTg170Pv1mmIyCSWD1AGyzGlRhFVYhyv3sEabsC1dfYXjyZYrpbhvfZbi46TzeNA4kPLIVfVNiW0QoJtT90UqpOLcnQKwf23WIxPemzsT9iQj0/pEci+umHG6MkH2zE1eBOlj2VdopBqVEwQEgDvqt6zQMKAbLPU1E3VM94gCxeTidwIMoFGhSXl6nlGwI9ozBXvKyFNgMiz9UKtjEXcQOG3cIBxxMEQ+sjGEu3vAMvD7OymSGSC81JCbcdOqHWH/iIh2K3IcGyibJ4ScVsUBMn7lnsMEPrQQEJ/Df0X2mplJc0i8n19H1S7SrLKSaO7FDENPC0d1dN5nnOt5nDGU2/qkiGJ+LepX/Gb3cxc0fpcnC359rB5WCTKsxBOzjqW5IapCRSa0ztZz+xeXUBN4sDRUzPun2yCqaCxe+ptzTxzT+Ej9oMKSJJncc/zUuwRmd6KbnyQ3/ISeNNIC0BLWaohaj3lP/tRkLkcBNqu/MA4pxUxs1Lwu/8gW9d4GrrxCxOSsQnDk5VzF80LCk4oq+BVrjPYY0vWzVxtj+56slIoUIBIUWRjFTbBuyr3gejPjis5NwiFMyi8emH+a9aa3Ge44YnrDSAA+JVzqHSAF8erIuiqgaTAFwprSmED8pIIV2A0/WWrPcuI5xaAv4e2bQWfzM3SujvY--rs7RvQb911E+dh50--fCsEmvYD6LcaB6ReBcHGrA== \ No newline at end of file
diff --git a/config/deploy.rb b/config/deploy.rb index f28894f..de297e3 100644 --- a/config/deploy.rb +++ b/config/deploy.rb
@@ -1,5 +1,5 @@
1# config valid only for current version of Capistrano 1# config valid only for current version of Capistrano
2lock "3.18.0" 2lock "3.19.2"
3 3
4set :application, "thoughts" 4set :application, "thoughts"
5set :repo_url, "/srv/git/thoughts" 5set :repo_url, "/srv/git/thoughts"
diff --git a/config/mail.yml b/config/mail.yml index e2d15c3..6c0435d 100644 --- a/config/mail.yml +++ b/config/mail.yml
@@ -1,8 +1,8 @@
1production: 1production:
2 smtp_settings: 2 smtp_settings:
3 address: "smtp.sendgrid.com" 3 address: "mail.smtp2go.com"
4 port: 587 4 port: 2525
5 user_name: "apikey" 5 user_name: "fourisland.com"
6 password: <%= Rails.application.credentials.sendgrid_api_key %> 6 password: <%= Rails.application.credentials.smtp2go_password %>
7 authentication: "plain" 7 authentication: "plain"
8 openssl_verify_mode: "none" 8 openssl_verify_mode: "none"
diff --git a/config/routes.rb b/config/routes.rb index 9e62ebb..ad66501 100644 --- a/config/routes.rb +++ b/config/routes.rb
@@ -56,6 +56,7 @@ Rails.application.routes.draw do
56 end 56 end
57 end 57 end
58 58
59 get 'thinks', to: 'streams#index', as: :streams
59 get 'thinks/:slug', to: 'streams#show', as: :stream 60 get 'thinks/:slug', to: 'streams#show', as: :stream
60 61
61 get 'plays', to: 'games#index' 62 get 'plays', to: 'games#index'
diff --git a/config/schedule.rb b/config/schedule.rb index e53ebf4..268dfcb 100644 --- a/config/schedule.rb +++ b/config/schedule.rb
@@ -21,3 +21,7 @@
21every 1.day, at: "6:00am" do 21every 1.day, at: "6:00am" do
22 rake "thoughts:email_upvote_report" 22 rake "thoughts:email_upvote_report"
23end 23end
24
25every 6.hours do
26 rake "thoughts:refresh_scrobbles"
27end
diff --git a/db/migrate/20250512181245_add_latest_post_at_to_stream.rb b/db/migrate/20250512181245_add_latest_post_at_to_stream.rb new file mode 100644 index 0000000..9d753e3 --- /dev/null +++ b/db/migrate/20250512181245_add_latest_post_at_to_stream.rb
@@ -0,0 +1,21 @@
1class AddLatestPostAtToStream < ActiveRecord::Migration[7.1]
2 def up
3 add_column :streams, :latest_post_at, :datetime
4
5 Stream.all.each do |stream|
6 if stream.updates.empty?
7 stream.latest_post_at = stream.created_at
8 else
9 stream.latest_post_at = stream.updates.order(created_at: :desc).first.created_at
10 end
11
12 stream.save!
13 end
14
15 change_column_null :streams, :latest_post_at, false
16 end
17
18 def down
19 remove_column :streams, :latest_post_at, :datetime
20 end
21end
diff --git a/db/migrate/20251122012500_create_scrobbles.rb b/db/migrate/20251122012500_create_scrobbles.rb new file mode 100644 index 0000000..9f00435 --- /dev/null +++ b/db/migrate/20251122012500_create_scrobbles.rb
@@ -0,0 +1,12 @@
1class CreateScrobbles < ActiveRecord::Migration[7.1]
2 def change
3 create_table :scrobbles do |t|
4 t.string :title
5 t.string :artist
6 t.string :album
7 t.string :image
8
9 t.timestamps
10 end
11 end
12end
diff --git a/db/schema.rb b/db/schema.rb index 795a986..74e1976 100644 --- a/db/schema.rb +++ b/db/schema.rb
@@ -10,7 +10,7 @@
10# 10#
11# It's strongly recommended that you check this file into your version control system. 11# It's strongly recommended that you check this file into your version control system.
12 12
13ActiveRecord::Schema[7.1].define(version: 2025_01_12_025207) do 13ActiveRecord::Schema[7.1].define(version: 2025_11_22_012500) do
14 create_table "active_storage_attachments", force: :cascade do |t| 14 create_table "active_storage_attachments", force: :cascade do |t|
15 t.string "name", null: false 15 t.string "name", null: false
16 t.string "record_type", null: false 16 t.string "record_type", null: false
@@ -352,12 +352,22 @@ ActiveRecord::Schema[7.1].define(version: 2025_01_12_025207) do
352 t.index ["recordable_type", "recordable_id"], name: "index_records_on_recordable_type_and_recordable_id" 352 t.index ["recordable_type", "recordable_id"], name: "index_records_on_recordable_type_and_recordable_id"
353 end 353 end
354 354
355 create_table "scrobbles", force: :cascade do |t|
356 t.string "title"
357 t.string "artist"
358 t.string "album"
359 t.string "image"
360 t.datetime "created_at", null: false
361 t.datetime "updated_at", null: false
362 end
363
355 create_table "streams", force: :cascade do |t| 364 create_table "streams", force: :cascade do |t|
356 t.string "title" 365 t.string "title"
357 t.text "body" 366 t.text "body"
358 t.string "slug" 367 t.string "slug"
359 t.datetime "created_at", precision: nil, null: false 368 t.datetime "created_at", precision: nil, null: false
360 t.datetime "updated_at", precision: nil, null: false 369 t.datetime "updated_at", precision: nil, null: false
370 t.datetime "latest_post_at", null: false
361 end 371 end
362 372
363 create_table "taggings", force: :cascade do |t| 373 create_table "taggings", force: :cascade do |t|
diff --git a/lib/tasks/tasks.rake b/lib/tasks/tasks.rake index fee22f4..91e196f 100644 --- a/lib/tasks/tasks.rake +++ b/lib/tasks/tasks.rake
@@ -1,3 +1,5 @@
1require 'net/http'
2
1namespace :thoughts do 3namespace :thoughts do
2 desc "Email a review of the last day's upvotes" 4 desc "Email a review of the last day's upvotes"
3 task :email_upvote_report => :environment do 5 task :email_upvote_report => :environment do
@@ -7,4 +9,23 @@ namespace :thoughts do
7 VoteMailer.with(votes: votes).daily_report_email.deliver 9 VoteMailer.with(votes: votes).daily_report_email.deliver
8 end 10 end
9 end 11 end
12
13 desc "Refresh the recently listened tracks"
14 task :refresh_scrobbles => :environment do
15 Scrobble.destroy_all
16
17 api_key = Rails.application.credentials.lastfm_api_key
18 url = URI.parse("http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=fefferburbia&api_key=#{api_key}&format=json")
19 req = Net::HTTP::Get.new(url.to_s)
20 res = Net::HTTP.start(url.host, url.port) {|http|
21 http.request(req)
22 }
23 output = JSON.parse(res.body)
24
25 items = output["recenttracks"]["track"].map {|p| [p["name"], p["artist"]["#text"], p["album"]["#text"], p["image"][2]["#text"]]}.sort.uniq
26
27 items.each do |item|
28 Scrobble.create!(title: item[0], artist: item[1], album: item[2], image: item[3])
29 end
30 end
10end 31end
diff --git a/public/robots.txt b/public/robots.txt index 37b576a..d7e6b45 100644 --- a/public/robots.txt +++ b/public/robots.txt
@@ -1 +1,7 @@
1# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 1# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2User-agent: PetalBot
3Disallow: /
4
5User-agent: AI2Bot
6Disallow: /
7
diff --git a/test/fixtures/scrobbles.yml b/test/fixtures/scrobbles.yml new file mode 100644 index 0000000..9ca535d --- /dev/null +++ b/test/fixtures/scrobbles.yml
@@ -0,0 +1,13 @@
1# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
3one:
4 title: MyString
5 artist: MyString
6 album: MyString
7 image: MyString
8
9two:
10 title: MyString
11 artist: MyString
12 album: MyString
13 image: MyString
diff --git a/test/models/scrobble_test.rb b/test/models/scrobble_test.rb new file mode 100644 index 0000000..48541da --- /dev/null +++ b/test/models/scrobble_test.rb
@@ -0,0 +1,7 @@
1require "test_helper"
2
3class ScrobbleTest < ActiveSupport::TestCase
4 # test "the truth" do
5 # assert true
6 # end
7end