mako_rss 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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +7 -0
  5. data/.rubocop_todo.yml +38 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +124 -0
  10. data/Rakefile +13 -0
  11. data/bin/mako +6 -0
  12. data/lib/mako/article.rb +36 -0
  13. data/lib/mako/cli.rb +49 -0
  14. data/lib/mako/commands/build.rb +33 -0
  15. data/lib/mako/commands/new.rb +28 -0
  16. data/lib/mako/commands/schedule.rb +12 -0
  17. data/lib/mako/commands/version.rb +11 -0
  18. data/lib/mako/configuration.rb +44 -0
  19. data/lib/mako/core.rb +81 -0
  20. data/lib/mako/core_ext/numeric.rb +10 -0
  21. data/lib/mako/core_ext/time.rb +10 -0
  22. data/lib/mako/core_ext.rb +4 -0
  23. data/lib/mako/errors.rb +25 -0
  24. data/lib/mako/feed.rb +29 -0
  25. data/lib/mako/feed_constructor.rb +71 -0
  26. data/lib/mako/feed_requester.rb +43 -0
  27. data/lib/mako/file_open_util.rb +19 -0
  28. data/lib/mako/html_renderer.rb +30 -0
  29. data/lib/mako/layouts/_feed_container.html.erb +19 -0
  30. data/lib/mako/mako_logger.rb +12 -0
  31. data/lib/mako/sass_renderer.rb +30 -0
  32. data/lib/mako/subscription_list_parser.rb +28 -0
  33. data/lib/mako/version.rb +5 -0
  34. data/lib/mako/view_helpers.rb +28 -0
  35. data/lib/mako/writer.rb +18 -0
  36. data/lib/mako.rb +41 -0
  37. data/lib/templates/Gemfile +5 -0
  38. data/lib/templates/config.yaml +14 -0
  39. data/lib/templates/sample_subscriptions/subscriptions.json +1 -0
  40. data/lib/templates/sample_subscriptions/subscriptions.txt +5 -0
  41. data/lib/templates/sample_subscriptions/subscriptions.xml +12 -0
  42. data/lib/templates/themes/sass/_fonts.scss +38 -0
  43. data/lib/templates/themes/sass/_layout.scss +97 -0
  44. data/lib/templates/themes/sass/_reboot.scss +473 -0
  45. data/lib/templates/themes/sass/_utilities.scss +13 -0
  46. data/lib/templates/themes/sass/_variables.scss +57 -0
  47. data/lib/templates/themes/simple.html.erb +46 -0
  48. data/lib/templates/themes/simple.scss +6 -0
  49. data/mako_rss.gemspec +36 -0
  50. metadata +233 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb8b5a3c394b989b9722f6ae04b6373d23ba7e08
