wcc-blogs-client 0.3.4 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2fe6e7e2f78e21fcfc1152bfafd4d3803092be05
4
- data.tar.gz: 04e1df2e485fb58dfa0617650906c615dc986aaa
3
+ metadata.gz: d0ee40f5dbfa5ed338bfcfc4ef0a28cf1fac1554
4
+ data.tar.gz: cd4b83add94f175077a9a31062caf2a7a9a0dd64
5
5
  SHA512:
6
- metadata.gz: b67d47ffa6a706f454c4c3bba183c5597a2b3cb538fa18fe23c36a7a89286cd88875b1cfebc75161f838ca5e8bcb3a7314a1de3500702b58648fdb4862f18826
7
- data.tar.gz: 2ec2a66bac4f62eeefee9e24abe1ca113b92ba23014c0d74b44b2fd2b82ebbfc694a6c561ca3b87c833af3f16bf09202c7dce5991a466f57befc53c5d38bc025
6
+ metadata.gz: 1b3cb2d385fe25b0c312e2e742d2245f7f4e6eb1a58bc7a4b140b210bb612cab8fcb396c16afbbedfb36555f9c6ed5e73387854b5c477b8b7af3c28f99d2b9b1
7
+ data.tar.gz: 2efbf1c351083e6460b1e09db3a383f8e6c6c48ac9ad25720e6a3982823545e11d5080ab77d3772da86e2150e91b29ef3ba91e7dcb2ec302d0d1339b0edcd3d7
data/.rubocop.yml CHANGED
@@ -14,6 +14,9 @@ Style/ClassAndModuleChildren:
14
14
  Style/Documentation:
15
15
  Enabled: false
16
16
 
17
+ Style/DoubleNegation:
18
+ Enabled: false
19
+
17
20
  Style/MultilineBlockChain:
18
21
  Exclude:
19
22
  - 'spec/**/*'
data/Guardfile ADDED
@@ -0,0 +1,32 @@
1
+ directories %w[lib spec]
2
+
3
+ group :red_green_refactor, halt_on_fail: true do
4
+ guard :rspec, cmd: 'bundle exec rspec --order rand', all_on_start: false do
5
+ require 'guard/rspec/dsl'
6
+ dsl = Guard::RSpec::Dsl.new(self)
7
+
8
+ # RSpec files
9
+ rspec = dsl.rspec
10
+ watch(rspec.spec_helper) { rspec.spec_dir }
11
+ watch(rspec.spec_support) { rspec.spec_dir }
12
+ watch(rspec.spec_files)
13
+
14
+ # Ruby files
15
+ ruby = dsl.ruby
16
+ dsl.watch_spec_files_for(ruby.lib_files)
17
+ end
18
+
19
+ guard :rubocop, cli: ['--display-cop-names'] do
20
+ watch(/.+\.rb$/)
21
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
22
+ end
23
+ end
24
+
25
+ group :autofix do
26
+ guard :rubocop, all_on_start: false, cli: ['--auto-correct', '--display-cop-names'] do
27
+ watch(/.+\.rb$/)
28
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
+ end
30
+ end
31
+
32
+ scope group: :red_green_refactor
data/lib/wcc/blogs.rb CHANGED
@@ -9,50 +9,14 @@ require 'wcc/blogs/errors'
9
9
 
10
10
  require 'wcc/blogs/metadata'
11
11
  require 'wcc/blogs/post'
12
+ require 'wcc/blogs/post_summary'
12
13
 
13
14
  module WCC::Blogs
14
15
  class << self
15
- def configure
16
- yield config
17
-
18
- @client = WCC::Blogs::Client.new(config: config)
19
-
20
- config
21
- end
16
+ attr_writer :client
22
17
 
23
18
  def client
24
19
  @client || raise(WCC::Blogs::NotConfiguredException, 'Not configured')
25
20
  end
26
-
27
- def config
28
- Config.instance
29
- end
30
- end
31
-
32
- class Config
33
- include Singleton
34
-
35
- attr_accessor :publishing_target
36
- attr_writer :base_url, :cache, :query_url
37
-
38
- def base_url
39
- @base_url || 'https://wcc-papyrus.herokuapp.com'
40
- end
41
-
42
- def cache
43
- @cache || NilCache.new
44
- end
45
-
46
- def query_url
47
- @query_url || 'http://papyrus.wcc/graphql'
48
- end
49
-
50
- class NilCache
51
- def clear; end
52
-
53
- def fetch(*_args)
54
- yield
55
- end
56
- end
57
21
  end
