newscast 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ecb966380bc32d55480f8d0415c071a03578381
4
+ data.tar.gz: 20c3742e4bd41f188a323a21f23605613610a477
5
+ SHA512:
6
+ metadata.gz: 814d7a15c7abd29cb545602f008aad5ddaf282548255e703f78ac9578e15ae64a982babbbf38492e4c846c0af0f33916489e72cadb3213d9282ad0ce6c831820
7
+ data.tar.gz: 6fec147ef395ad06ae6cbb671c06e48d4e0049544304b3454c48b63f4b99d25df06309a387feba326201d879e58da84b052106bdcfa22a182209a3ee18c2f6a8
@@ -0,0 +1,4 @@
1
+
2
+ ### 0.0.1 / 2020-02-04
3
+
4
+ * Everything is new. First release
@@ -0,0 +1,8 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/newscast.rb
6
+ lib/newscast/version.rb
7
+ test/helper.rb
8
+ test/test_queries.rb
@@ -0,0 +1,249 @@
1
+ # newscast gem - newsreader for easy (re)use - build your own facebook newsfeed in 1-2-3 steps in 5 minutes
2
+
3
+
4
+ * home :: [github.com/feedreader/pluto](https://github.com/feedreader/pluto)
5
+ * bugs :: [github.com/feedreader/pluto/issues](https://github.com/feedreader/pluto/issues)
6
+ * gem :: [rubygems.org/gems/newscast](https://rubygems.org/gems/newscast)
7
+ * rdoc :: [rubydoc.info/gems/newscast](http://rubydoc.info/gems/newscast)
8
+ * forum :: [groups.google.com/group/wwwmake](http://groups.google.com/group/wwwmake)
9
+
10
+
11
+
12
+ ## Usage
13
+
14
+ The `News` module offers easy to (re)use "porcelain" helpers / methods
15
+ that hide the "plumbing" for building your own newsreader or newsfeeds in minutes.
16
+
17
+
18
+ ### Step 1 - Subscribe
19
+
20
+ Use the `News.subscribe( *feeds )` method to subscribe to news feeds / channels.
21
+ Example:
22
+
23
+ ```ruby
24
+ News.subscribe(
25
+ 'http://www.ruby-lang.org/en/feeds/news.rss', # Ruby Lang News
26
+ 'http://www.jruby.org/atom.xml', # JRuby Lang News
27
+ 'http://blog.rubygems.org/atom.xml', # RubyGems News
28
+ 'http://bundler.io/blog/feed.xml', # Bundler News
29
+ 'http://weblog.rubyonrails.org/feed/atom.xml', # Ruby on Rails News
30
+ 'http://sinatrarb.com/feed.xml', # Sinatra News
31
+ 'https://hanamirb.org/atom.xml', # Hanami News
32
+ 'http://jekyllrb.com/feed.xml', # Jekyll News
33
+ 'http://feeds.feedburner.com/jetbrains_rubymine?format=xml', # RubyMine IDE News
34
+ 'https://blog.phusion.nl/rss/', # Phusion News
35
+ 'https://rubyinstaller.org/feed.xml', # Ruby Installer for Windows News
36
+ 'http://planetruby.github.io/calendar/feed.xml', # Ruby Conferences & Camps News
37
+ 'https://rubytogether.org/news.xml', # Ruby Together News
38
+ 'https://foundation.travis-ci.org/feed.xml', # Travis Foundation News
39
+ 'https://railsgirlssummerofcode.org/blog.xml', # Rails Girls Summer of Code News
40
+
41
+ 'http://blog.zenspider.com/atom.xml', # Ryan Davis @ Seattle › Washington › United States
42
+ 'http://tenderlovemaking.com/atom.xml', # Aaron Patterson @ Seattle › Washington › United States
43
+ 'http://blog.headius.com/feed.xml', # Charles Nutter @ Richfield › Minnesota › United States
44
+ 'http://www.schneems.com/feed.xml', # Richard Schneeman @ Austin › Texas › United States
45
+ 'https://eregon.me/blog/feed.xml', # Benoit Daloze @ Zurich › Switzerland
46
+ 'http://samsaffron.com/posts.rss', # Sam Saffron @ Sydney › Australia
47
+ )
48
+ ```
49
+
50
+
51
+ Note: If you call `News.subscribe` the method will setup a single-file SQLite database,
52
+ that is, `./news.db` and auto-migrate the schema, that is, database tables and so on.
53
+
54
+
55
+ ### Step 2 - Update
56
+
57
+ Use the `News.update` method to fetch from the internets all news feeds / channels
58
+ and update the `./news.db` database. Example:
59
+
60
+ ```ruby
61
+ News.update
62
+ ```
63
+
64
+
65
+
66
+ ### Step 3 - Query and Remix the News Items
67
+
68
+ Profit! Now you can query the `./news.db` database with any of the many
69
+ pre-made / ready-to-use query helpers. Example:
70
+
71
+ ``` ruby
72
+ puts ":::::::::::::::::::::::::::::::::::::::::::::::::::"
73
+ puts ":: #{News.items.count} news items from #{News.channels.count} channels:"
74
+ puts
75
+ ```
76
+
77
+ resulting in:
78
+
79
+ ```
80
+ :::::::::::::::::::::::::::::::::::::::::::::::::::
81
+ :: 793 news items from 21 channels:
82
+ ```
83
+
84
+ and
85
+
86
+ ``` ruby
87
+ puts "100 Latest News Items"
88
+ News.latest.limit( 100 ).each do |item|
89
+ print "%4dd " % (Date.today.jd-item.updated.to_date.jd)
90
+ print " #{item.updated}"
91
+ print " - #{item.title}"
92
+ print " - #{URI(item.feed.feed_url).host}"
93
+ print "\n"
94
+ end
95
+ ```
96
+
97
+ resulting in:
98
+
99
+ ```
100
+ 100 Latest News Items
101
+ 0d 2020-02-04 00:00:00 - RubyNess @ Inverness, Scotland, United Kingdom - planetruby.github.io
102
+ 0d 2020-02-04 00:00:00 - Ruby by the Bay (Ruby for Good, West Coast Edition) @ Marin Headlands (near San Francisco), California, United States - planetruby.github.io
103
+ 8d 2020-01-27 14:44:38 - RubyMine 2020.1 EAP is Open! - jetbrains.com
104
+ 11d 2020-01-24 00:00:00 - Crowdfunding for 2020 scholarships has commenced - railsgirlssummerofcode.org
105
+ 15d 2020-01-20 00:00:00 - Alumni Interview with Keziah Naggita - railsgirlssummerofcode.org
106
+ 16d 2020-01-19 00:00:00 - This week in Rails - Rack 2.1 released, disallowed deprecations, and more! - weblog.rubyonrails.org
107
+ 19d 2020-01-16 00:00:00 - RubyConf Belarus (BY) @ Minsk, Belarus - planetruby.github.io
108
+ 22d 2020-01-13 14:00:39 - Guide to String Encoding in Ruby - tenderlovemaking.com
109
+ 23d 2020-01-12 23:00:00 - A Migration Path to Bundler 2+ - eregon.me
110
+ 24d 2020-01-11 00:00:00 - This week in Rails - Deprecations, bugfixes and improvements! - weblog.rubyonrails.org
111
+ 30d 2020-01-05 00:00:00 - This week in Rails - The 2019 edition - weblog.rubyonrails.org
112
+ 30d 2020-01-05 00:00:00 - RubyInstaller 2.7.0-1 released - rubyinstaller.org
113
+ 32d 2020-01-03 00:00:00 - Submit Your Open Source Projects - railsgirlssummerofcode.org
114
+ 34d 2020-01-01 00:00:00 - Happy new year & Sinatra 2.0.8! - sinatrarb.com
115
+ 39d 2019-12-27 00:00:00 - Ruby 2.7.0, Rails 6.0.2.1 and more - weblog.rubyonrails.org
116
+ 41d 2019-12-25 00:00:00 - Ruby 2.7.0 Released - ruby-lang.org
117
+ ...
118
+ ```
119
+
120
+ More queries include:
121
+
122
+ ``` ruby
123
+ News.today
124
+
125
+ News.week
126
+ News.week( 1 )
127
+ News.week( 1, 2019 )
128
+
129
+ News.month
130
+ News.month( 1 )
131
+ News.month( 1, 2019 )
132
+
133
+ News.year
134
+ News.year( 2019 )
135
+
136
+ News.this_week
137
+ News.this_month
138
+ News.this_year
139
+
140
+ News.q1
141
+ News.q2
142
+ News.q3
143
+ News.q4
144
+ ```
145
+
146
+ and some more.
147
+
148
+
149
+
150
+ ### All Together Now - Let's Build A Newsfeed in 20 Lines
151
+
152
+ ```ruby
153
+ require 'newscast'
154
+
155
+ # step 1) subscribe to a list of news feeds / channels
156
+
157
+ News.subscribe(
158
+ 'http://www.ruby-lang.org/en/feeds/news.rss', # Ruby Lang News
159
+ 'http://www.jruby.org/atom.xml', # JRuby Lang News
160
+ 'http://blog.rubygems.org/atom.xml', # RubyGems News
161
+ 'http://bundler.io/blog/feed.xml', # Bundler News
162
+ 'http://weblog.rubyonrails.org/feed/atom.xml', # Ruby on Rails News
163
+ 'http://sinatrarb.com/feed.xml', # Sinatra News
164
+ 'https://hanamirb.org/atom.xml', # Hanami News
165
+ 'http://jekyllrb.com/feed.xml', # Jekyll News
166
+ 'http://feeds.feedburner.com/jetbrains_rubymine?format=xml', # RubyMine IDE News
167
+ 'https://blog.phusion.nl/rss/', # Phusion News
168
+ 'https://rubyinstaller.org/feed.xml', # Ruby Installer for Windows News
169
+ 'http://planetruby.github.io/calendar/feed.xml', # Ruby Conferences & Camps News
170
+ 'https://rubytogether.org/news.xml', # Ruby Together News
171
+ 'https://foundation.travis-ci.org/feed.xml', # Travis Foundation News
172
+ 'https://railsgirlssummerofcode.org/blog.xml', # Rails Girls Summer of Code News
173
+
174
+ 'http://blog.zenspider.com/atom.xml', # Ryan Davis @ Seattle › Washington › United States
175
+ 'http://tenderlovemaking.com/atom.xml', # Aaron Patterson @ Seattle › Washington › United States
176
+ 'http://blog.headius.com/feed.xml', # Charles Nutter @ Richfield › Minnesota › United States
177
+ 'http://www.schneems.com/feed.xml', # Richard Schneeman @ Austin › Texas › United States
178
+ 'https://eregon.me/blog/feed.xml', # Benoit Daloze @ Zurich › Switzerland
179
+ 'http://samsaffron.com/posts.rss', # Sam Saffron @ Sydney › Australia
180
+ )
181
+
182
+
183
+ # step 2) fetch and update news feeds / channels
184
+
185
+ News.update
186
+
187
+
188
+ # step 3) mix up all news items in a new page
189
+
190
+ puts News.render( <<TXT )
191
+ <% News.latest.limit(100).each do |item| %>
192
+ <div class="item">
193
+ <h2><a href="<%= item.url %>"><%= item.title %></a></h2>
194
+ <div><%= item.content %></div>
195
+ </div>
196
+ <% end %>
197
+ TXT
198
+ ```
199
+
200
+ resulting in:
201
+
202
+ ``` html
203
+ <div class="item">
204
+ <h2><a href="http://planetruby.github.io/calendar/2020#rubyness">RubyNess @ Inverness,
205
+ Scotland, United Kingdom - Ruby Conferences 'n' Camps Update</a></h2>
206
+ <div><p>What's News? What's Upcoming in 2020?</p>
207
+ <p>
208
+ <b><a href="https://rubyness.org/" id="rubyness">RubyNess</a></b><br>
209
+ Jul/16+17 (2d) Thu+Fri @ Inverness, Scotland, United Kingdom •
210
+ <a href="https://twitter.com/rubynessconf" title="@rubynessconf">(Updates)</a>
211
+ </p>
212
+ <p>
213
+ See all <a href="http://planetruby.github.io/calendar/2020">Conferences 'n' Camps in 2020»</a>.
214
+ </p></div>
215
+ </div>
216
+
217
+ <div class="item">
218
+ <h2><a href="http://feedproxy.google.com/~r/jetbrains_rubymine/~3/DiNxpQHKHrk/">RubyMine
219
+ 2020.1 EAP is Open!</a></h2>
220
+ <div><p>Hello everyone,</p>
221
+ <p>Today we are happy to announce the opening of the Early Access Program (EAP)
222
+ for RubyMine 2020.1! You can get EAP builds...
223
+ </p>
224
+ <p>Happy Developing!<br>
225
+ Your RubyMine team
226
+ </p></div>
227
+ </div>
228
+ ...
229
+ ```
230
+
231
+
232
+
233
+ ## More
234
+
235
+ - [news.rb quick starter script](https://github.com/feedreader/news.rb) - build your own facebook newsfeed in 1-2-3 steps in 5 minutes
236
+
237
+
238
+
239
+ ## License
240
+
241
+ ![](https://publicdomainworks.github.io/buttons/zero88x31.png)
242
+
243
+ The `newscast` scripts are dedicated to the public domain.
244
+ Use it as you please with no restrictions whatsoever.
245
+
246
+ ## Questions? Comments?
247
+
248
+ Send them along to the [wwwmake Forum/Mailing List](http://groups.google.com/group/wwwmake).
249
+ Thanks!
@@ -0,0 +1,32 @@
1
+ require 'hoe'
2
+ require './lib/newscast/version.rb'
3
+
4
+ Hoe.spec 'newscast' do
5
+
6
+ self.version = Newscast::VERSION
7
+
8
+ self.summary = "newscast - newsreader for easy (re)use - build your own facebook newsfeed in 1-2-3 steps in 5 minutes"
9
+ self.description = summary
10
+
11
+ self.urls = ['https://github.com/feedreader/pluto']
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'wwwmake@googlegroups.com'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'CHANGELOG.md'
19
+
20
+ self.extra_deps = [
21
+ ['pluto-models', '>= 1.6.1'],
22
+ ['pluto-update', '>= 1.6.3'],
23
+ ['sqlite3'], # note: for easy installation include sqlite database library
24
+ ]
25
+
26
+ self.licenses = ['Public Domain']
27
+
28
+ self.spec_extras = {
29
+ required_ruby_version: '>= 2.2.2'
30
+ }
31
+
32
+ end
@@ -0,0 +1,358 @@
1
+
2
+ require 'pluto/update'
3
+
4
+
5
+ ## todo/fix: include stdlibs in pluto/update or pluto/models upstream!!!
6
+ require 'date'
7
+ require 'time'
8
+ require 'cgi'
9
+ require 'uri'
10
+ require 'digest'
11
+ require 'yaml' ## check - already included upstream?
12
+ require 'json' ## check - already included upstream?
13
+
14
+ require 'erb'
15
+ require 'ostruct'
16
+
17
+
18
+
19
+ class Date
20
+ ## def quarter() 1+(self.month-1)/3; end
21
+
22
+ def quarter
23
+ case self.month
24
+ when 1,2,3 then 1
25
+ when 4,5,6 then 2
26
+ when 7,8,9 then 3
27
+ when 10,11,12 then 4
28
+ end
29
+ end
30
+ end
31
+
32
+ # -- for testing:
33
+ # (1..12).each do |num|
34
+ # puts "#{num} -> #{Date.new(2020,num,1).quarter}"
35
+ # end
36
+ # -- prints:
37
+ # 1 -> 1
38
+ # 2 -> 1
39
+ # 3 -> 1
40
+ # 4 -> 2
41
+ # 5 -> 2
42
+ # 6 -> 2
43
+ # 7 -> 3
44
+ # 8 -> 3
45
+ # 9 -> 3
46
+ # 10 -> 4
47
+ # 11 -> 4
48
+ # 12 -> 4
49
+
50
+
51
+
52
+ ## our own code
53
+ require 'newscast/version'
54
+
55
+
56
+
57
+ module News
58
+
59
+ ##
60
+ ## todo/check: allow (add) site = nil for no site "filter" (all items / feeds) - why? why not?
61
+ def self.site=(value) @site = value; end
62
+ def self.site() @site ||= 'news'; end ## note: defaults to news
63
+
64
+
65
+ ####
66
+ # helpers
67
+
68
+ def self.autogen_feed_key( feed_url )
69
+ ## note:
70
+ ## use a "fingerprint" hash digest as key
71
+ ## do NOT include scheme (e.g. https or http)
72
+ ## do NOT include port
73
+ ## so you can change it without "breaking" the key - why? why not?
74
+ ##
75
+ ## e.g. u = URI( 'https://example.com:333/a/b?f=xml'
76
+ ## u.host+u.request_uri
77
+ ## #=> example.com/a/b?f=xml
78
+ uri = URI( feed_url )
79
+ ## note: add host in "plain" text - making errors and the key more readable
80
+ ## note: cut-off www. if leading e.g. www.ruby-lang.org => ruby-lang.org
81
+ host = uri.host.downcase.sub( /^www\./, '' )
82
+ # use a differt separator e.g _ or ~ and NOT $ - why? why not?
83
+ key = "#{host}$#{Digest::MD5.hexdigest( uri.request_uri )}"
84
+ key
85
+ end
86
+
87
+ def self.norm_feed_hash( old_h ) ## todo/check: rename to normalize/unify_feeds or resolve_feedlist or something?
88
+ ## unify feed list entries
89
+ ## case 1) if section name is some thing like [Andrew Kane]
90
+ ## and NO title/name key than assume it's the title/name
91
+ ## and auto-generated key/id
92
+
93
+ ## move all "global" settings to [planet] section - why? why not?
94
+ ## e.g.
95
+ ## title = Planet Open Data News
96
+ ## becomes
97
+ ## [planet]
98
+ ## title = Planet Open Data News
99
+
100
+ h = {}
101
+ old_h.each do |k,v|
102
+ if v.is_a?( String )
103
+ h[ k ] = v ## pass along as-is (assume "top-level" / global setting)
104
+ elsif v.is_a?( Hash )
105
+ ## puts "#{k}:"
106
+ ## pp v
107
+ ## todo/fix: use "proper" ident e.g. allow 0-9 and .-_ too? why? why not?
108
+ if k =~ /^[a-z_][a-z0-9$_.]*$/ ## all lower case; assume id - add 0-9 and .-_ - why? why not?
109
+ h[ k ] = v
110
+ else
111
+ ## puts "bingo! section name shortcut - #{k}"
112
+ if k.start_with?( 'http' )
113
+ if v.has_key?( 'feed' ) then raise ArgumentError.new( "duplicate >feed< hash table entry in section >#{k}<; cannot autogen key" ); end
114
+
115
+ new_k = autogen_feed_key( k )
116
+ # note: use merge - why? why not? to NOT overwrite existing entry - why? why not?
117
+ h[ new_k ] = { 'feed' => k }.merge( v )
118
+ else
119
+ ## transform key to title and auto-generate id (new key)
120
+ if v.has_key?( 'title' ) || v.has_key?( 'name' ) then raise ArgumentError.new( "duplicate >name< or >title< hash table entry in section >#{k}<; cannot autogen key" ); end
121
+ if v.has_key?( 'feed' ) == false then raise ArgumentError.new( "expected / required >feed< hash table entry missing for section >#{k}<"); end
122
+
123
+ new_k = autogen_feed_key( v['feed'] )
124
+ # note: use merge - why? why not? to NOT overwrite existing entry - why? why not?
125
+ h[ new_k ] = { 'title' => k }.merge( v )
126
+ end
127
+ end
128
+ else
129
+ raise ArgumentError.new( "expected String or Hash for value (in hash table) but got >#{v.class.name}< for key >#{k}<" )
130
+ end
131
+ end
132
+
133
+ ## todo/check - auto-add required (?) missing title if missing - why? why not?
134
+ h['title'] = 'Untitled' if h.has_key?( 'title' ) == false
135
+
136
+ h
137
+ end
138
+
139
+
140
+ def self.subscribe( *feeds )
141
+
142
+ site_hash = if feeds.size == 1 && feeds[0].is_a?( Hash )
143
+ ## puts "bingo! it's a hash"
144
+ norm_feed_hash( feeds[0] )
145
+ elsif feeds.size == 1 && feeds[0].is_a?( String ) && feeds[0] =~ /\n/
146
+ ## string includes newline (e.g. more than a single line?)
147
+ ## if yes, parse and assume ini (config) format
148
+ norm_feed_hash( INI.load( feeds[0] ))
149
+ else ## assume list / array of strings (feed_urls)
150
+ ## puts "bingo! it's a string list"
151
+ ## auto-build a (simple) site hash
152
+ feeds.reduce( { ## note: keys are strings (NOT symbols) for now
153
+ 'title' => 'Untitled'
154
+ ## todo/check: remove title? required? check in model update if missing?
155
+ } ) do |h, feed|
156
+ key = autogen_feed_key( feed )
157
+
158
+ h[ key ] = { 'feed' => feed }
159
+ h
160
+ end
161
+ end
162
+
163
+ connection ## make sure we have a database connection (and setup) up-and-running
164
+ ## note: always use "multi-site" setup; defaults to 'news' site key
165
+ Pluto::Model::Site.deep_create_or_update_from_hash!( site, site_hash )
166
+ end
167
+
168
+ def self.update
169
+ connection ## make sure we have a database connection (and setup) up-and-running
170
+ Pluto.update_feeds
171
+ end
172
+ def self.refresh() update; end ## convenience alias for update
173
+
174
+
175
+
176
+ def self.feeds
177
+ connection
178
+ ## note: always use "multi-site" setup; defaults to 'news' site key
179
+ ## note: add "default" scope - orders (sorts) by latest / time
180
+ rec = Pluto::Model::Site.where(key: site).first
181
+ if rec.nil?
182
+ Pluto::Model::Feed.none ## use null (relation) pattern to avoid crash on nil - why? why not?
183
+ else
184
+ rec.feeds.order(
185
+ Arel.sql( "coalesce(feeds.updated,feeds.published,'1970-01-01') desc" )
186
+ )
187
+ end
188
+ end
189
+ def self.channels() feeds; end ## convenience alias for feeds
190
+
191
+
192
+ def self.items
193
+ connection ## make sure we have a database connection (and setup) up-and-running
194
+ ## note: always use "multi-site" setup; defaults to 'news' site key
195
+ ## note: add "default" scope - orders (sorts) by latest / time
196
+ rec = Pluto::Model::Site.where(key: site).first
197
+ if rec.nil?
198
+ Pluto::Model::Item.none ## use null (relation) pattern to avoid crash on nil - why? why not?
199
+ else
200
+ rec.items.order(
201
+ Arel.sql( "coalesce(items.updated,items.published,'1970-01-01') desc" )
202
+ )
203
+ end
204
+ end
205
+ def self.latest() items; end ## note: "default" scope orders (sorts) by latest / time
206
+
207
+
208
+ def self.today
209
+ ## todo: order by time!! - possible - why? why not?
210
+ ## note: use date() to cut-off hours etc. if present?
211
+
212
+ q = Date.today.strftime('%Y-%m-%d') # e.g. 2020-02-20
213
+ items.
214
+ where(
215
+ Arel.sql( "date(coalesce(items.updated,items.published)) = '#{q}'" )
216
+ )
217
+ end
218
+
219
+
220
+ def self.between( start_date, end_date ) ## helper for week, q1, q2, etc.
221
+ q_start = start_date.strftime('%Y-%m-%d')
222
+ q_end = end_date.strftime('%Y-%m-%d')
223
+
224
+ items.
225
+ where(
226
+ Arel.sql( "date(coalesce(items.updated,items.published,'1970-01-01')) BETWEEN '#{q_start}' AND '#{q_end}'" )
227
+ )
228
+ end
229
+
230
+ def self.week( week=Date.today.cweek, year=Date.today.year )
231
+ ## note: SQLite only supports "classic" week of year (not ISO "commercial week" starting on monday - and not on sunday)
232
+ ## %W - week of year: 00-53
233
+ ## thus, needs to calculate start and end date!!!
234
+
235
+ start_date = Date.commercial(year, week, 1)
236
+ end_date = Date.commercial(year, week, 7)
237
+
238
+ between( start_date, end_date )
239
+ end
240
+
241
+ def self.month( month=Date.today.month, year=Date.today.year )
242
+ q = "%4d-%02d" % [year,month] # e.g. 2020-01 etc.
243
+ items.
244
+ where(
245
+ Arel.sql( "strftime('%Y-%m', coalesce(items.updated,items.published,'1970-01-01')) = '#{q}'" )
246
+ )
247
+ end
248
+
249
+ def self.quarter( quarter=Date.today.quarter, year=Date.today.year )
250
+ case quarter
251
+ when 1 then between( Date.new( year, 1, 1), Date.new( year, 3, 31) );
252
+ when 2 then between( Date.new( year, 4, 1), Date.new( year, 6, 30) );
253
+ when 3 then between( Date.new( year, 7, 1), Date.new( year, 9, 30) );
254
+ when 4 then between( Date.new( year, 10, 1), Date.new( year,12, 31) );
255
+ else raise ArgumentError
256
+ end
257
+ end
258
+
259
+ def self.quarter1( year=Date.today.year ) quarter(1, year); end
260
+ def self.quarter2( year=Date.today.year ) quarter(2, year); end
261
+ def self.quarter3( year=Date.today.year ) quarter(3, year); end
262
+ def self.quarter4( year=Date.today.year ) quarter(4, year); end
263
+
264
+ def self.q( quarter=Date.today.quarter, year=Date.today.year ) quarter( quarter, year ); end
265
+ def self.q1( year=Date.today.year ) quarter1( year ); end
266
+ def self.q2( year=Date.today.year ) quarter2( year ); end
267
+ def self.q3( year=Date.today.year ) quarter3( year ); end
268
+ def self.q4( year=Date.today.year ) quarter4( year ); end
269
+
270
+
271
+ def self.year( year=Date.today.year )
272
+ q = year
273
+ items.
274
+ where(
275
+ Arel.sql( "strftime('%Y', coalesce(items.updated,items.published,'1970-01-01')) = '#{q}'" )
276
+ )
277
+ end
278
+
279
+
280
+
281
+
282
+ def self.this_week() week; end ## convenience alias - keep - why? why not?
283
+ def self.this_month() month; end
284
+ def self.this_quarter() quarter; end
285
+ def self.this_year() year; end
286
+
287
+
288
+
289
+ class Template
290
+ class Context < OpenStruct
291
+ ## use a different name - why? why not?
292
+ ## e.g. to_h, to_hash, vars, locals, assigns, etc.
293
+ def get_binding() binding; end
294
+
295
+ ## add builtin helpers / shortcuts
296
+ def h( text ) CGI.escapeHTML( text ); end
297
+ end # class Template::Context
298
+
299
+
300
+ def initialize( text )
301
+ @template = ERB.new( text )
302
+ end
303
+
304
+ ## todo: use locals / assigns or something instead of **kwargs - why? why not?
305
+ ## allow/support (extra) locals / assigns - why? why not?
306
+ def render( **kwargs )
307
+ ## note: Ruby >= 2.5 has ERB#result_with_hash - use later - why? why not?
308
+ @template.result( Context.new( **kwargs ).get_binding )
309
+ end
310
+ end # class Template
311
+
312
+ def self.render( text, **kwargs )
313
+ template = Template.new( text )
314
+ template.render( **kwargs )
315
+ end
316
+
317
+
318
+
319
+
320
+ class Configuration
321
+ def database=(value) @database = value; end
322
+ def database() @database || { adapter: 'sqlite3', database: './news.db' }; end
323
+ end # class Configuration
324
+
325
+ def self.configure
326
+ yield( config )
327
+ end
328
+
329
+ def self.config
330
+ @config ||= Configuration.new
331
+ end
332
+
333
+
334
+ class Connection
335
+ def initialize # convenience shortcut w/ automigrate
336
+ config = if News.config.database.is_a?(Hash)
337
+ News.config.database
338
+ else ## assume a string (e.g. :memory:, or <path> AND sqlite3 adapter)
339
+ { adapter: 'sqlite3', database: News.config.database }
340
+ end
341
+
342
+ Pluto.connect( config )
343
+ Pluto.auto_migrate!
344
+ end
345
+ end # class Connection
346
+
347
+ def self.connection ## use for "auto-magic" connection w/ automigrate
348
+ ## todo/fix: check - "just simply" return ActiveRecord connection - possible - why? why not?
349
+ ## do NOT use our own Connection class
350
+ @connection ||= Connection.new
351
+ end
352
+ end # module News
353
+
354
+
355
+
356
+
357
+ # say hello
358
+ puts Newscast.banner if $DEBUG || (defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG)
@@ -0,0 +1,22 @@
1
+
2
+ module Newscast
3
+
4
+ MAJOR = 1
5
+ MINOR = 1
6
+ PATCH = 1
7
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
8
+
9
+ def self.version
10
+ VERSION
11
+ end
12
+
13
+ def self.banner
14
+ ### todo: add RUBY_PATCHLEVEL or RUBY_PATCH_LEVEL e.g. -p124
15
+ "newscast/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
16
+ end
17
+
18
+ def self.root
19
+ "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
20
+ end
21
+
22
+ end # module Newscast
@@ -0,0 +1,10 @@
1
+ ## minitest setup
2
+ require 'minitest/autorun'
3
+
4
+ ## our own code
5
+ require 'newscast'
6
+
7
+
8
+ Pluto.config.debug = true
9
+ Pluto.config.logger.level = :debug
10
+
@@ -0,0 +1,94 @@
1
+ ###
2
+ # to run use
3
+ # ruby -I ./lib -I ./test test/test_queries.rb
4
+
5
+
6
+ require 'helper'
7
+
8
+ class TestQueries < MiniTest::Test
9
+
10
+ def test_queries
11
+ puts News.channels.to_sql
12
+ puts News.feeds.to_sql
13
+
14
+ puts News.items.to_sql
15
+
16
+
17
+ puts News.latest.limit( 2 ).to_sql
18
+ puts News.today.to_sql
19
+
20
+ puts News.week.to_sql
21
+ puts News.week( 1 ).to_sql
22
+ puts News.week( 1, 2019 ).to_sql
23
+
24
+ puts News.month.to_sql
25
+ puts News.month( 1 ).to_sql
26
+ puts News.month( 1, 2019 ).to_sql
27
+
28
+ puts News.year.to_sql
29
+ puts News.year( 2019 ).to_sql
30
+
31
+ puts News.this_week.to_sql
32
+ puts News.this_month.to_sql
33
+ puts News.this_quarter.to_sql
34
+ puts News.this_year.to_sql
35
+
36
+
37
+ puts News.quarter.to_sql
38
+ puts News.q.to_sql
39
+
40
+ puts News.quarter( 1 ).to_sql
41
+ puts News.quarter( 2 ).to_sql
42
+ puts News.quarter( 3 ).to_sql
43
+ puts News.quarter( 4 ).to_sql
44
+ puts News.quarter1.to_sql
45
+ puts News.quarter2.to_sql
46
+ puts News.quarter3.to_sql
47
+ puts News.quarter4.to_sql
48
+
49
+ puts News.q( 1 ).to_sql
50
+ puts News.q( 2 ).to_sql
51
+ puts News.q( 3 ).to_sql
52
+ puts News.q( 4 ).to_sql
53
+ puts News.q1.to_sql
54
+ puts News.q2.to_sql
55
+ puts News.q3.to_sql
56
+ puts News.q4.to_sql
57
+ puts News.q( 1, 2019 ).to_sql
58
+ puts News.q( 2, 2019 ).to_sql
59
+ puts News.q( 3, 2019 ).to_sql
60
+ puts News.q( 4, 2019 ).to_sql
61
+ puts News.q1( 2019 ).to_sql
62
+ puts News.q2( 2019 ).to_sql
63
+ puts News.q3( 2019 ).to_sql
64
+ puts News.q4( 2019 ).to_sql
65
+
66
+
67
+ ###### run queries
68
+ pp News.latest.limit( 2 ).to_a
69
+ pp News.today.to_a
70
+
71
+ pp News.week.to_a
72
+ pp News.week( 1 ).to_a
73
+ pp News.week( 1, 2019 ).to_a
74
+
75
+ pp News.month.to_a
76
+ pp News.month( 1 ).to_a
77
+ pp News.month( 1, 2019 ).to_a
78
+
79
+ pp News.year.to_a
80
+ pp News.year( 2019 ).to_a
81
+
82
+ pp News.this_week.to_a
83
+ pp News.this_month.to_a
84
+ pp News.this_year.to_a
85
+
86
+ pp News.q1.to_a
87
+ pp News.q2.to_a
88
+ pp News.q3.to_a
89
+ pp News.q4.to_a
90
+
91
+ assert true
92
+ end
93
+
94
+ end # class TestQueries
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: newscast
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pluto-models
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.6.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.6.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: pluto-update
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hoe
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.16'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.16'
83
+ description: newscast - newsreader for easy (re)use - build your own facebook newsfeed
84
+ in 1-2-3 steps in 5 minutes
85
+ email: wwwmake@googlegroups.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files:
89
+ - CHANGELOG.md
90
+ - Manifest.txt
91
+ - README.md
92
+ files:
93
+ - CHANGELOG.md
94
+ - Manifest.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/newscast.rb
98
+ - lib/newscast/version.rb
99
+ - test/helper.rb
100
+ - test/test_queries.rb
101
+ homepage: https://github.com/feedreader/pluto
102
+ licenses:
103
+ - Public Domain
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options:
107
+ - "--main"
108
+ - README.md
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 2.2.2
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.5.2
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: newscast - newsreader for easy (re)use - build your own facebook newsfeed
127
+ in 1-2-3 steps in 5 minutes
128
+ test_files: []