mako_rss 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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