4
+ data.tar.gz: 11efdfb201da11cf3ec348d84ae41c639c7a52a8
5
+ SHA512:
6
+ metadata.gz: a7603d695ded87c83719c2cd159d4e33f85878525fdd29c597c6a6fd033148c7e7a3add809f824c6e544634b196b109b3a1ada4d8cb0fcb5b6a44f7c93751f99
7
+ data.tar.gz: 0ba4e36181aa8dd232ad7f83fb3960dba146c136095cd0e875e0547a262aee1ceb80980e2a98a886a1534bb000bfd5259df3e77815ea87c0da2f8ea1bec675d9
data/.gitignore ADDED
@@ -0,0 +1,33 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+ .byebug_history
13
+ .sass-cache
14
+
15
+ # Used by dotenv library to load environment variables.
16
+ # .env
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalization:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ Gemfile.lock
32
+ # .ruby-version
33
+ # .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+
6
+ Layout/IndentHeredoc:
7
+ EnforcedStyle: squiggly
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,38 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2017-06-06 16:33:49 -0400 using RuboCop version 0.35.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 3
10
+ Metrics/AbcSize:
11
+ Max: 26
12
+
13
+ # Offense count: 37
14
+ # Configuration parameters: AllowURI, URISchemes.
15
+ Metrics/LineLength:
16
+ Max: 629
17
+
18
+ # Offense count: 5
19
+ # Configuration parameters: CountComments.
20
+ Metrics/MethodLength:
21
+ Max: 25
22
+
23
+ # Offense count: 1
24
+ Style/AccessorMethodName:
25
+ Exclude:
26
+ - 'lib/mako/core.rb'
27
+
28
+ # Offense count: 22
29
+ # Configuration parameters: Exclude.
30
+ Style/Documentation:
31
+ Enabled: false
32
+
33
+ # Offense count: 1
34
+ # Cop supports --auto-correct.
35
+ # Configuration parameters: AllowAsExpressionSeparator.
36
+ Style/Semicolon:
37
+ Exclude:
38
+ - 'lib/mako/article.rb'
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ cache: bundler
6
+ before_install: gem install bundler -v 1.14.6
7
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in mako.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Jonathan Pike
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # Mako 🥛 [![Build Status](https://travis-ci.org/jonathanpike/mako.svg?branch=master)](https://travis-ci.org/jonathanpike/mako) [![Coverage Status](https://coveralls.io/repos/github/jonathanpike/mako/badge.svg?branch=master)](https://coveralls.io/github/jonathanpike/mako?branch=master)
2
+
3
+ Mako is a static-site generating RSS reader. Mako is also my son's name for
4
+ milk.
5
+
6
+ I designed Mako with the following principals in mind:
7
+
8
+ 1. RSS feeds are freely available, so reader software should be freely
9
+ available.
10
+ 2. RSS feeds are part of the web, so reader software should be part of the web.
11
+
12
+ ## Installation
13
+
14
+ To install Mako, do:
15
+
16
+ $ gem install mako
17
+
18
+ Once the gem is installed, you'll need to generate a new Mako site. You can do
19
+ this in 1 of 2 ways.
20
+
21
+ 1. If you already have a directory that you want Mako to live in, run:
22
+
23
+ $ mako new
24
+
25
+ 2. If you don't have a directory and want Mako to create on, run:
26
+
27
+ $ mako new path/to/directory
28
+
29
+ In your new Mako site, you'll see a directory structure like this:
30
+
31
+ ```
32
+ .
33
+ ├── sample_subscriptions
34
+ | ├── subscriptions.json
35
+ | ├── subscriptions.txt
36
+ | └── subscriptions.xml
37
+ ├── site
38
+ ├── themes
39
+ | ├── sass
40
+ | | ├── _fonts.scss
41
+ | | ├── _layout.scss
42
+ | | ├── _reboot.scss
43
+ | | ├── _utitilies.scss
44
+ | | └── _variables.scss
45
+ | ├── simple.html.erb
46
+ | └── simple.scss
47
+ ├── Gemfile
48
+ └── config.yaml
49
+ ```
50
+
51
+ ## Adding Feeds
52
+
53
+ Once Mako is installed and your Mako site has been created, the next step is to
54
+ add your own feeds. Mako currently supports 3 formats:
55
+
56
+ 1. OPML
57
+ 2. JSON
58
+ 3. Plain Text
59
+
60
+ In the [`sample_subscriptions`](/lib/templates/sample_subscriptions) directory, you'll see an example of each format.
61
+ Whichever you choose, place the file in the root directory and be sure its name
62
+ is `subscriptions`. The file extension (`.xml`/`.opml`, `.json`, or `.txt`)
63
+ will tell Mako what kind of subscription file you have.
64
+
65
+ ## Configuration
66
+
67
+ Mako has very few configuration options. You can see all of them in the
68
+ comments in [`config.yaml`](/lib/templates/config.yaml).
69
+
70
+ ## Building your Site
71
+
72
+ Once you've added your subscriptions, you can build your Mako site for the first
73
+ time. By default, Mako only builds the HTML portion of the site with the `mako
74
+ build` command. Since the CSS has not been generated, use the `--with-sass`
75
+ flag, like this:
76
+
77
+ $ mako build --with-sass
78
+
79
+ The built files will be present in the `site` directory after the `build`
80
+ command has been run.
81
+
82
+ At present, Mako only displays the last 2 days worth of content from your feeds.
83
+ If a feed has not been updated in the last 2 days, it will not be displayed on
84
+ your Mako site. Similarly, articles that were published > 2 days in the past
85
+ will not be displayed on your Mako site.
86
+
87
+ ## Themes
88
+
89
+ Mako comes with a simple theme of my own design (and own liking). Perhaps you
90
+ want something different. Mako's themes are templated with
91
+ [ERB](https://ruby-doc.org/stdlib-2.4.1/libdoc/erb/rdoc/ERB.html). You have
92
+ access to the following convenience methods:
93
+ - `today` - Gives you the current date in Day, `Date Month Year` format.
94
+ - `last_updated` - Gives you the current date and time in `day month year
95
+ hour:minute:second` format.
96
+
97
+ Within your template, you can access the feed data through the `feeds` array.
98
+ The `feeds` array contains `feed` objects, which have the following attributes:
99
+ - `title` - a string of the feed title.
100
+ - `url` - a string of the feed url.
101
+ - `articles` - an array of the articles associated with the feed.
102
+
103
+ To access the `articles` within a `feed`, you can use:
104
+ - `articles_desc` - sorts the array of articles by newest first.
105
+ - `articles_asc` - sorts the array of articles by oldest first.
106
+
107
+ `articles` contain the following attributes:
108
+ - `title` - a string of the article title.
109
+ - `url` - a string of the article url.
110
+ - `published` - a `Time` instance of the published date. Can also be accessed
111
+ by `formatted_published` to get it as formatted string.
112
+ - `summary` - a string of the article content.
113
+
114
+ When in doubt, check out the implementation of [my
115
+ theme](/lib/templates/themese/simple.html.erb).
116
+
117
+ ## Contributing
118
+
119
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jonathanpike/mako.
120
+
121
+ ## License
122
+
123
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
124
+
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+ require 'rubocop/rake_task'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.pattern = 'test/*_test.rb'
9
+ end
10
+
11
+ RuboCop::RakeTask.new(:rubocop)
12
+
13
+ task default: %i[test rubocop]
data/bin/mako ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/mako'
5
+
6
+ Mako::CLI.start(ARGV)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mako
4
+ class Article
5
+ attr_reader :title, :published, :summary, :url
6
+
7
+ def initialize(args)
8
+ @title = args.fetch(:title, '')
9
+ @published = args.fetch(:published)
10
+ @summary = sanitize(args.fetch(:summary))
11
+ @url = args.fetch(:url)
12
+ end
13
+
14
+ # Converts published Time object to formatted string
15
+ #
16
+ # @return [String]
17
+ def formatted_published
18
+ @published.strftime('%A, %d %B %Y at %I:%M %P')
19
+ end
20
+
21
+ private
22
+
23
+ # @private
24
+ # Removes img tags (if configured) and transforms h1 tags into
25
+ # p tags with the class bold
26
+ #
27
+ # @param [String] html an html document string
28
+ # @return [String] a sanitized html document string
29
+ def sanitize(html)
30
+ doc = Nokogiri::HTML::DocumentFragment.parse(html)
31
+ doc.css('img').each(&:remove) if Mako.config.sanitize_images
32
+ doc.css('h1,h2,h3,h4,h5,h6').each { |n| n.name = 'p'; n.set_attribute('class', 'bold') }
33
+ doc.to_s
34
+ end
35
+ end
36
+ end
data/lib/mako/cli.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'commands/build'
4
+ require_relative 'commands/new'
5
+ require_relative 'commands/version'
6
+
7
+ module Mako
8
+ class CLI
9
+ COMMANDS = %w[build new version].freeze
10
+
11
+ # Takes ARGV and parses the first element (command) to see if it is
12
+ # in the commands array. If not, display help.
13
+ #
14
+ # @param [Array] argv
15
+ def self.start(argv)
16
+ command = argv.shift
17
+ if COMMANDS.include? command
18
+ CLI.invoke(command, argv)
19
+ else
20
+ help = <<~EOS
21
+ Usage:
22
+ mako [subcommand] [path...]
23
+
24
+ Subcommands:
25
+ new Create a new Mako scaffold in PATH. If no PATH provided, defaults to current directory.
26
+ build Build your Mako site. Default: only build HTML.
27
+ version Display the version.
28
+
29
+ Options:
30
+ --with-sass When supplied to build, also generates CSS from SCSS files.
31
+ EOS
32
+ Mako.logger.info help
33
+ end
34
+ end
35
+
36
+ # Calls #perform on the provided command class. When the command is done
37
+ # running, print out any errors that the command had.
38
+ #
39
+ # @param [String] command
40
+ # @param [Array] args the remainder of the ARGV arguments
41
+ def self.invoke(command, args = [])
42
+ Object.const_get("Mako::#{command.capitalize}").perform(args)
43
+ return unless Mako.errors.any?
44
+ Mako.errors.messages.each do |error_msg|
45
+ Mako.logger.warn error_msg
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mako
4
+ class Build
5
+ # Calls #build on Mako::Core. This class stores knowledge of all of the
6
+ # different component classes needed to build the site.
7
+ #
8
+ # @param [Array] args the following are accepted arguments:
9
+ # "--with-sass": builds with SassRenderer
10
+ def self.perform(args)
11
+ subscription_list = load_subscription_list
12
+ if args.include? '--with-sass'
13
+ Mako::Core.new(requester: FeedRequester,
14
+ constructor: FeedConstructor,
15
+ renderers: [HTMLRenderer,
16
+ SassRenderer],
17
+ writer: Writer,
18
+ subscription_list: subscription_list).build
19
+ else
20
+ Mako::Core.new(requester: FeedRequester,
21
+ constructor: FeedConstructor,
22
+ renderers: [HTMLRenderer],
23
+ writer: Writer,
24
+ subscription_list: subscription_list).build
25
+ end
26
+ end
27
+
28
+ def self.load_subscription_list
29
+ path = File.expand_path(Dir.glob('subscriptions.*').first, Dir.pwd)
30
+ Mako::SubscriptionListParser.new(list: path).parse
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module Mako
6
+ class New
7
+ # Copies template files stored in ../lib/templates to specified directory.
8
+ # If the directory specified doesn't exist, it will be created.
9
+ # If no directory is specified, it defaults to the current directory.
10
+ def self.perform(args)
11
+ location = args.empty? ? Dir.pwd : File.expand_path(args.join(' '), Dir.pwd)
12
+ create_dir(File.basename(location)) if location != Dir.pwd && File.directory?(location)
13
+ copy_templates(location)
14
+ Mako.logger.info "Created new Mako installation in #{location}"
15
+ end
16
+
17
+ # @private
18
+ # Copies source templates to specified path.
19
+ def self.copy_templates(path)
20
+ FileUtils.cp_r "#{Mako.config.source_templates}/.", path
21
+ end
22
+
23
+ # If the directory does not exist, create the specified directory.
24
+ def self.create_dir(path)
25
+ FileUtils.mkdir path
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'whenever'
4
+
5
+ module Mako
6
+ class Schedule
7
+ def self.perform(_args)
8
+ Whenever::CommandLine.execute(file: File.expand_path('schedule.rb', Dir.pwd),
9
+ write: true)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../version'
4
+
5
+ module Mako
6
+ class Version
7
+ def self.perform(_args)
8
+ puts "mako #{VERSION}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Mako
6
+ class Configuration
7
+ DEFAULT_CONFIGURATION = { 'source_templates' => File.expand_path('../templates', File.dirname(__FILE__)),
8
+ 'destination' => File.expand_path('site/', Dir.pwd),
9
+ 'theme' => 'simple',
10
+ 'sanitize_images' => true,
11
+ 'config_file' => '' }.freeze
12
+
13
+ include FileOpenUtil
14
+
15
+ # Loads default config file and attempts to merge in any user settings.
16
+ # Creates a new instance of Mako::Configuration.
17
+ #
18
+ # @param [String]
19
+ # @return [Mako::Configuration]
20
+ def self.load(file)
21
+ begin
22
+ user_config_yaml = load_resource(file)
23
+ rescue SystemCallError
24
+ config = DEFAULT_CONFIGURATION
25
+ return new(config)
26
+ end
27
+ user_config = YAML.safe_load(user_config_yaml) || {}
28
+ user_config['config_file'] = file
29
+ config = DEFAULT_CONFIGURATION.merge(user_config)
30
+ new(config)
31
+ end
32
+
33
+ attr_reader :source_templates, :theme, :destination, :sanitize_images,
34
+ :config_file
35
+
36
+ def initialize(args)
37
+ @source_templates = args.fetch('source_templates')
38
+ @theme = args.fetch('theme')
39
+ @destination = args.fetch('destination')
40
+ @sanitize_images = args.fetch('sanitize_images')
41
+ @config_file = args.fetch('config_file')
42
+ end
43
+ end
44
+ end
data/lib/mako/core.rb ADDED
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mako
4
+ class Core
5
+ include ERB::Util
6
+ include ViewHelpers
7
+
8
+ attr_reader :feeds, :requester, :constructor, :renderers, :writer,
9
+ :subscription_list
10
+
11
+ def initialize(args)
12
+ @feeds = []
13
+ @requester = args.fetch(:requester)
14
+ @constructor = args.fetch(:constructor)
15
+ @renderers = args.fetch(:renderers)
16
+ @writer = args.fetch(:writer)
17
+ @subscription_list = args.fetch(:subscription_list)
18
+ end
19
+
20
+ # Gets list of feed_urls, requests each of them and uses the constructor to
21
+ # make Feed and Article objects, then calls to the renderers to render
22
+ # the page and stylesheets.
23
+ def build
24
+ log_configuration_information
25
+
26
+ if subscription_list.empty?
27
+ Mako.logger.warn 'No feeds were found in your subscriptions file. Please add feeds and try again.'
28
+ return
29
+ end
30
+
31
+ log_time do
32
+ request_and_build_feeds
33
+ renderers.each do |renderer|
34
+ renderer_instance = renderer.new(bound: self)
35
+ writer.new(renderer: renderer_instance,
36
+ destination: File.expand_path(renderer_instance.file_path, Mako.config.destination)).write
37
+ end
38
+ end
39
+ end
40
+
41
+ # Returns the Binding of Core for the ERB renderer. Binding encapsulates the
42
+ # execution context for the Core class, and makes all of the variables
43
+ # and methods of Core available to the renderer.
44
+ #
45
+ # @return [Binding]
46
+ def get_binding
47
+ binding
48
+ end
49
+
50
+ private
51
+
52
+ # @private
53
+ # Prints configuration file, source, and destination directory to STDOUT.
54
+ def log_configuration_information
55
+ Mako.logger.info "Configuration File: #{Mako.config.config_file}"
56
+ Mako.logger.info "Theme: #{Mako.config.theme}"
57
+ Mako.logger.info "Destination: #{Mako.config.destination}"
58
+ end
59
+
60
+ # Provides build time logging information and writes it to STDOUT.
61
+ def log_time
62
+ Mako.logger.info 'Generating...'
63
+ start_time = Time.now.to_f
64
+ yield
65
+ generation_time = Time.now.to_f - start_time
66
+ Mako.logger.info "done in #{generation_time} seconds"
67
+ end
68
+
69
+ def request_and_build_feeds
70
+ requesters = subscription_list.map { |feed_url| requester.new(feed_url: feed_url) }
71
+ requesters.each do |feed_request|
72
+ feed_response = feed_request.fetch
73
+ next unless feed_request.ok?
74
+ constructed_feed = constructor.new(feed_data: feed_response.body,
75
+ feed_url: feed_response.feed_url)
76
+ .parse_and_create
77
+ feeds << constructed_feed if constructed_feed
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Numeric
4
+ SECONDS_IN_DAY = 86_400
5
+
6
+ def days
7
+ self * SECONDS_IN_DAY
8
+ end
9
+ alias day days
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ class Time
6
+ def beginning_of_day
7
+ Time.new(year, month, day,
8
+ 0, 0, 0, utc_offset)
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core_ext/numeric'
4
+ require_relative 'core_ext/time'
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mako
4
+ class Errors
5
+ attr_accessor :messages
6
+
7
+ def initialize
8
+ @messages = []
9
+ end
10
+
11
+ # Add an error message to the messages array
12
+ #
13
+ # @param [String]
14
+ def add_error(msg)
15
+ messages << msg
16
+ end
17
+
18
+ # Predicate method to see if there are any error messages
19
+ #
20
+ # @return [Boolean]
21
+ def any?
22
+ messages.count.positive?
23
+ end
24
+ end
25
+ end
data/lib/mako/feed.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mako
4
+ class Feed
5
+ attr_accessor :feed_url, :url, :title, :articles
6
+
7
+ def initialize(args)
8
+ @url = args.fetch(:url)
9
+ @title = args.fetch(:title)
10
+ @articles = []
11
+ end
12
+
13
+ # Returns the articles array sorted by date published ascending
14
+ # (oldest first).
15
+ #
16
+ # @return [Array]
17
+ def articles_asc
18
+ articles.sort_by(&:published)
19
+ end
20
+
21
+ # Returns the articles array sorted by date published descending
22
+ # (newest first).
23
+ #
24
+ # @return [Array]
25
+ def articles_desc
26
+ articles_asc.reverse
27
+ end
28
+ end
29
+ end