diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 976c845..e458a43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,13 @@ jobs: bundler-cache: true - run: bundle exec rspec - run: gem build millionsend.gemspec + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 + with: + ruby-version: "3.3" + bundler-cache: true + - run: bundle exec rubocop diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..8dc7bee --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,52 @@ +AllCops: + TargetRubyVersion: 3.0 + NewCops: enable + SuggestExtensions: false + +Gemspec/DevelopmentDependencies: + EnforcedStyle: gemspec + +Layout/FirstArgumentIndentation: + EnforcedStyle: consistent + +Layout/FirstHashElementIndentation: + EnforcedStyle: consistent + +Layout/HashAlignment: + EnforcedHashRocketStyle: [key, table] + +# Specs inline whole URLs and JSON bodies; wrapping them hurts more than it helps. +Layout/LineLength: + Enabled: false + +# Size thresholds would demand refactors rather than catch bugs. +Metrics: + Enabled: false + +Style/Alias: + EnforcedStyle: prefer_alias_method + +# ENV[] returning nil for an unset variable is the intended fallback. +Style/FetchEnvVar: + Enabled: false + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/TrailingCommaInArrayLiteral: + EnforcedStyleForMultiline: diff_comma + +Style/TrailingCommaInHashLiteral: + EnforcedStyleForMultiline: diff_comma + +# These rewrites change behavior for some caller inputs: Hash#except also drops the string +# keys of a HashWithIndifferentAccess, compact keeps [key, nil] pairs in an array, and +# freezing USER_AGENT breaks callers that append to it. +Style/CollectionCompact: + Enabled: false + +Style/HashExcept: + Enabled: false + +Style/MutableConstant: + Enabled: false diff --git a/lib/millionsend/contacts.rb b/lib/millionsend/contacts.rb index 481c76f..54c2a5c 100644 --- a/lib/millionsend/contacts.rb +++ b/lib/millionsend/contacts.rb @@ -20,7 +20,7 @@ def get(id_or_email) # everything else is the body. A nil value clears a field; omit a key to # leave it unchanged. def update(params) - body = params.reject { |k, _| [:id, :email].include?(k) } + body = params.reject { |k, _| %i[id email].include?(k) } Millionsend::Request.new(method: :patch, path: member_path(params), body: body).perform end diff --git a/lib/millionsend/request.rb b/lib/millionsend/request.rb index bbe5a5c..7654f40 100644 --- a/lib/millionsend/request.rb +++ b/lib/millionsend/request.rb @@ -23,7 +23,7 @@ class Request # (WebMock::NetConnectNotAllowedError) is not swallowed as a transport error. TRANSPORT_ERRORS = [ Timeout::Error, SocketError, SystemCallError, IOError, - Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError + Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError, ].freeze def initialize(method:, path:, body: nil, query: nil, idempotency_key: nil, batch_validation: nil) diff --git a/millionsend.gemspec b/millionsend.gemspec index 396c189..b7c5f47 100644 --- a/millionsend.gemspec +++ b/millionsend.gemspec @@ -25,5 +25,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.12" + spec.add_development_dependency "rubocop", "1.91.0" spec.add_development_dependency "webmock", "~> 3.19" end diff --git a/spec/resources_spec.rb b/spec/resources_spec.rb index c099cc2..dba2ee8 100644 --- a/spec/resources_spec.rb +++ b/spec/resources_spec.rb @@ -242,7 +242,7 @@ def list_ok it "list joins include: into one comma-separated query value and omits it when unset" do stub_request(:get, "https://api.test/contacts") .with(query: { "limit" => "100", "include" => "properties,topics" }).to_return(list_ok) - Millionsend::Contacts.list(limit: 100, include: ["properties", "topics"]) + Millionsend::Contacts.list(limit: 100, include: %w[properties topics]) expect(WebMock).to have_requested(:get, "https://api.test/contacts") .with(query: { "limit" => "100", "include" => "properties,topics" }) @@ -361,11 +361,11 @@ def list_ok it "Batch.remove posts ids or emails to /contacts/batch/remove" do stub_request(:post, "https://api.test/contacts/batch/remove") .to_return(ok('{"data":[{"object":"contact","contact":"c1","deleted":true}]}')) - res = Millionsend::Contacts::Batch.remove({ ids: ["c1", "c2"] }) + res = Millionsend::Contacts::Batch.remove({ ids: %w[c1 c2] }) Millionsend::Contacts::Batch.remove({ emails: ["a@x.dev"] }) Millionsend::Contacts::Batch.remove(emails: ["b@x.dev"], erase: true) expect(WebMock).to have_requested(:post, "https://api.test/contacts/batch/remove") - .with(body: { "ids" => ["c1", "c2"] }) + .with(body: { "ids" => %w[c1 c2] }) expect(WebMock).to have_requested(:post, "https://api.test/contacts/batch/remove") .with(body: { "emails" => ["a@x.dev"] }) expect(WebMock).to have_requested(:post, "https://api.test/contacts/batch/remove") @@ -383,12 +383,12 @@ def list_ok )) res = Millionsend::Contacts::Batch.get( ["c1", "ghost@x.dev", { email: "b@x.dev" }, { contact_id: "c4" }, { id: "c5" }], - include: ["properties", "topics"] + include: %w[properties topics] ) expect(WebMock).to have_requested(:post, "https://api.test/contacts/batch/get").with( body: { "contacts" => [{ "id" => "c1" }, { "email" => "ghost@x.dev" }, { "email" => "b@x.dev" }, { "id" => "c4" }, { "id" => "c5" }], - "include" => ["properties", "topics"] + "include" => %w[properties topics], } ) expect(res[:data].first).to include(object: "contact", id: "c1", email: "a@x.dev", unsubscribed: false) @@ -537,7 +537,7 @@ def list_ok expect(WebMock).to have_requested(:post, "https://api.test/segments").with( body: { "name" => "Active", - "filter" => { "match" => "all", "conditions" => [{ "field" => "email", "op" => "is_set", "value" => nil }] } + "filter" => { "match" => "all", "conditions" => [{ "field" => "email", "op" => "is_set", "value" => nil }] }, } ) @@ -607,11 +607,11 @@ def list_ok stub_request(:post, "https://api.test/suppressions/batch/remove").to_return(ok('{"data":[]}')) Millionsend::Suppressions::Batch.remove({ emails: ["a@x.dev"] }) - Millionsend::Suppressions::Batch.remove({ ids: ["sup1", "sup2"] }) + Millionsend::Suppressions::Batch.remove({ ids: %w[sup1 sup2] }) expect(WebMock).to have_requested(:post, "https://api.test/suppressions/batch/remove") .with(body: { "emails" => ["a@x.dev"] }) expect(WebMock).to have_requested(:post, "https://api.test/suppressions/batch/remove") - .with(body: { "ids" => ["sup1", "sup2"] }) + .with(body: { "ids" => %w[sup1 sup2] }) end end