Skip to content

Commit c38068f

Browse files
authored
add rubocop (#3)
* add rubocop * fix cops
1 parent 70bffd3 commit c38068f

21 files changed

Lines changed: 224 additions & 110 deletions

.github/workflows/ruby-tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ jobs:
2727

2828
- name: Run tests
2929
run: bundle exec rspec
30+
31+
- name: Run rubocop
32+
run: bundle exec rubocop

.rubocop.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
inherit_from: .rubocop_todo.yml
2+
3+
AllCops:
4+
NewCops: disable
5+
SuggestExtensions: false
6+
TargetRubyVersion: 3.0
7+
8+
Layout/LineLength:
9+
Max: 199
10+
11+
Metrics/BlockLength:
12+
Max: 63
13+
14+
Metrics/ClassLength:
15+
Max: 200
16+
17+
Metrics/CyclomaticComplexity:
18+
Max: 8
19+
20+
Metrics/MethodLength:
21+
Max: 20
22+
23+
Style/ClassVars:
24+
Exclude:
25+
- 'lib/earl/scraper.rb'
26+
27+
Style/Documentation:
28+
Exclude:
29+
- 'spec/**/*'
30+
31+
Style/ClassAndModuleChildren:
32+
EnforcedStyleForClasses: compact
33+
34+
Layout/FirstHashElementIndentation:
35+
EnforcedStyle: consistent

.rubocop_todo.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This configuration was generated by
2+
# `rubocop --auto-gen-config`
3+
# on 2025-07-29 10:15:32 UTC using RuboCop version 1.79.0.
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: 7
10+
# This cop supports unsafe autocorrection (--autocorrect-all).
11+
# Configuration parameters: AllowSafeAssignment.
12+
Lint/AssignmentInCondition:
13+
Exclude:
14+
- 'lib/earl/earl.rb'
15+
- 'lib/earl/scraper.rb'
16+
17+
# Offense count: 2
18+
# Configuration parameters: AllowedMethods.
19+
# AllowedMethods: enums
20+
Lint/ConstantDefinitionInBlock:
21+
Exclude:
22+
- 'spec/unit/earl/scraper_spec.rb'

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ gemspec
88
# development dependencies
99
gem 'bundler', '>= 2.2.33'
1010
gem 'guard-rspec'
11+
gem 'guard-rubocop'
1112
gem 'rake'
1213
gem 'rspec'
14+
gem 'rubocop', require: false
1315
gem 'vcr'
1416
gem 'webmock'

Guardfile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
# frozen_string_literal: true
2+
13
# A sample Guardfile
24
# More info at https://github.com/guard/guard#readme
35

46
guard :rspec, cmd: 'bundle exec rspec' do
57
watch(%r{^spec/.+_spec\.rb$})
6-
watch('spec/spec_helper.rb') { "spec" }
8+
watch('spec/spec_helper.rb') { 'spec' }
79
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
810
end
11+
12+
guard :rubocop, cli: ['--display-cop-names'] do
13+
watch(/.+\.rb$/)
14+
watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
15+
end

Rakefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ RSpec::Core::RakeTask.new(:spec)
77

88
task default: :spec
99

10-
desc "Open an irb session preloaded with this library"
10+
desc 'Open an irb session preloaded with this library'
1111
task :console do
12-
sh "irb -I lib -r earl.rb"
12+
sh 'irb -I lib -r earl.rb'
1313
end

earl.gemspec

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
# frozen_string_literal: true
22

3+
require 'English'
34
lib = File.expand_path('lib', __dir__)
45
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
56
require 'earl/version'
67

78
Gem::Specification.new do |gem|
8-
gem.authors = ["teejayvanslyke", "Paul Gallagher"]
9-
gem.email = ["tj@elctech.com", "gallagher.paul@gmail.com"]
9+
gem.authors = ['teejayvanslyke', 'Paul Gallagher']
10+
gem.email = ['tj@elctech.com', 'gallagher.paul@gmail.com']
1011
gem.description = 'URL metadata API'
1112
gem.summary = 'URL metadata API for scraping titles, descriptions, images, and videos from URLs'
1213
gem.homepage = 'https://github.com/evendis/earl'
1314

14-
gem.files = `git ls-files`.split($\)
15-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
16+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
1617
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
1718
gem.name = 'earl'
18-
gem.require_paths = ["lib"]
19+
gem.require_paths = ['lib']
1920
gem.version = Earl::VERSION
2021
gem.license = 'MIT'
2122

lib/earl.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require 'nokogiri'
24
require 'open-uri'
35

lib/earl/earl.rb

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
# frozen_string_literal: true
2+
13
require 'open-uri'
24
require 'oembedr'
35

6+
# Earl is a class that represents a URL and provides methods to fetch metadata about the page
47
class Earl
5-
attr_accessor :url, :options, :oembed
8+
attr_accessor :url, :options
9+
attr_writer :oembed
610

7-
def initialize(url, options={})
11+
def initialize(url, options = {})
812
@url = url
913
@options = options
1014
end
@@ -18,7 +22,7 @@ def uri
1822
end
1923

2024
def uri_response
21-
@uri_response ||= URI.open(uri)
25+
@uri_response ||= uri.open
2226
end
2327

2428
# Returns
@@ -35,13 +39,13 @@ def uri_response_attribute(name)
3539
when :headers
3640
uri_response_attribute(:meta)
3741
else
38-
uri_response && uri_response.respond_to?(name) && uri_response.send(name)
42+
uri_response.respond_to?(name) && uri_response.send(name)
3943
end
4044
end
4145
protected :uri_response_attribute
4246

4347
def uri_response_attributes
44-
[:content_type,:base_url,:charset,:content_encoding,:headers]
48+
%i[content_type base_url charset content_encoding headers]
4549
end
4650
protected :uri_response_attributes
4751

@@ -50,7 +54,7 @@ def scraper
5054
end
5155

5256
def response
53-
scraper && scraper.response
57+
scraper&.response
5458
end
5559

5660
# Returns a hash of link meta data, including:
@@ -59,23 +63,28 @@ def response
5963
def metadata
6064
data = oembed || {}
6165
attributes.each do |attribute|
62-
if attribute_value = self.send(attribute)
66+
if attribute_value = send(attribute)
6367
data[attribute] ||= attribute_value
6468
end
6569
end
6670
data
6771
end
6872

73+
def respond_to_missing?(name, include_private)
74+
uri_response_attributes.include?(name) || scraper&.attribute?(name) || super
75+
end
76+
6977
# Dispatch missing methods if a match for:
7078
# - uri_response_attributes
7179
# - scraper attributes
7280
def method_missing(method, *args)
7381
if uri_response_attributes.include?(method)
74-
return uri_response_attribute(method)
75-
elsif scraper && scraper.has_attribute?(method)
76-
return scraper.attribute(method)
82+
uri_response_attribute(method)
83+
elsif scraper&.attribute?(method)
84+
scraper.attribute(method)
85+
else
86+
super
7787
end
78-
super
7988
end
8089

8190
# Returns a full array of attributes available for the link
@@ -85,7 +94,7 @@ def attributes
8594

8695
# Returns the options to be used for oembed
8796
def oembed_options
88-
{ :maxwidth => "560", :maxheight => "315" }.merge(options[:oembed]||{})
97+
{ maxwidth: '560', maxheight: '315' }.merge(options[:oembed] || {})
8998
end
9099

91100
# Returns the oembed meta data hash for the URL (or nil if not defined/available) e.g.
@@ -108,21 +117,27 @@ def oembed_options
108117
#
109118
# +options+ defines a custom oembed options hash and will cause a re-fetch of the oembed metadata
110119
# TODO: Oembedr is outdated and not longer works with most/all providers
111-
def oembed(options=nil)
120+
def oembed(options = nil)
112121
if options # use custom options, refetch oembed metadata
113122
@options[:oembed] = options
114123
@oembed = nil
115124
end
116-
begin
117-
@oembed ||= if h = Oembedr.fetch(base_url, :params => oembed_options).body
125+
@oembed ||= begin
126+
h = Oembedr.fetch(base_url, params: oembed_options).body
127+
if h
118128
h.keys.each do |key| # symbolize_keys!
119-
h[(key.to_sym rescue key) || key] = h.delete(key)
129+
new_key = begin
130+
key.to_sym
131+
rescue StandardError
132+
key
133+
end
134+
h[new_key] = h.delete(key)
120135
end
121136
h
122137
end
123-
rescue
138+
rescue StandardError
139+
nil
124140
end
125-
@oembed
126141
end
127142

128143
# Returns the oembed code for the url (or nil if not defined/available)
@@ -131,7 +146,7 @@ def oembed_html
131146
end
132147

133148
# Returns true if there is an ATOM or RSS feed associated with this URL.
134-
def has_feed?
149+
def feed?
135150
!feed.nil?
136151
end
137152

lib/earl/scraper.rb

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
class Earl::Scraper
1+
# frozen_string_literal: true
22

3+
# Base class for nokogiri page scraping
4+
class Earl::Scraper
35
class << self
46
@@registry = []
5-
attr_reader :regexp
6-
attr_reader :attributes
7+
attr_reader :regexp, :attributes
78

89
def match(regexp)
910
@regexp = regexp
@@ -17,9 +18,9 @@ def define_attribute(name, &block)
1718

1819
def for(url, earl_source)
1920
@@registry.each do |klass|
20-
return klass.new(url,earl_source) if klass.regexp.match(url)
21+
return klass.new(url, earl_source) if klass.regexp.match(url)
2122
end
22-
Earl::Scraper.new(url,earl_source)
23+
Earl::Scraper.new(url, earl_source)
2324
end
2425

2526
def register(scraper_klass)
@@ -40,9 +41,9 @@ def response
4041
end
4142

4243
def attribute(name)
43-
return unless has_attribute?(name)
44+
return unless attribute?(name)
4445

45-
self.attributes[name].call(response)
46+
attributes[name].call(response)
4647
end
4748

4849
def attributes
@@ -53,10 +54,10 @@ def attributes
5354
end
5455
end
5556

56-
def has_attribute?(name)
57+
def attribute?(name)
5758
return false unless self.class.attributes
5859

59-
self.attributes.has_key?(name)
60+
attributes.key?(name)
6061
end
6162

6263
define_attribute :title do |doc|

0 commit comments

Comments
 (0)