58
22
  end
@@ -1,42 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday'
4
+ require 'faraday-http-cache'
5
+ require_relative 'client/response'
6
+
3
7
  module WCC::Blogs
4
8
  class Client
5
- def initialize(config:)
6
- @config = config
9
+ attr_reader :base_url, :publishing_target
10
+
11
+ def initialize(publishing_target:, **options)
12
+ @publishing_target = publishing_target
13
+ @base_url = options[:base_url] || 'https://di0v2frwtdqnv.cloudfront.net'
14
+ @connection = options[:connection] || default_connection
15
+ @query_defaults = options[:query_defaults] || {}
16
+ end
17
+
18
+ # performs an HTTP GET request to the specified path within the configured
19
+ # space and environment. Query parameters are merged with the defaults and
20
+ # appended to the request.
21
+ def get(path, query = {})
22
+ url = URI.join(@base_url, path)
23
+
24
+ Response.new(self,
25
+ { url: url, query: query },
26
+ get_http(url, query))
27
+ end
28
+
29
+ def blog_post(slug, digest: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
30
+ path = '/blog/' + slug.sub(%r{^/}, '')
31
+ path += '/' + digest if digest
32
+ path += '.json'
33
+
34
+ get(path).tap do |resp|
35
+ resp.assert_ok!
36
+ next if resp.raw['publishingTargets'].any? { |t| t['key'] == publishing_target }
37
+
38
+ raise WCC::Blogs::NotFoundException,
39
+ "Blog post '#{slug}' is not published to #{publishing_target}"
40
+ end
41
+ rescue WCC::Blogs::Client::NotFoundError => e
42
+ raise WCC::Blogs::NotFoundException, e.message
7
43
  end
8
44
 
9
- REDIRECT_HANDLER =
10
- ->(resp) {
11
- location = resp.headers['Location']
12
- digest = File.basename(location).gsub(/\.(fragment|json)$/, '')
45
+ def list
46
+ path = '/targets/' + publishing_target + '.json'
47
+ get(path).assert_ok!
48
+ end
49
+
50
+ private
51
+
52
+ def get_http(url, query, headers = {})
53
+ q = @query_defaults.merge(query || {})
13
54
 
14
- @config.cache.fetch(['BlogPost', digest, File.extname(location)]) do
15
- get(location, resp.effective_url)
55
+ loop do
56
+ resp = @connection.get(url, q, headers)
57
+
58
+ if [301, 302, 307].include?(resp.status)
59
+ url = URI.join(@base_url, resp.headers['Location'])
60
+ next
16
61
  end
17
- }
18
-
19
- RESPONSE_HANDLERS = {
20
- 404 => ->(resp) {
21
- raise WCC::Blogs::NotFoundException, "Blog post '#{resp.effective_url}' does not exist"
22
- },
23
- 200 => ->(resp) {
24
- resp.body
25
- },
26
- 301 => REDIRECT_HANDLER,
27
- 302 => REDIRECT_HANDLER,
28
- }.freeze
29
-
30
- def get(path, base = @config.base_url)
31
- url = URI.join(base, path)
32
-
33
- resp = Typhoeus.get(url, followlocation: false)
34
-
35
- if handler = RESPONSE_HANDLERS[resp.code]
36
- return instance_exec(resp, &handler)
62
+
63
+ return resp
37
64
  end
65
+ end
38
66
 
39
- raise WCC::Blogs::ApiException, "#{resp.return_code}: #{resp.effective_url}"
67
+ def default_connection
68
+ ::Faraday.new do |faraday|
69
+ faraday.use :http_cache,
70
+ shared_cache: false,
71
+ logger: (Rails.logger if defined?(Rails)),
72
+ store: default_cache_store,
73
+ serializer: Marshal
74
+ faraday.response :logger, (Rails.logger if defined?(Rails)), headers: false, bodies: false
75
+ faraday.adapter :typhoeus
76
+ end
77
+ end
78
+
79
+ def default_cache_store
80
+ if defined?(Rails)
81
+ Rails.cache
82
+ elsif defined?(ActiveSupport::Cache)
83
+ ActiveSupport::Cache.lookup_store(:memory, size: 64.megabytes)
84
+ end
40
85
  end
41
86
  end
42
87
  end
@@ -0,0 +1,106 @@
1
+ require 'forwardable'
2
+
3
+ module WCC::Blogs
4
+ class Client
5
+ Response = Struct.new(:client, :request, :raw_response) do # rubocop:disable Metrics/BlockLength
6
+ extend Forwardable
7
+
8
+ def_delegators :raw_response, :status, :headers
9
+ alias_method :code, :status
10
+
11
+ def body
12
+ @body ||= raw_response.body.to_s
13
+ end
14
+
15
+ def raw
16
+ @raw ||= JSON.parse(body)
17
+ end
18
+ alias_method :to_json, :raw
19
+
20
+ def next_page_url
21
+ raw.dig('_links', 'previous')
22
+ end
23
+
24
+ def next_page?
25
+ !!next_page_url
26
+ end
27
+
28
+ def next_page
29
+ return unless next_page_url
30
+ return @next_page if @next_page
31
+
32
+ np = client.get(next_page_url, request[:query])
33
+ @next_page = np.assert_ok!
34
+ end
35
+
36
+ def assert_ok!
37
+ return self if status >= 200 && status < 300
38
+
39
+ raise ApiError[status], self
40
+ end
41
+
42
+ def each_page(&block) # rubocop:disable Metrics/MethodLength
43
+ raise ArgumentError, 'Not a collection response' unless page_items
44
+
45
+ ret =
46
+ Enumerator.new do |y|
47
+ y << self
48
+
49
+ if next_page?
50
+ next_page.each_page.each do |page|
51
+ y << page
52
+ end
53
+ end
54
+ end
55
+
56
+ if block_given?
57
+ ret.map(&block)
58
+ else
59
+ ret.lazy
60
+ end
61
+ end
62
+
63
+ def items
64
+ each_page.flat_map(&:page_items)
65
+ end
66
+
67
+ def page_items
68
+ raw['items']
69
+ end
70
+
71
+ def count
72
+ total
73
+ end
74
+
75
+ def first
76
+ raise ArgumentError, 'Not a collection response' unless page_items
77
+
78
+ page_items.first
79
+ end
80
+ end
81
+
82
+ class ApiError < StandardError
83
+ attr_reader :response
84
+
85
+ def self.[](code)
86
+ case code
87
+ when 404
88
+ NotFoundException
89
+ else
90
+ ApiError
91
+ end
92
+ end
93
+
94
+ def initialize(response, message = nil)
95
+ @response = response
96
+ super(message || "An unexpected error occurred: #{response.status}")
97
+ end
98
+ end
99
+
100
+ class NotFoundError < ApiError
101
+ def initialize(response)
102
+ super(response, "Blog post '#{response.effective_url}' does not exist")
103
+ end
104
+ end
105
+ end
106
+ end
@@ -6,16 +6,18 @@ module WCC::Blogs
6
6
  require 'time'
7
7
 
8
8
  def self.find(slug)
9
- path = '/blog/' + slug.sub(%r{^/}, '') + '.json'
10
- new(JSON.parse(WCC::Blogs.client.get(path))).tap do |blog|
11
- raise WCC::Blogs::NotFoundException, "Blog post '#{slug}' is not published" unless blog.published?
12
- end
9
+ new(WCC::Blogs.client.blog_post(slug).raw, client: WCC::Blogs.client)
10
+ end
11
+
12
+ def self.find_all
13
+ PostSummary.find_all.map(&:full_post)
13
14
  end
14
15
 
15
16
  attr_reader :raw
16
17
 
17
- def initialize(raw)
18
+ def initialize(raw, client: WCC::Blogs.client)
18
19
  @raw = raw
20
+ @client = client
19
21
  end
20
22
 
21
23
  def author_full_name
@@ -25,12 +27,9 @@ module WCC::Blogs
25
27
  end
26
28
 
27
29
  def html
28
- @html ||=
29
- WCC::Blogs.config.cache.fetch(['BlogPost', fragment_path]) do
30
- WCC::Blogs.client
31
- .get(fragment_path, host || WCC::Blogs.config.base_url)
32
- .force_encoding('UTF-8')
33
- end
30
+ @html ||= @client.get(URI.join(host, fragment_path).to_s)
31
+ .body
32
+ .force_encoding('UTF-8')
34
33
  end
35
34
 
36
35
  def metadata
@@ -38,9 +37,7 @@ module WCC::Blogs
38
37
  end
39
38
 
40
39
  def published?
41
- return false if publishing_targets.empty?
42
-
43
- publishing_targets.any? { |t| t['key'] == WCC::Blogs.config.publishing_target }
40
+ true
44
41
  end
45
42
 
46
43
  def time_to_read
@@ -0,0 +1,57 @@
1
+ module WCC::Blogs
2
+ class PostSummary
3
+ extend WCC::Blogs::Utils
4
+
5
+ def self.find_all
6
+ WCC::Blogs.client.list.items.map do |summary|
7
+ new(summary, client: WCC::Blogs.client)
8
+ end
9
+ end
10
+
11
+ attr_reader :raw
12
+
13
+ def initialize(raw, client: WCC::Blogs.client)
14
+ @raw = raw
15
+ @client = client
16
+ end
17
+
18
+ def full_post
19
+ Post.new(@client.blog_post(slug, digest: digest).raw)
20
+ end
21
+
22
+ define_camelcase_alias(
23
+ 'id',
24
+ 'title',
25
+ 'subtitle',
26
+ 'slug',
27
+ 'path',
28
+ 'digest',
29
+ 'fragment_path'
30
+ ) do |camelcase|
31
+ raw[camelcase]
32
+ end
33
+
34
+ define_camelcase_alias(
35
+ 'date',
36
+ 'updated_at'
37
+ ) do |camelcase|
38
+ value = raw[camelcase]
39
+
40
+ Time.parse(value) if value && value.length
41
+ end
42
+
43
+ define_camelcase_alias(
44
+ 'thumbnail_image'
45
+ ) do |camelcase|
46
+ OpenStruct.new(raw[camelcase]) if raw[camelcase]
47
+ end
48
+
49
+ define_camelcase_alias('publishing_targets') do |camelcase|
50
+ targets = raw[camelcase] || []
51
+
52
+ targets.map { |val| OpenStruct.new(val) if val }
53
+ end
54
+
55
+ alias cache_key digest
56
+ end
57
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Blogs
5
- VERSION = '0.3.4'.freeze
5
+ VERSION = '0.4.0'.freeze
6
6
  end
7
7
  end
@@ -24,12 +24,17 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.require_paths = ['lib']
26
26
 
27
- spec.add_runtime_dependency 'typhoeus', '~> 1.3'
27
+ spec.add_runtime_dependency 'faraday', '~> 0.15.4'
28
+ spec.add_runtime_dependency 'faraday-http-cache', '~> 1.3'
28
29
  spec.add_runtime_dependency 'wcc-base'
29
30
 
31
+ spec.add_development_dependency 'guard', '~> 2.15'
32
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
33
+ spec.add_development_dependency 'guard-rubocop', '~> 1.3'
30
34
  spec.add_development_dependency 'rake', '~> 12.3'
31
35
  spec.add_development_dependency 'rspec', '~> 3.0'
32
36
  spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3.0'
33
37
  spec.add_development_dependency 'rubocop', '~> 0.60.0'
38
+ spec.add_development_dependency 'typhoeus'
34
39
  spec.add_development_dependency 'webmock', '~> 3.0'
35
40
  end
metadata CHANGED
@@ -1,17 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wcc-blogs-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watermark Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-04 00:00:00.000000000 Z
11
+ date: 2019-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: typhoeus
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.15.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.15.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-http-cache
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
@@ -38,6 +52,48 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.15'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
41
97
  - !ruby/object:Gem::Dependency
42
98
  name: rake
43
99
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +150,20 @@ dependencies:
94
150
  - - "~>"
95
151
  - !ruby/object:Gem::Version
96
152
  version: 0.60.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: typhoeus
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
97
167
  - !ruby/object:Gem::Dependency
98
168
  name: webmock
99
169
  requirement: !ruby/object:Gem::Requirement
@@ -119,13 +189,16 @@ files:
119
189
  - ".rubocop.yml"
120
190
  - ".rubocop_todo.yml"
121
191
  - Gemfile
192
+ - Guardfile
122
193
  - README.md
123
194
  - Rakefile
124
195
  - lib/wcc/blogs.rb
125
196
  - lib/wcc/blogs/client.rb
197
+ - lib/wcc/blogs/client/response.rb
126
198
  - lib/wcc/blogs/errors.rb
127
199
  - lib/wcc/blogs/metadata.rb
128
200
  - lib/wcc/blogs/post.rb
201
+ - lib/wcc/blogs/post_summary.rb
129
202
  - lib/wcc/blogs/utils.rb
130
203
  - lib/wcc/blogs/version.rb
131
204
  - wcc-blogs-client.gemspec
@@ -149,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
222
  version: '0'
150
223
  requirements: []
151
224
  rubyforge_project:
152
- rubygems_version: 2.5.2
225
+ rubygems_version: 2.6.11
153
226
  signing_key:
154
227
  specification_version: 4
155
228
  summary: ''