rubocop-solidus 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3f5300b213565a1a439d50b1e8a10f1fdb85559dd82c22590d583afaee88cd80
4
+ data.tar.gz: f449296bd8ce1235e5760eb0284ca29b33634ed6fc095230f4837bb60a72a28f
5
+ SHA512:
6
+ metadata.gz: 3c2704f57083a3bfdb599b5e8fd684271703a541a1e636307955a798c8ddcb3db79fa61698840ecc76b90917e6063f24c0582fb45fda1403b605abb4f99c28be
7
+ data.tar.gz: 40fe450071e0b1bfcf88b989090d1fdd4cd6e2837eadab97cdc88bc618124453050c1fce6a9118978cf32683d73fcc6655bed0ddc4878d141004f8a8f80c4bc1
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.4
4
+
5
+ Naming/FileName:
6
+ Exclude:
7
+ - lib/rubocop-solidus.rb
8
+
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - 'Rakefile'
12
+ - '**/*.rake'
13
+ - 'spec/**/*.rb'
14
+ - '*.gemspec'
15
+
16
+ Metrics/ClassLength:
17
+ Exclude:
18
+ - 'tasks/**/*.rb'
19
+
20
+ # Prevents Ruby 3.1 incompatibility error. You can enable this cop when Ruby 2.4 support is dropped.
21
+ # See https://github.com/rubocop/rubocop/issues/10258
22
+ Layout/BlockAlignment:
23
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## master (unreleased)
2
+
3
+ ## 0.1.0 (2023-07-10)
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in rubocop-solidus.gemspec
6
+ gemspec
7
+
8
+ gem 'bump'
9
+ gem 'pry'
10
+ gem 'rake'
11
+ gem 'rspec'
12
+ gem 'rubocop'
data/Gemfile.lock ADDED
@@ -0,0 +1,70 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rubocop-solidus (0.1.0)
5
+ rubocop
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ bump (0.10.0)
12
+ coderay (1.1.3)
13
+ diff-lcs (1.5.0)
14
+ method_source (1.0.0)
15
+ parallel (1.20.1)
16
+ parser (3.2.2.3)
17
+ ast (~> 2.4.1)
18
+ racc
19
+ pry (0.14.2)
20
+ coderay (~> 1.1)
21
+ method_source (~> 1.0)
22
+ racc (1.5.2)
23
+ rainbow (3.1.1)
24
+ rake (13.0.6)
25
+ regexp_parser (2.8.1)
26
+ rexml (3.2.5)
27
+ rspec (3.12.0)
28
+ rspec-core (~> 3.12.0)
29
+ rspec-expectations (~> 3.12.0)
30
+ rspec-mocks (~> 3.12.0)
31
+ rspec-core (3.12.2)
32
+ rspec-support (~> 3.12.0)
33
+ rspec-expectations (3.12.3)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.12.0)
36
+ rspec-mocks (3.12.6)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-support (3.12.1)
40
+ rubocop (1.12.1)
41
+ parallel (~> 1.10)
42
+ parser (>= 3.0.0.0)
43
+ rainbow (>= 2.2.2, < 4.0)
44
+ regexp_parser (>= 1.8, < 3.0)
45
+ rexml
46
+ rubocop-ast (>= 1.2.0, < 2.0)
47
+ ruby-progressbar (~> 1.7)
48
+ unicode-display_width (>= 1.4.0, < 3.0)
49
+ rubocop-ast (1.4.1)
50
+ parser (>= 2.7.1.5)
51
+ ruby-progressbar (1.13.0)
52
+ unicode-display_width (2.4.2)
53
+
54
+ PLATFORMS
55
+ -darwin-22
56
+ arm64-darwin-21
57
+ arm64-darwin-22
58
+ x86_64-darwin-21
59
+ x86_64-linux
60
+
61
+ DEPENDENCIES
62
+ bump
63
+ pry
64
+ rake
65
+ rspec
66
+ rubocop
67
+ rubocop-solidus!
68
+
69
+ BUNDLED WITH
70
+ 2.3.26
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 piyushswain
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,70 @@
1
+ # Rubocop::Solidus
2
+
3
+ Automatic Solidus code style checking tool.
4
+ A RuboCop extension focused on enforcing Solidus best practices and coding conventions.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'rubocop-solidus', github: 'nebulab/rubocop-solidus', require: false
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle install
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rubocop-solidus
21
+
22
+ ## Usage
23
+
24
+ To Start using this extension in your application add the following lines to your `.rubocop.yml` file
25
+
26
+ ```yaml
27
+ require:
28
+ - rubocop-solidus
29
+ ```
30
+
31
+ After this simply use the `rubocop` command to start linting.
32
+
33
+ ## Creating new cops
34
+
35
+ To create a new cop, run the following command:
36
+
37
+ ```bash
38
+ $ bundle exec rake new_cop[Solidus/NameOfTheCop]
39
+ ```
40
+
41
+ and then follow the instructions on the screen.
42
+
43
+ ## Release a new version
44
+
45
+ To release a new version, run the following command:
46
+
47
+ ```bash
48
+ $ bundle exec rake cut_release:[major|minor|patch]
49
+ ```
50
+
51
+ and then follow the instructions on the screen.
52
+
53
+ The type of the release is determined by:
54
+ - `major` if there are breaking changes
55
+ - `minor` if there are new cops or new features
56
+ - `patch` if there are only bug fixes
57
+
58
+ To deploy the new version to RubyGems, run the following command:
59
+
60
+ ```bash
61
+ $ bundle exec rake release
62
+ ```
63
+
64
+ ## Contributing
65
+
66
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nebulab/rubocop-solidus.
67
+
68
+ ## License
69
+
70
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+ require 'rspec/core/rake_task'
6
+
7
+ Dir['tasks/**/*.rake'].each { |t| load t }
8
+
9
+ task default: %i[spec rubocop]
10
+
11
+ RuboCop::RakeTask.new
12
+
13
+ RSpec::Core::RakeTask.new(:spec) do |spec|
14
+ spec.pattern = FileList['spec/**/*_spec.rb']
15
+ end
16
+
17
+ desc 'Generate a new cop with a template'
18
+ task :new_cop, [:cop] do |_task, args|
19
+ require 'rubocop'
20
+
21
+ cop_name = args.fetch(:cop) do
22
+ warn 'usage: bundle exec rake new_cop[Department/Name]'
23
+ exit!
24
+ end
25
+
26
+ generator = RuboCop::Cop::Generator.new(cop_name)
27
+
28
+ generator.write_source
29
+ generator.write_spec
30
+ generator.inject_require(root_file_path: 'lib/rubocop/cop/solidus_cops.rb')
31
+ generator.inject_config(config_file_path: 'config/default.yml')
32
+
33
+ puts generator.todo
34
+ end
@@ -0,0 +1,69 @@
1
+ AllCops:
2
+ # What version of Solidus is the inspected code using? If a value is specified
3
+ # for TargetSolidusVersion then it is used. Acceptable values are specified
4
+ # as a float (i.e. 3.1); the patch version of Solidus should not be included.
5
+ # If TargetSolidusVersion is not set, RuboCop will parse the Gemfile.lock or
6
+ # gems.locked file to find the version of Solidus that has been bound to the
7
+ # application. If neither of those files exist, RuboCop will use Solidus 3.0
8
+ # as the default.
9
+ TargetSolidusVersion: ~
10
+
11
+ Solidus/ClassEvalDecorator:
12
+ Description: 'Checks if Class.class_eval is being used in the code'
13
+ Enabled: true
14
+ VersionAdded: '0.1.0'
15
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/21'
16
+
17
+ Solidus/ReimbursementHookDeprecated:
18
+ Description: 'Checks if reimbursement_success_hooks and reimbursement_failed_hooks is being used'
19
+ Enabled: true
20
+ VersionAdded: '0.1.0'
21
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/27'
22
+
23
+ Solidus/SpreeCalculatorFreeShippingDeprecated:
24
+ Description: 'Checks if Spree::Calculator::FreeShipping is being used and add deprecation message'
25
+ Enabled: true
26
+ VersionAdded: '0.1.0'
27
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/29'
28
+
29
+ Solidus/SpreeCalculatorPercentPerItemDeprecated:
30
+ Description: 'Checks if Spree::Calculator::PercentPerItem is being used and add deprecation message'
31
+ Enabled: true
32
+ VersionAdded: '0.1.0'
33
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/29'
34
+
35
+ Solidus/SpreeCalculatorPriceSackDeprecated:
36
+ Description: 'Checks if Spree::Calculator::PriceSack is being used and add deprecation message'
37
+ Enabled: true
38
+ VersionAdded: '0.1.0'
39
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/29'
40
+
41
+ Solidus/SpreeDefaultCreditCardDeprecated:
42
+ Description: 'Checks if user.default_credit_card is used and suggest using user.wallet.default_wallet_payment_source'
43
+ Enabled: true
44
+ VersionAdded: '0.1.0'
45
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/33'
46
+
47
+ Solidus/SpreeGatewayBogusDeprecated:
48
+ Description: 'Checks if SpreeGatewayBogus is being used and replaces it with Spree::PaymentMethod::BogusCreditCard'
49
+ Enabled: true
50
+ VersionAdded: '0.1.0'
51
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/26'
52
+
53
+ Solidus/SpreeIconDeprecated:
54
+ Description: 'Checks if icon helper is being used and suggest `solidus_icon`'
55
+ Enabled: true
56
+ VersionAdded: '0.1.0'
57
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/32'
58
+
59
+ Solidus/SpreeRefundCallPerform:
60
+ Description: 'Checks if Spree::Refund.create is being used and require calling .perform!'
61
+ Enabled: true
62
+ VersionAdded: '0.1.0'
63
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/28'
64
+
65
+ Solidus/SpreeTDeprecated:
66
+ Description: 'Checks if Spree.t is being used and replaces it with I18n.t.'
67
+ Enabled: true
68
+ VersionAdded: '0.1.0'
69
+ Reference: 'https://github.com/nebulab/rubocop-solidus/issues/22'
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module provides a way to specify a minimum Solidus version for a cop.
6
+ # It also provides a way to check if the current Solidus version is affected.
7
+ # The minimum Solidus version can be specified in the cop itself in this way:
8
+ # include TargetSolidusVersion
9
+ # minimum_solidus_version 2.11
10
+ module TargetSolidusVersion
11
+ DEFAULT_SOLIDUS_VERSION = 3.0
12
+
13
+ def self.included(target)
14
+ target.extend(ClassMethods)
15
+ end
16
+
17
+ # These class methods are automatically added to the cop when the module
18
+ # is included. They are used to specify the minimum Solidus version for the cop.
19
+ module ClassMethods
20
+ def minimum_solidus_version(version)
21
+ @minimum_solidus_version = version
22
+ end
23
+
24
+ def targeted_solidus_version?(version)
25
+ @minimum_solidus_version <= version
26
+ end
27
+ end
28
+
29
+ # This method overrides the one in RuboCop::Cop::Base.
30
+ # Since this method is called for every offense, we can use it to check
31
+ # if the Solidus version is affected and skip the offense if it's not.
32
+ def add_offense(*args, **kwargs, &block)
33
+ return unless affected_solidus_version?
34
+
35
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3')
36
+ super(*args, **kwargs, &block)
37
+ else
38
+ super(*args, &block)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def affected_solidus_version?
45
+ self.class.targeted_solidus_version?(target_solidus_version)
46
+ end
47
+
48
+ def target_solidus_version
49
+ @target_solidus_version ||=
50
+ config.for_all_cops['TargetSolidusVersion']&.to_f || solidus_version_from_lock_file || DEFAULT_SOLIDUS_VERSION
51
+ end
52
+
53
+ def solidus_version_from_lock_file
54
+ @solidus_version_from_lock_file ||= read_solidus_version_from_lock_file
55
+ end
56
+
57
+ def read_solidus_version_from_lock_file
58
+ lock_file_path = config.bundler_lock_file_path
59
+ return nil unless lock_file_path
60
+
61
+ File.foreach(lock_file_path) do |line|
62
+ # If Solidus (or one of its frameworks) is in Gemfile.lock, there should be a line like:
63
+ # solidus_core (X.X.X)
64
+ result = line.match(/^\s+solidus_core\s+\((\d+\.\d+)/)
65
+ return result.captures.first.to_f if result
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # TODO: Write cop description and example of bad / good code. For every
7
+ # `SupportedStyle` and unique configuration, there needs to be examples.
8
+ # Examples must have valid Ruby syntax. Do not use upticks.
9
+ #
10
+ #
11
+ # @example EnforcedStyle: SpreeClass
12
+ # # Description of the `SpreeClass` style.
13
+ #
14
+ # # bad
15
+ # SpreeClass.class_eval do
16
+ # .
17
+ # .
18
+ # end
19
+ #
20
+ #
21
+ # # good
22
+ # module SpreeClassDecorator
23
+ # .
24
+ # .
25
+ # end
26
+ #
27
+ class ClassEvalDecorator < Base
28
+ MSG = 'Do not use `class_eval` flag. Use a decorator module instead. Check this link for an example https://guides.solidus.io/cookbook/redefining-checkout-steps'
29
+
30
+ # TODO: Don't call `on_send` unless the method name is in this list
31
+ # If you don't need `on_send` in the cop you created, remove it.
32
+ RESTRICT_ON_SEND = %i[class_eval].freeze
33
+
34
+ # @!method on_class_eval?(node)
35
+ def_node_matcher :on_class_eval?, <<~PATTERN
36
+ (send ($...) :class_eval)
37
+ PATTERN
38
+
39
+ def on_send(node)
40
+ return unless on_class_eval?(node)
41
+
42
+ add_offense(node)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds reimbursement_success_hooks and reimbursement_failed_hooks calls and
7
+ # asks to remove them and subscribe to reimbursement_reimbursed event instead.
8
+ class ReimbursementHookDeprecated < Base
9
+ include TargetSolidusVersion
10
+ minimum_solidus_version 2.11
11
+
12
+ MSG = 'Please remove reimbursement_success_hooks and reimbursement_failed_hooks. ' \
13
+ 'Subscribe to reimbursement_reimbursed event instead.'
14
+
15
+ def_node_matcher :success_hook?, <<~PATTERN
16
+ (send (send nil? :reimbursement_success_hooks) ...)
17
+ PATTERN
18
+
19
+ def_node_matcher :fail_hook?, <<~PATTERN
20
+ (send (send nil? :reimbursement_failed_hooks) ...)
21
+ PATTERN
22
+
23
+ def on_send(node)
24
+ return unless success_hook?(node) || fail_hook?(node)
25
+
26
+ add_offense(node)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree::Calculator::FreeShipping calls.
7
+ # This cop is needed as they have been deprecated in future version.
8
+ #
9
+ class SpreeCalculatorFreeShippingDeprecated < Base
10
+ MSG = 'Spree::Calculator::FreeShipping is deprecated.'
11
+
12
+ def_node_matcher :free_shipping?, <<~PATTERN
13
+ (send (... (... :Calculator) :FreeShipping) $_)
14
+ PATTERN
15
+
16
+ def on_send(node)
17
+ return unless free_shipping?(node)
18
+
19
+ add_offense(node)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree::Calculator::PercentPerItem calls.
7
+ # This cop is needed as they have been deprecated in future version.
8
+ #
9
+ class SpreeCalculatorPercentPerItemDeprecated < Base
10
+ extend AutoCorrector
11
+
12
+ MSG = 'Spree::Calculator::PercentPerItem is deprecated.'
13
+
14
+ def_node_matcher :percent_per_item?, <<~PATTERN
15
+ (send (... (... :Calculator) :PercentPerItem) $_)
16
+ PATTERN
17
+
18
+ def on_send(node)
19
+ percent_per_item?(node) do |method_used|
20
+ add_offense(node, message: MSG) do |corrector|
21
+ corrector.replace(node, "Spree::Calculator::PercentOnLineItem.#{method_used}")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree::Calculator::PriceSack calls.
7
+ # This cop is needed as they have been deprecated in future version.
8
+ #
9
+ class SpreeCalculatorPriceSackDeprecated < Base
10
+ MSG = 'Spree::Calculator::PriceSack is deprecated.'
11
+
12
+ def_node_matcher :price_sack?, <<~PATTERN
13
+ (send (... (... :Calculator) :PriceSack) $_)
14
+ PATTERN
15
+
16
+ def on_send(node)
17
+ return unless price_sack?(node)
18
+
19
+ add_offense(node)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds user.default_credit_card suggest using user.wallet.default_wallet_payment_source
7
+ #
8
+ # @example EnforcedStyle:
9
+ # # bad
10
+ # user.default_credit_card
11
+ #
12
+ # # good
13
+ # user.wallet.default_wallet_payment_source
14
+ #
15
+ #
16
+ class SpreeDefaultCreditCardDeprecated < Base
17
+ extend AutoCorrector
18
+ include TargetSolidusVersion
19
+ minimum_solidus_version 2.2
20
+
21
+ MSG = 'user.default_credit_card is deprecated. Please use user.wallet.default_wallet_payment_source instead.'
22
+
23
+ # @!method bad_method?(node)
24
+ def_node_matcher :default_credit_card?, <<~PATTERN
25
+ (send ... :default_credit_card)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless default_credit_card?(node)
30
+
31
+ add_offense(node) do |corrector|
32
+ corrector.replace(node, node.source.gsub('default_credit_card', 'wallet.default_wallet_payment_source'))
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree::Gateway::Bogus calls and replaces them with the Spree::PaymentMethod::BogusCreditCard call
7
+ # This cop is needed as the Spree::Gateway::Bogus has been deprecated in future version.
8
+ #
9
+ # @example EnforcedStyle:
10
+ # # bad
11
+ # Spree::Gateway::Bogus.new
12
+ #
13
+ # # good
14
+ # Spree::PaymentMethod::BogusCreditCard.new
15
+ #
16
+ # # bad
17
+ # Spree::Gateway::Bogus.create
18
+ #
19
+ # # good
20
+ # Spree::PaymentMethod::BogusCreditCard.create
21
+ #
22
+ # # bad
23
+ # Spree::Gateway::Bogus.create!
24
+ #
25
+ # # good
26
+ # Spree::PaymentMethod::BogusCreditCard.create!
27
+ #
28
+ #
29
+ class SpreeGatewayBogusDeprecated < Base
30
+ extend AutoCorrector
31
+ include TargetSolidusVersion
32
+ minimum_solidus_version 2.10
33
+
34
+ MSG = 'Spree::Gateway::Bogus is deprecated. Please use Spree::PaymentMethod::BogusCreditCard instead.'
35
+
36
+ # @!method bad_method?(node)
37
+ def_node_matcher :bad_class?, <<~PATTERN
38
+ (send (... (... :Gateway) :Bogus) $_)
39
+ PATTERN
40
+
41
+ def on_send(node)
42
+ bad_class?(node) do |method_used|
43
+ add_offense(node, message: MSG) do |corrector|
44
+ corrector.replace(node, "Spree::PaymentMethod::BogusCreditCard.#{method_used}")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds icon helper calls and suggest using solidus_icon
7
+ #
8
+ # @example EnforcedStyle:
9
+ # # bad
10
+ # helper.icon('example')
11
+ #
12
+ # # good
13
+ # helper.solidus_icon('example')
14
+ #
15
+ #
16
+ class SpreeIconDeprecated < Base
17
+ extend AutoCorrector
18
+ include TargetSolidusVersion
19
+ minimum_solidus_version 2.3
20
+
21
+ MSG = 'In Solidus 2.3, `icon` helper has been deprecated in favor of `solidus_icon`'
22
+
23
+ RESTRICT_ON_SEND = %i[icon].freeze
24
+
25
+ def_node_matcher :icon?, <<~PATTERN
26
+ (send (send nil? ...) :icon ...)
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ return unless icon?(node)
31
+
32
+ add_offense(node) do |corrector|
33
+ corrector.replace(node, node.source.gsub('icon', 'solidus_icon'))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree::Refund.create(your: attributes) calls and
7
+ # replaces them with the Spree::Refund.create(your: attributes, perform_after_create: false).perform! call
8
+ #
9
+ class SpreeRefundCallPerform < Base
10
+ include TargetSolidusVersion
11
+ minimum_solidus_version 2.11
12
+
13
+ MSG = 'From Solidus v3.0 onwards, #perform! will need to be explicitly called when creating new refunds. ' \
14
+ 'Please, change your code from `Spree::Refund.create(your: attributes)` ' \
15
+ 'to `Spree::Refund.create(your: attributes, perform_after_create: false).perform!`'
16
+
17
+ RESTRICT_ON_SEND = %i[create].freeze
18
+
19
+ # @!method bad_method?(node)
20
+ def_node_matcher :create_refund?, <<~PATTERN
21
+ (send (const (const nil? :Spree) :Refund) :create ...)
22
+ PATTERN
23
+
24
+ def on_send(node)
25
+ return unless create_refund?(node)
26
+
27
+ add_offense(node)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Solidus
6
+ # This cop finds Spree.t method calls and replaces them with the I18n,t method call
7
+ # This cop is needed as the Spree.t version has been deprecated in future version.
8
+ #
9
+ #
10
+ # @example EnforcedStyle: bar (default)
11
+ # # bad
12
+ # Spree.t(:bar)
13
+ #
14
+ # # good
15
+ # I18n.t(:bar, scope: :spree)
16
+ #
17
+ # # bad
18
+ # Spree.t(:bar, scope: [:city])
19
+ #
20
+ # # good
21
+ # I18n.t(:bar, scope: [:spree, :city])
22
+ #
23
+ # # bad
24
+ # Spree.t(:bar, scope: [:city], email: email)
25
+ #
26
+ # # good
27
+ # I18n.t(:bar, scope: [:spree, :city], email: email)
28
+ #
29
+ # # bad
30
+ # Spree.t('bar', scope: 'admin.city')
31
+ #
32
+ # # good
33
+ # I18n.t('bar', scope: 'spree.admin.city')
34
+ #
35
+ #
36
+ class SpreeTDeprecated < Base
37
+ extend AutoCorrector
38
+ MSG = 'Use I18n.t instead of Spree.t which has been deprecated in future versions.'
39
+
40
+ RESTRICT_ON_SEND = %i[t].freeze
41
+
42
+ # @!method spree_t?(node)
43
+ def_node_matcher :spree_t?, <<~PATTERN
44
+ (send ($...) :t ...)
45
+ PATTERN
46
+
47
+ def on_send(node)
48
+ return unless spree_t?(node)
49
+
50
+ return unless spree_t?(node).include?(:Spree)
51
+
52
+ add_offense(node) do |corrector|
53
+ corrector.replace(node, corrected_statement(node))
54
+ end
55
+ end
56
+
57
+ # rubocop:disable Metrics/MethodLength
58
+ def corrected_statement(node)
59
+ arguments = node.arguments
60
+
61
+ new_statement = 'I18n.t('
62
+ new_statement += arguments.first.source
63
+ new_statement += ', scope: :spree' if scope_missing?(arguments)
64
+
65
+ node.arguments.drop(1).each do |argument|
66
+ if argument.source.include? 'scope:'
67
+ new_argument = add_spree_scope(argument)
68
+ new_statement += ", #{new_argument}"
69
+ else
70
+ new_statement += ", #{argument.source}"
71
+ end
72
+ end
73
+ new_statement += ')'
74
+ new_statement
75
+ end
76
+ # rubocop:enable Metrics/MethodLength
77
+
78
+ def scope_missing?(arguments)
79
+ arguments.each do |argument|
80
+ return false if argument.source.include? 'scope:'
81
+ end
82
+ true
83
+ end
84
+
85
+ # rubocop:disable Metrics/MethodLength
86
+ def add_spree_scope(argument)
87
+ modified_argument = ''
88
+
89
+ argument.each_pair do |key, value|
90
+ if key.source == 'scope'
91
+ modified_argument = if value.type == :array
92
+ "#{key.source}: #{value.source.gsub('[', '[:spree, ')}"
93
+ else
94
+ "#{key.source}: 'spree.#{value.source.gsub("'", '')}'"
95
+ end
96
+ else
97
+ modified_argument += ", #{key.source}: #{value.source}"
98
+ end
99
+ end
100
+
101
+ modified_argument
102
+ end
103
+ # rubocop:enable Metrics/MethodLength
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'mixin/target_solidus_version'
4
+
5
+ require_relative 'solidus/class_eval_decorator'
6
+ require_relative 'solidus/reimbursement_hook_deprecated'
7
+ require_relative 'solidus/spree_calculator_free_shipping_deprecated'
8
+ require_relative 'solidus/spree_calculator_percent_per_item_deprecated'
9
+ require_relative 'solidus/spree_calculator_price_sack_deprecated'
10
+ require_relative 'solidus/spree_default_credit_card_deprecated'
11
+ require_relative 'solidus/spree_gateway_bogus_deprecated'
12
+ require_relative 'solidus/spree_icon_deprecated'
13
+ require_relative 'solidus/spree_refund_call_perform'
14
+ require_relative 'solidus/spree_t_deprecated'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The original code is from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
4
+ # See https://github.com/rubocop/rubocop-rspec/blob/master/MIT-LICENSE.md
5
+ module RuboCop
6
+ module Solidus
7
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
8
+ # bit of our configuration.
9
+ module Inject
10
+ def self.defaults!
11
+ path = CONFIG_DEFAULT.to_s
12
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
13
+ config = Config.new(hash, path).tap(&:make_excludes_absolute)
14
+ puts "configuration from #{path}" if ConfigLoader.debug?
15
+ config = ConfigLoader.merge_with_default(config, path)
16
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Solidus
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'solidus/version'
4
+
5
+ module RuboCop
6
+ # RuboCop Solidus project namespace
7
+ module Solidus
8
+ class Error < StandardError; end
9
+ # Your code goes here...
10
+ PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
11
+ CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
12
+ CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
13
+
14
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ require_relative 'rubocop/solidus'
6
+ require_relative 'rubocop/solidus/version'
7
+ require_relative 'rubocop/solidus/inject'
8
+
9
+ RuboCop::Solidus::Inject.defaults!
10
+
11
+ require_relative 'rubocop/cop/solidus_cops'
@@ -0,0 +1,6 @@
1
+ module Rubocop
2
+ module Solidus
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ autoload :Changelog, "#{__dir__}/changelog"
4
+
5
+ namespace :changelog do
6
+ %i[new fix change].each do |type|
7
+ desc "Create a Changelog entry (#{type})"
8
+ task type, [:id] do |_task, args|
9
+ ref_type = :pull if args[:id]
10
+ path = Changelog::Entry.new(type: type, ref_id: args[:id], ref_type: ref_type).write
11
+ cmd = "git add #{path}"
12
+ system cmd
13
+ puts "Entry '#{path}' created and added to git index"
14
+ end
15
+ end
16
+
17
+ desc 'Merge entries and delete them'
18
+ task :merge do
19
+ raise 'No entries!' unless Changelog.pending?
20
+
21
+ Changelog.new.merge!.and_delete!
22
+ cmd = "git commit -a -m 'Update Changelog'"
23
+ puts cmd
24
+ system cmd
25
+ end
26
+
27
+ task :check_clean do
28
+ next unless Changelog.pending?
29
+
30
+ puts '*** Pending changelog entries!'
31
+ puts 'Do `bundle exec rake changelog:merge`'
32
+ exit(1)
33
+ end
34
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Changelog utility
4
+ class Changelog
5
+ ENTRIES_PATH = 'changelog/'
6
+ FIRST_HEADER = /#{Regexp.escape("## master (unreleased)\n")}/m.freeze
7
+ ENTRIES_PATH_TEMPLATE = "#{ENTRIES_PATH}%<type>s_%<name>s.md"
8
+ TYPE_REGEXP = /#{Regexp.escape(ENTRIES_PATH)}([a-z]+)_/.freeze
9
+ TYPE_TO_HEADER = { new: 'New features', fix: 'Bug fixes', change: 'Changes' }.freeze
10
+ HEADER = /### (.*)/.freeze
11
+ PATH = 'CHANGELOG.md'
12
+ REF_URL = 'https://github.com/nebulab/rubocop-solidus'
13
+ MAX_LENGTH = 40
14
+ CONTRIBUTOR = '[@%<user>s]: https://github.com/%<user>s'
15
+ SIGNATURE = Regexp.new(format(Regexp.escape('[@%<user>s][]'), user: '([\w-]+)'))
16
+ EOF = "\n"
17
+
18
+ # New entry
19
+ Entry = Struct.new(:type, :body, :ref_type, :ref_id, :user, keyword_init: true) do
20
+ def initialize(type:, body: last_commit_title, ref_type: nil, ref_id: nil, user: github_user)
21
+ id, body = extract_id(body)
22
+ ref_id ||= id || 'x'
23
+ ref_type ||= id ? :issues : :pull
24
+ super
25
+ end
26
+
27
+ def write
28
+ File.write(path, content)
29
+ path
30
+ end
31
+
32
+ def path
33
+ format(ENTRIES_PATH_TEMPLATE, type: type, name: str_to_filename(body))
34
+ end
35
+
36
+ def content
37
+ period = '.' unless body.end_with? '.'
38
+ "* #{ref}: #{body}#{period} ([@#{user}][])\n"
39
+ end
40
+
41
+ def ref
42
+ "[##{ref_id}](#{REF_URL}/#{ref_type}/#{ref_id})"
43
+ end
44
+
45
+ def last_commit_title
46
+ `git log -1 --pretty=%B`.lines.first.chomp
47
+ end
48
+
49
+ def extract_id(body)
50
+ /^\[Fix(?:es)? #(\d+)\] (.*)/.match(body)&.captures || [nil, body]
51
+ end
52
+
53
+ def str_to_filename(str)
54
+ str
55
+ .split
56
+ .reject(&:empty?)
57
+ .map { |s| prettify(s) }
58
+ .inject do |result, word|
59
+ s = "#{result}_#{word}"
60
+ return result if s.length > MAX_LENGTH
61
+
62
+ s
63
+ end
64
+ end
65
+
66
+ def github_user
67
+ user = `git config --global credential.username`.chomp
68
+ warn 'Set your username with `git config --global credential.username "myusernamehere"`' if user.empty?
69
+
70
+ user
71
+ end
72
+
73
+ private
74
+
75
+ def prettify(str)
76
+ str.gsub!(/\W/, '_')
77
+
78
+ # Separate word boundaries by `_`.
79
+ str.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) do
80
+ (Regexp.last_match(1) || Regexp.last_match(2)) << '_'
81
+ end
82
+
83
+ str.gsub!(/\A_+|_+\z/, '')
84
+ str.downcase!
85
+ str
86
+ end
87
+ end
88
+
89
+ def self.pending?
90
+ entry_paths.any?
91
+ end
92
+
93
+ def self.entry_paths
94
+ Dir["#{ENTRIES_PATH}*"]
95
+ end
96
+
97
+ def self.read_entries
98
+ entry_paths.to_h { |path| [path, File.read(path)] }
99
+ end
100
+
101
+ attr_reader :header, :rest
102
+
103
+ def initialize(content: File.read(PATH), entries: Changelog.read_entries)
104
+ require 'strscan'
105
+
106
+ parse(content)
107
+ @entries = entries
108
+ end
109
+
110
+ def and_delete!
111
+ @entries.each_key { |path| File.delete(path) }
112
+ end
113
+
114
+ def merge!
115
+ File.write(PATH, merge_content)
116
+ self
117
+ end
118
+
119
+ def unreleased_content
120
+ entry_map = parse_entries(@entries)
121
+ merged_map = merge_entries(entry_map)
122
+ merged_map.flat_map { |header, things| ["### #{header}\n", *things, ''] }.join("\n")
123
+ end
124
+
125
+ def merge_content
126
+ merged_content = [@header, unreleased_content, @rest.chomp, *new_contributor_lines].join("\n")
127
+
128
+ merged_content << EOF
129
+ end
130
+
131
+ def new_contributor_lines
132
+ contributors
133
+ .map { |user| format(CONTRIBUTOR, user: user) }
134
+ .reject { |line| @rest.include?(line) }
135
+ end
136
+
137
+ def contributors
138
+ contributors = @entries.values.flat_map do |entry|
139
+ entry.match(/\. \((?<contributors>.+)\)\n/)[:contributors].split(',')
140
+ end
141
+
142
+ contributors.join.scan(SIGNATURE).flatten
143
+ end
144
+
145
+ private
146
+
147
+ def merge_entries(entry_map)
148
+ all = @unreleased.merge(entry_map) { |_k, v1, v2| v1.concat(v2) }
149
+ canonical = TYPE_TO_HEADER.values.to_h { |v| [v, nil] }
150
+ canonical.merge(all).compact
151
+ end
152
+
153
+ def parse(content)
154
+ ss = StringScanner.new(content)
155
+ @header = ss.scan_until(FIRST_HEADER)
156
+ @unreleased = parse_release(ss.scan_until(/\n(?=## )/m))
157
+ @rest = ss.rest
158
+ end
159
+
160
+ # @return [Hash<type, Array<String>]]
161
+ def parse_release(unreleased)
162
+ unreleased
163
+ .lines
164
+ .map(&:chomp)
165
+ .reject(&:empty?)
166
+ .slice_before(HEADER)
167
+ .to_h do |header, *entries|
168
+ [HEADER.match(header)[1], entries]
169
+ end
170
+ end
171
+
172
+ def parse_entries(path_content_map)
173
+ changes = Hash.new { |h, k| h[k] = [] }
174
+ path_content_map.each do |path, content|
175
+ header = TYPE_TO_HEADER.fetch(TYPE_REGEXP.match(path)[1].to_sym)
176
+ changes[header].concat(content.lines.map(&:chomp))
177
+ end
178
+ changes
179
+ end
180
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bump'
4
+
5
+ namespace :cut_release do
6
+ %w[major minor patch pre].each do |release_type|
7
+ desc "Cut a new #{release_type} release and create release notes."
8
+ task release_type => 'changelog:check_clean' do
9
+ run(release_type)
10
+ end
11
+ end
12
+
13
+ def add_header_to_changelog(version)
14
+ changelog = File.read('CHANGELOG.md')
15
+ head, tail = changelog.split("## master (unreleased)\n\n", 2)
16
+
17
+ File.open('CHANGELOG.md', 'w') do |f|
18
+ f << head
19
+ f << "## master (unreleased)\n\n"
20
+ f << "## #{version} (#{Time.now.strftime('%F')})\n\n"
21
+ f << tail
22
+ end
23
+ end
24
+
25
+ def create_release_notes(version)
26
+ release_notes = new_version_changes.strip
27
+ contributor_links = user_links(release_notes)
28
+
29
+ File.open("relnotes/v#{version}.md", 'w') do |file|
30
+ file << release_notes
31
+ file << "\n\n"
32
+ file << contributor_links
33
+ file << "\n"
34
+ end
35
+ end
36
+
37
+ def version_sans_patch(version)
38
+ version.split('.').take(2).join('.')
39
+ end
40
+
41
+ # Replace `<<next>>` (and variations) with version being cut.
42
+ def update_cop_versions(_old_version, new_version)
43
+ update_file('config/default.yml') do |default|
44
+ default.gsub(/['"]?<<\s*next\s*>>['"]?/i, "'#{version_sans_patch(new_version)}'")
45
+ end
46
+ end
47
+
48
+ def new_version_changes
49
+ changelog = File.read('CHANGELOG.md')
50
+ _, _, new_changes, _older_changes = changelog.split(/^## .*$/, 4)
51
+ new_changes
52
+ end
53
+
54
+ def update_file(path)
55
+ content = File.read(path)
56
+ File.write(path, yield(content))
57
+ end
58
+
59
+ def user_links(text)
60
+ names = text.scan(/\[@(\S+)\]\[\]/).map(&:first).uniq
61
+ names.map { |name| "[@#{name}]: https://github.com/#{name}" }.join("\n")
62
+ end
63
+
64
+ def run(release_type)
65
+ old_version = Bump::Bump.current
66
+ Bump::Bump.run(release_type, commit: false, bundle: false, tag: false)
67
+ new_version = Bump::Bump.current
68
+
69
+ update_cop_versions(old_version, new_version)
70
+ add_header_to_changelog(new_version)
71
+ create_release_notes(new_version)
72
+
73
+ puts "Changed version from #{old_version} to #{new_version}."
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-solidus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - piyushswain
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: |
28
+ Automatic Solidus code style checking tool.
29
+ A RuboCop extension focused on enforcing Solidus best practices and coding conventions.
30
+ email:
31
+ - danielepalombo@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".rspec"
37
+ - ".rubocop.yml"
38
+ - CHANGELOG.md
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - config/default.yml
45
+ - lib/rubocop-solidus.rb
46
+ - lib/rubocop/cop/mixin/target_solidus_version.rb
47
+ - lib/rubocop/cop/solidus/class_eval_decorator.rb
48
+ - lib/rubocop/cop/solidus/reimbursement_hook_deprecated.rb
49
+ - lib/rubocop/cop/solidus/spree_calculator_free_shipping_deprecated.rb
50
+ - lib/rubocop/cop/solidus/spree_calculator_percent_per_item_deprecated.rb
51
+ - lib/rubocop/cop/solidus/spree_calculator_price_sack_deprecated.rb
52
+ - lib/rubocop/cop/solidus/spree_default_credit_card_deprecated.rb
53
+ - lib/rubocop/cop/solidus/spree_gateway_bogus_deprecated.rb
54
+ - lib/rubocop/cop/solidus/spree_icon_deprecated.rb
55
+ - lib/rubocop/cop/solidus/spree_refund_call_perform.rb
56
+ - lib/rubocop/cop/solidus/spree_t_deprecated.rb
57
+ - lib/rubocop/cop/solidus_cops.rb
58
+ - lib/rubocop/solidus.rb
59
+ - lib/rubocop/solidus/inject.rb
60
+ - lib/rubocop/solidus/version.rb
61
+ - sig/rubocop/solidus.rbs
62
+ - tasks/changelog.rake
63
+ - tasks/changelog.rb
64
+ - tasks/cut_release.rake
65
+ homepage: https://www.github.com/nebulab/rubocop-solidus
66
+ licenses:
67
+ - MIT
68
+ metadata:
69
+ homepage_uri: https://www.github.com/nebulab/rubocop-solidus
70
+ source_code_uri: https://www.github.com/nebulab/rubocop-solidus
71
+ changelog_uri: https://www.github.com/nebulab/rubocop-solidus
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.4.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.3.17
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Automatic Solidus code style checking tool.
91
+ test_files: []