ruby-lsp-factory_bot 0.3.0 → 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
  SHA256:
3
- metadata.gz: e53f81bee237aa1380533073b84434270af2452c98ad6edbb9edbf1663cdbc43
4
- data.tar.gz: 24d5309b51a16ef658f2b15f3f44a5f9bd6fb18743e5f4517344ca130f0aa82d
3
+ metadata.gz: 4c4ec9b8d3a45f3e55dce6c1de8a44e4e91c35b5a2d4ebb05fe43017a44f4e4d
4
+ data.tar.gz: 82ff80b589abd172eb92e97e7afe0ad518864c397dd74eff0318961f57f1247a
5
5
  SHA512:
6
- metadata.gz: 27b794a78d67d2ae305f0f099340a183eb92fed59741f4b1264dbffc8f86cdaffbc3efdcd81fa4586b908c71abe260df8f0e3c896197b5478833aa6e466a5426
7
- data.tar.gz: 5f357307d3e982e759a8da970a4aca79886cbb1853c66809c0c00ebb8fd7eaa5cc6da8077038e70e7a0141f1646b9466a22eb8a4d03c0cc22eb11bdb64d27ef4
6
+ metadata.gz: 4d6cb00fb7ef0aa59a48cd818ab406a864d6457d363c7bd6ccb16161dcd8d634e8c503161adf67a5e9464ed761456184996dfd265e11a0622c5dd87df7a7f548
7
+ data.tar.gz: 448f83767509d07f38ea4e2f3c11504bde4104b9175e20b2a52910e4e3c23e26f80d87a933ac9e7e9c7d22c63a0656198733570e9773d5ac980f010337de9b44
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+
4
+ Style/StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # FactoryBot add-on
2
+
3
+ The FactoryBot add-on is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) [add-on](https://shopify.github.io/ruby-lsp/add-ons.html) for extra FactoryBot editor features.
4
+
5
+ ## Installation
6
+
7
+ If you haven't already done so, you'll need to first [set up Ruby LSP](https://shopify.github.io/ruby-lsp/#usage).
8
+
9
+ Add the following to your application's Gemfile:
10
+ ```ruby
11
+ # Gemfile
12
+ gem "ruby-lsp-factory_bot", require: false, group: :development
13
+ ```
14
+
15
+ run `bundle install` and restart Ruby LSP server.
16
+
17
+ ## Features
18
+
19
+ - [x] Go-to-definition for factories
20
+ - [x] Go-to-definition for factory traits
21
+ - [ ] Completions for factories and traits
22
+
23
+ ## License
24
+
25
+ The gem is available as open source under the terms of the
26
+ [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sorbet-runtime"
4
+ require "ruby_lsp/factory_bot/version"
5
+ require "ruby_lsp/factory_bot/addon"
6
+
7
+ module RubyLsp
8
+ module FactoryBot
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # parts << 'gem "ruby-lsp-factory_bot", require: false, group: :development, path: "~/Projects/ruby-lsp-factory_bot"'
4
+ require "ruby_lsp/addon"
5
+ require "ruby_lsp/internal"
6
+
7
+ require_relative "definition"
8
+ require_relative "indexing_enhancement"
9
+
10
+ module RubyLsp
11
+ module FactoryBot
12
+ class Addon < ::RubyLsp::Addon
13
+ def name
14
+ "Ruby LSP - Factory Bot"
15
+ end
16
+
17
+ def version
18
+ VERSION
19
+ end
20
+
21
+ def activate(global_state, _message_queue)
22
+ @index = global_state.index
23
+ end
24
+
25
+ def deactivate; end
26
+
27
+ def create_definition_listener(response_builder, uri, node_context, dispatcher)
28
+ Definition.new(response_builder, uri, node_context, @index, dispatcher)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "utils"
4
+
5
+ module RubyLsp
6
+ module FactoryBot
7
+ class Definition
8
+ include ::RubyLsp::Requests::Support::Common
9
+
10
+ FACTORY_BOT_STRATEGIES = %i[
11
+ create
12
+ build
13
+ build_stubbed
14
+ attributes_for
15
+ ].flat_map { |attr| [attr, :"#{attr}_list", :"#{attr}_pair"] }.freeze
16
+
17
+ def initialize(response_builder, uri, node_context, index, dispatcher)
18
+ @response_builder = response_builder
19
+ @uri = uri
20
+ @node_context = node_context
21
+ @index = index
22
+ dispatcher.register(self, :on_symbol_node_enter, :on_call_node_enter)
23
+ end
24
+
25
+ def on_symbol_node_enter(node)
26
+ return unless (call_node = @node_context&.call_node)
27
+
28
+ case call_node.name
29
+ when *FACTORY_BOT_STRATEGIES then factory_or_trait_location_for(call_node, node)
30
+ when :generate then sequence_location_for(node.value)
31
+ end
32
+ end
33
+
34
+ def on_call_node_enter(node)
35
+ return unless @node_context&.call_node&.message == "factory"
36
+ return if node.arguments || node.block
37
+
38
+ factory_location_for(node.message)
39
+ end
40
+
41
+ private
42
+
43
+ def factory_or_trait_location_for(call_node, node)
44
+ called_factory_name = Utils.name_from_node(call_node.arguments&.arguments&.first)
45
+ return unless called_factory_name
46
+
47
+ return factory_location_for(node.value) if node.value == called_factory_name
48
+
49
+ trait_location_for(node.value, called_factory_name)
50
+ end
51
+
52
+ def factory_location_for(factory_name)
53
+ @index["#{factory_name}FactoryBot"]&.each do |entry|
54
+ @response_builder << Interface::LocationLink.new(
55
+ target_uri: URI::Generic.from_path(path: entry.file_path).to_s,
56
+ target_range: range_from_location(entry.location),
57
+ target_selection_range: range_from_location(entry.name_location)
58
+ )
59
+ end
60
+ end
61
+
62
+ def trait_location_for(trait_name, called_factory_name)
63
+ @index["#{called_factory_name}-t-#{trait_name}FactoryBot"]&.each do |entry|
64
+ @response_builder << Interface::LocationLink.new(
65
+ target_uri: URI::Generic.from_path(path: entry.file_path).to_s,
66
+ target_range: range_from_location(entry.location),
67
+ target_selection_range: range_from_location(entry.name_location)
68
+ )
69
+ end
70
+ end
71
+
72
+ def sequence_location_for(sequence_name)
73
+ @index["#{sequence_name}-s-FactoryBot"]&.each do |entry|
74
+ @response_builder << Interface::LocationLink.new(
75
+ target_uri: URI::Generic.from_path(path: entry.file_path).to_s,
76
+ target_range: range_from_location(entry.location),
77
+ target_selection_range: range_from_location(entry.name_location)
78
+ )
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "utils"
4
+
5
+ module RubyLsp
6
+ module FactoryBot
7
+ class IndexingEnhancement < RubyIndexer::Enhancement
8
+ def initialize(...)
9
+ super
10
+ @inside_define_block = false
11
+ @factory_stack = []
12
+ end
13
+
14
+ FACTORIES_PATH = "spec/factories"
15
+
16
+ def on_call_node_enter(node)
17
+ @inside_define_block = true if node.message == "define"
18
+ return unless @inside_define_block
19
+
20
+ case node.message
21
+ when "factory"
22
+ @factory_stack << register_factory(node)
23
+ when "trait"
24
+ register_trait(node)
25
+ when "sequence"
26
+ register_sequence(node) if @factory_stack.empty?
27
+ end
28
+ end
29
+
30
+ def on_call_node_leave(node)
31
+ @inside_define_block = false if node.message == "define"
32
+ @factory_stack.pop if node.message == "factory"
33
+ end
34
+
35
+ private
36
+
37
+ def register_factory(node)
38
+ factory_names = resolve_factory_names(node)
39
+ factory_names&.each do |factory_name|
40
+ @listener.add_method(
41
+ "#{factory_name}FactoryBot",
42
+ node.location,
43
+ [RubyIndexer::Entry::Signature.new([])]
44
+ )
45
+ end
46
+
47
+ factory_names
48
+ end
49
+
50
+ def resolve_factory_names(node)
51
+ arguments = node.arguments&.arguments
52
+ return unless arguments
53
+
54
+ factory_names = []
55
+ factory_names << Utils.name_from_node(arguments.first)
56
+
57
+ keyword_hash_node = arguments.find do |argument|
58
+ argument.type == :keyword_hash_node || argument.type == :hash_node
59
+ end
60
+ return factory_names unless keyword_hash_node
61
+
62
+ aliases_node = keyword_hash_node.elements.find do |element|
63
+ Utils.name_from_node(element.key) == "aliases"
64
+ end&.value
65
+ return factory_names unless aliases_node
66
+
67
+ case aliases_node
68
+ when Prism::ArrayNode
69
+ factory_names += aliases_node.elements.map { |element| Utils.name_from_node(element) }
70
+ when Prism::SymbolNode, Prism::StringNode
71
+ factory_names << Utils.name_from_node(aliases_node)
72
+ end
73
+
74
+ factory_names
75
+ end
76
+
77
+ def register_trait(node)
78
+ return if !current_factory_names || current_factory_names.empty?
79
+
80
+ arguments = node.arguments&.arguments
81
+ return unless arguments
82
+
83
+ trait_name = Utils.name_from_node(arguments.first)
84
+ return unless trait_name
85
+
86
+ current_factory_names.each do |factory_name|
87
+ @listener.add_method(
88
+ "#{factory_name}-t-#{trait_name}FactoryBot",
89
+ node.location,
90
+ [RubyIndexer::Entry::Signature.new([])]
91
+ )
92
+ end
93
+ end
94
+
95
+ def current_factory_names
96
+ @factory_stack.last
97
+ end
98
+
99
+ def register_sequence(node)
100
+ arguments = node.arguments&.arguments
101
+ return unless arguments
102
+
103
+ sequence_name = Utils.name_from_node(arguments.first)
104
+ return unless sequence_name
105
+
106
+ @listener.add_method(
107
+ "#{sequence_name}-s-FactoryBot",
108
+ node.location,
109
+ [RubyIndexer::Entry::Signature.new([])]
110
+ )
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Utils
4
+ module_function
5
+
6
+ def name_from_node(name_node)
7
+ case name_node
8
+ when Prism::StringNode
9
+ name_node.content
10
+ when Prism::SymbolNode
11
+ name_node.value
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLsp
4
+ module FactoryBot
5
+ VERSION = "0.4.0"
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/ruby_lsp/factory_bot/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ruby-lsp-factory_bot"
7
+ spec.version = RubyLsp::FactoryBot::VERSION
8
+ spec.authors = ["donny741"]
9
+
10
+ spec.summary = "Ruby LSP FactoryBot Addon"
11
+ spec.description = "Provides go to definition and completion for FactoryBot attributes in Ruby LSP"
12
+ spec.homepage = "https://github.com/donny741/ruby-lsp-factory_bot"
13
+ spec.required_ruby_version = ">= 3.0.0"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(__dir__) do
21
+ `git ls-files -z`.split("\x0").reject do |f|
22
+ (File.expand_path(f) == __FILE__) ||
23
+ f.start_with?(
24
+ "bin/",
25
+ "test/",
26
+ "spec/",
27
+ "features/",
28
+ ".git",
29
+ ".circleci",
30
+ "appveyor",
31
+ "Gemfile",
32
+ "misc/",
33
+ "sorbet/"
34
+ )
35
+ end
36
+ end
37
+ spec.bindir = "exe"
38
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
39
+ spec.require_paths = ["lib"]
40
+
41
+ # Uncomment to register a new dependency of your gem
42
+ # spec.add_dependency "example-gem", "~> 1.0"
43
+ spec.add_dependency("ruby-lsp", ">= 0.24.0", "< 0.25.0")
44
+
45
+ # For more information and examples about making a new gem, check out our
46
+ # guide at: https://bundler.io/guides/creating_gem.html
47
+ end
@@ -0,0 +1,6 @@
1
+ module RubyLsp
2
+ module FactoryBot
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-factory_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - donny741
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-18 00:00:00.000000000 Z
10
+ date: 2025-06-24 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ruby-lsp
@@ -15,26 +15,38 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.23.0
18
+ version: 0.24.0
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: 0.24.0
21
+ version: 0.25.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 0.23.0
28
+ version: 0.24.0
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
- version: 0.24.0
31
+ version: 0.25.0
32
32
  description: Provides go to definition and completion for FactoryBot attributes in
33
33
  Ruby LSP
34
34
  executables: []
35
35
  extensions: []
36
36
  extra_rdoc_files: []
37
- files: []
37
+ files:
38
+ - ".rspec"
39
+ - ".rubocop.yml"
40
+ - README.md
41
+ - Rakefile
42
+ - lib/ruby-lsp-factory_bot.rb
43
+ - lib/ruby_lsp/factory_bot/addon.rb
44
+ - lib/ruby_lsp/factory_bot/definition.rb
45
+ - lib/ruby_lsp/factory_bot/indexing_enhancement.rb
46
+ - lib/ruby_lsp/factory_bot/utils.rb
47
+ - lib/ruby_lsp/factory_bot/version.rb
48
+ - ruby-lsp-factory_bot.gemspec
49
+ - sig/ruby_lsp/factory_bot.rbs
38
50
  homepage: https://github.com/donny741/ruby-lsp-factory_bot
39
51
  licenses: []
40
52
  metadata: