Module: News

Defined in:
lib/newscast.rb

Defined Under Namespace

Classes: Configuration, Connection, Template

Class Method Summary collapse

Class Method Details

.autogen_feed_key(feed_url) ⇒ Object

helpers



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/newscast.rb', line 68

def self.autogen_feed_key( feed_url )
  ## note:
  ##   use a "fingerprint" hash digest as key
  ##     do NOT include scheme (e.g. https or http)
  ##     do NOT include port
  ##   so you can change it without "breaking" the key - why? why not?
  ##
  ##  e.g.   u = URI( 'https://example.com:333/a/b?f=xml'
  ##          u.host+u.request_uri
  ##         #=> example.com/a/b?f=xml
  uri = URI( feed_url )
  ## note: add host in "plain" text - making errors and the key more readable
  ## note: cut-off www. if leading e.g. www.ruby-lang.org => ruby-lang.org
  host = uri.host.downcase.sub( /^www\./, '' )
  #   use a differt separator e.g _ or ~ and NOT $ - why? why not?
  key = "#{host}$#{Digest::MD5.hexdigest( uri.request_uri )}"
  key
end

.between(start_date, end_date) ⇒ Object

helper for week, q1, q2, etc.



220
221
222
223
224
225
226
227
228
# File 'lib/newscast.rb', line 220

def self.between( start_date, end_date )   ## helper for week, q1, q2, etc.
  q_start = start_date.strftime('%Y-%m-%d')
  q_end   = end_date.strftime('%Y-%m-%d')

  items.
    where(
      Arel.sql( "date(coalesce(items.updated,items.published,'1970-01-01')) BETWEEN '#{q_start}' AND '#{q_end}'" )
    )
end

.channelsObject

convenience alias for feeds



189
# File 'lib/newscast.rb', line 189

def self.channels()  feeds; end

.configObject



329
330
331
# File 'lib/newscast.rb', line 329

def self.config
  @config ||= Configuration.new
end

.configure {|config| ... } ⇒ Object

class Configuration

Yields:



325
326
327
# File 'lib/newscast.rb', line 325

def self.configure
  yield( config )
end

.connectionObject

class Connection



347
348
349
350
351
# File 'lib/newscast.rb', line 347

def self.connection     ## use for "auto-magic" connection w/ automigrate
  ##  todo/fix: check - "just simply" return ActiveRecord connection - possible - why? why not?
  ##                       do NOT use our own Connection class
  @connection ||= Connection.new
end

.feedsObject



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/newscast.rb', line 176

def self.feeds
  connection
  ## note: always use "multi-site" setup; defaults to 'news' site key
  ## note: add "default" scope - orders (sorts) by latest / time
  rec = Pluto::Model::Site.where(key: site).first
  if rec.nil?
    Pluto::Model::Feed.none     ## use null (relation) pattern to avoid crash on nil - why? why not?
  else
    rec.feeds.order(
      Arel.sql( "coalesce(feeds.updated,feeds.published,'1970-01-01') desc" )
    )
  end
end

.itemsObject



192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/newscast.rb', line 192

def self.items
  connection    ## make sure we have a database connection (and setup) up-and-running
  ## note: always use "multi-site" setup; defaults to 'news' site key
  ## note: add "default" scope - orders (sorts) by latest / time
  rec = Pluto::Model::Site.where(key: site).first
  if rec.nil?
    Pluto::Model::Item.none    ## use null (relation) pattern to avoid crash on nil - why? why not?
  else
    rec.items.order(
      Arel.sql( "coalesce(items.updated,items.published,'1970-01-01') desc" )
    )
  end
end

.latestObject

note: “default” scope orders (sorts) by latest / time



205
# File 'lib/newscast.rb', line 205

def self.latest()    items; end

.month(month = Date.today.month, year = Date.today.year) ⇒ Object



241
242
243
244
245
246
247
# File 'lib/newscast.rb', line 241

def self.month( month=Date.today.month, year=Date.today.year )
  q = "%4d-%02d" % [year,month]     # e.g. 2020-01 etc.
  items.
    where(
      Arel.sql( "strftime('%Y-%m', coalesce(items.updated,items.published,'1970-01-01')) = '#{q}'" )
    )
end

.norm_feed_hash(old_h) ⇒ Object

todo/check: rename to normalize/unify_feeds or resolve_feedlist or something?



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/newscast.rb', line 87

def self.norm_feed_hash( old_h )   ## todo/check: rename to normalize/unify_feeds or resolve_feedlist or something?
  ## unify feed list entries
  ## case 1) if section name is some thing like [Andrew Kane]
  ##           and  NO title/name key than assume it's the title/name
  ##           and auto-generated key/id

  ## move all "global" settings to [planet] section - why? why not?
  ##  e.g.
  ##   title = Planet Open Data News
  ##  becomes
  ##   [planet]
  ##     title = Planet Open Data News

  h = {}
  old_h.each do |k,v|
     if    v.is_a?( String )
        h[ k ] = v   ## pass along as-is (assume "top-level" / global setting)
     elsif v.is_a?( Hash )
        ## puts "#{k}:"
        ## pp v
        ## todo/fix: use "proper" ident e.g. allow 0-9 and .-_ too? why? why not?
        if k =~ /^[a-z_][a-z0-9$_.]*$/   ## all lower case; assume id - add 0-9 and .-_ - why? why not?
          h[ k ] = v
        else
           ## puts "bingo! section name shortcut - #{k}"
           if k.start_with?( 'http' )
              if v.has_key?( 'feed' ) then raise ArgumentError.new( "duplicate >feed< hash table entry in section >#{k}<; cannot autogen key" );  end

              new_k = autogen_feed_key( k )
              # note: use merge - why? why not? to NOT overwrite existing entry - why? why not?
              h[ new_k ]  = { 'feed' => k }.merge( v )
           else
              ## transform key to title and auto-generate id (new key)
              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
              if v.has_key?( 'feed' ) == false                 then raise ArgumentError.new( "expected / required >feed< hash table entry missing for section >#{k}<");  end

              new_k = autogen_feed_key( v['feed'] )
              # note: use merge - why? why not? to NOT overwrite existing entry - why? why not?
              h[ new_k ]  = { 'title' => k }.merge( v )
           end
        end
     else
       raise ArgumentError.new( "expected String or Hash for value (in hash table) but got >#{v.class.name}< for key >#{k}<" )
     end
  end

  ## todo/check - auto-add required (?) missing title if missing - why? why not?
  h['title'] = 'Untitled'   if h.has_key?( 'title' ) == false

  h
end

.q(quarter = Date.today.quarter, year = Date.today.year) ⇒ Object



264
# File 'lib/newscast.rb', line 264

def self.q( quarter=Date.today.quarter, year=Date.today.year ) quarter( quarter, year ); end

.q1(year = Date.today.year) ⇒ Object



265
# File 'lib/newscast.rb', line 265

def self.q1( year=Date.today.year ) quarter1( year ); end

.q2(year = Date.today.year) ⇒ Object



266
# File 'lib/newscast.rb', line 266

def self.q2( year=Date.today.year ) quarter2( year ); end

.q3(year = Date.today.year) ⇒ Object



267
# File 'lib/newscast.rb', line 267

def self.q3( year=Date.today.year ) quarter3( year ); end

.q4(year = Date.today.year) ⇒ Object



268
# File 'lib/newscast.rb', line 268

def self.q4( year=Date.today.year ) quarter4( year ); end

.quarter(quarter = Date.today.quarter, year = Date.today.year) ⇒ Object



249
250
251
252
253
254
255
256
257
# File 'lib/newscast.rb', line 249

def self.quarter( quarter=Date.today.quarter, year=Date.today.year )
  case quarter
  when 1 then between( Date.new( year,  1, 1), Date.new( year, 3, 31) );
  when 2 then between( Date.new( year,  4, 1), Date.new( year, 6, 30) );
  when 3 then between( Date.new( year,  7, 1), Date.new( year, 9, 30) );
  when 4 then between( Date.new( year, 10, 1), Date.new( year,12, 31) );
  else  raise ArgumentError
  end
end

.quarter1(year = Date.today.year) ⇒ Object



259
# File 'lib/newscast.rb', line 259

def self.quarter1( year=Date.today.year ) quarter(1, year); end

.quarter2(year = Date.today.year) ⇒ Object



260
# File 'lib/newscast.rb', line 260

def self.quarter2( year=Date.today.year ) quarter(2, year); end

.quarter3(year = Date.today.year) ⇒ Object



261
# File 'lib/newscast.rb', line 261

def self.quarter3( year=Date.today.year ) quarter(3, year); end

.quarter4(year = Date.today.year) ⇒ Object



262
# File 'lib/newscast.rb', line 262

def self.quarter4( year=Date.today.year ) quarter(4, year); end

.refreshObject

convenience alias for update



172
# File 'lib/newscast.rb', line 172

def self.refresh() update; end

.render(text, **kwargs) ⇒ Object

class Template



312
313
314
315
# File 'lib/newscast.rb', line 312

def self.render( text, **kwargs )
  template = Template.new( text )
  template.render( **kwargs )
end

.siteObject

note: defaults to news



62
# File 'lib/newscast.rb', line 62

def self.site()        @site ||= 'news'; end

.site=(value) ⇒ Object

todo/check: allow (add) site = nil for no site “filter” (all items / feeds) - why? why not?



61
# File 'lib/newscast.rb', line 61

def self.site=(value)  @site   = value;  end

.subscribe(*feeds) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/newscast.rb', line 140

def self.subscribe( *feeds )

  site_hash =   if feeds.size == 1 && feeds[0].is_a?( Hash )
                   ## puts "bingo!  it's a hash"
                   norm_feed_hash( feeds[0] )
                elsif feeds.size == 1 && feeds[0].is_a?( String ) && feeds[0] =~ /\n/
                   ## string includes newline (e.g. more than a single line?)
                   ## if yes, parse and assume ini (config) format
                   norm_feed_hash( INI.load( feeds[0] ))
                else   ## assume list / array of strings (feed_urls)
                   ## puts "bingo! it's a string list"
                   ##   auto-build a (simple) site hash
                   feeds.reduce( {        ## note: keys are strings (NOT symbols) for now
                                    'title' => 'Untitled'
                                    ## todo/check: remove title? required? check in model update if missing?
                                  } ) do |h, feed|
                                       key = autogen_feed_key( feed )

                                       h[ key ] = { 'feed'  => feed }
                                       h
                                      end
                end

  connection   ## make sure we have a database connection (and setup) up-and-running
  ## note: always use "multi-site" setup; defaults to 'news' site key
  Pluto::Model::Site.deep_create_or_update_from_hash!( site, site_hash )
end

.this_monthObject



283
# File 'lib/newscast.rb', line 283

def self.this_month()   month;   end

.this_quarterObject



284
# File 'lib/newscast.rb', line 284

def self.this_quarter() quarter; end

.this_weekObject

convenience alias - keep - why? why not?



282
# File 'lib/newscast.rb', line 282

def self.this_week()    week;    end

.this_yearObject



285
# File 'lib/newscast.rb', line 285

def self.this_year()    year;    end

.todayObject



208
209
210
211
212
213
214
215
216
217
# File 'lib/newscast.rb', line 208

def self.today
    ## todo: order by time!! - possible - why? why not?
    ## note: use date() to cut-off hours etc. if present?

    q = Date.today.strftime('%Y-%m-%d')   # e.g. 2020-02-20
    items.
      where(
        Arel.sql( "date(coalesce(items.updated,items.published)) = '#{q}'" )
      )
end

.updateObject



168
169
170
171
# File 'lib/newscast.rb', line 168

def self.update
  connection   ## make sure we have a database connection (and setup) up-and-running
  Pluto.update_feeds
end

.week(week = Date.today.cweek, year = Date.today.year) ⇒ Object



230
231
232
233
234
235
236
237
238
239
# File 'lib/newscast.rb', line 230

def self.week( week=Date.today.cweek, year=Date.today.year )
  ## note: SQLite only supports "classic" week of year (not ISO "commercial week" starting on monday - and not on sunday)
  ## %W - week of year: 00-53
  ## thus, needs to calculate start and end date!!!

  start_date = Date.commercial(year, week, 1)
  end_date   = Date.commercial(year, week, 7)

  between( start_date, end_date )
end

.year(year = Date.today.year) ⇒ Object



271
272
273
274
275
276
277
# File 'lib/newscast.rb', line 271

def self.year( year=Date.today.year )
  q = year
  items.
    where(
      Arel.sql( "strftime('%Y', coalesce(items.updated,items.published,'1970-01-01')) = '#{q}'" )
    )
end