Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
.gem_rbs_collection
.rspec_status
node_modules
spec/sandbox/log/
spec/sandbox/storage/
spec/sandbox/tmp/
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
inherit_gem: { servactory-rubocop: rubocop-gem.yml }

plugins:
- rubocop-capybara
- rubocop-rspec_rails

Style/Documentation:
Enabled: false
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ GEM
rubocop-ast (1.45.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
Expand Down Expand Up @@ -350,6 +353,7 @@ DEPENDENCIES
rbs (>= 3.8)
rspec (>= 3.13)
rspec-rails (>= 7.0)
rubocop-capybara (>= 0.9)
servactory-rubocop (>= 0.9)
servactory-web!
sqlite3 (>= 2.1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="container mx-auto px-4 h-16 flex items-center justify-between">
<div class="flex items-center gap-6">
<h1 class="text-2xl">
<%= render Servactory::Web::UiKit::Atoms::LinkComponent.new(href: Servactory::Web::Engine.routes.url_helpers.services_path, text: "Servactory", options: { class: "font-bold text-gray-900 hover:text-blue-600 transition-colors", aria: { label: "Home" } }) %>
<%= render Servactory::Web::UiKit::Atoms::LinkComponent.new(href: Servactory::Web::Engine.routes.url_helpers.internal_services_path, text: "Servactory", options: { class: "font-bold text-gray-900 hover:text-blue-600 transition-colors", aria: { label: "Home" } }) %>
</h1>
<% if @app_name.present? %>
<div class="h-6 w-px bg-gray-300" aria-hidden="true"></div>
Expand All @@ -18,4 +18,4 @@
<%= render Servactory::Web::UiKit::Atoms::LinkComponent.new(href: @github_url, text: "GitHub", options: { class: "text-sm font-medium text-gray-700 hover:text-gray-900 transition-colors", target: "_blank", rel: "nofollow", aria: { label: "GitHub repository" } }) %>
</nav>
</div>
</header>
</header>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<nav aria-label="Services navigation" class="<%= @class_name %> <%= @options[:class] %>" <%= tag.attributes(@options.except(:class)) %>>
<ul class="list-none p-0 m-0" role="tree">
<% @nodes.each do |node| %>
<%= render Servactory::Web::UiKit::Organisms::TreeNodeComponent.new(node: node, level: 0) %>
<%= render Servactory::Web::UiKit::Organisms::TreeNodeComponent.new(node: node, level: 0, route_type: @route_type, gem_name: @gem_name) %>
<% end %>
</ul>
</nav>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ module UiKit
module Organisms
class TreeComponent < ViewComponent::Base
include Servactory::Web::UiKit::Concerns::ComponentOptions
def initialize(nodes: [], class_name: nil, options: {})
def initialize(route_type:, nodes: [], class_name: nil, options: {}, gem_name: nil)
super()
@nodes = nodes
@route_type = route_type
@gem_name = gem_name
initialize_component_options(class_name:, options:)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</div>
<ul role="group">
<% @node[:children].each do |child| %>
<%= render Servactory::Web::UiKit::Organisms::TreeNodeComponent.new(node: child, level: @level + 1) %>
<%= render Servactory::Web::UiKit::Organisms::TreeNodeComponent.new(node: child, level: @level + 1, route_type: @route_type, gem_name: @gem_name) %>
<% end %>
</ul>
</div>
Expand All @@ -17,7 +17,12 @@
<div class="<%= border_class %>">
<div class="flex items-center gap-2 p-1 hover:bg-gray-50 rounded-md transition-colors">
<%= render Servactory::Web::UiKit::Atoms::IconComponent.new(name: :file, class_name: 'size-3 text-gray-500') %>
<%= render Servactory::Web::UiKit::Atoms::LinkComponent.new(href: Servactory::Web::Engine.routes.url_helpers.service_path(@node[:path]), text: @node[:name], options: { class: "text-sm text-gray-700 hover:text-blue-600 transition-colors", aria: { label: "View service: #{@node[:name]}" } }) %>
<% url = if @route_type.to_sym == :internal
Servactory::Web::Engine.routes.url_helpers.internal_service_path(@node[:path])
else
Servactory::Web::Engine.routes.url_helpers.external_service_path(@gem_name, @node[:path])
end %>
<%= render Servactory::Web::UiKit::Atoms::LinkComponent.new(href: url, text: @node[:name], options: { class: "text-sm text-gray-700 hover:text-blue-600 transition-colors", aria: { label: "View service: #{@node[:name]}" } }) %>
</div>
</div>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ module Web
module UiKit
module Organisms
class TreeNodeComponent < ViewComponent::Base
def initialize(node:, level: 0)
def initialize(node:, route_type:, level: 0, gem_name: nil)
super()

@node = node
@level = level
@route_type = route_type
@gem_name = gem_name
end

def border_class
Expand Down
31 changes: 31 additions & 0 deletions app/controllers/servactory/web/external/services_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Servactory
module Web
module External
class ServicesController < ActionController::Base
layout "servactory/web/application"

before_action :assign_gem

def index
@gem_services_tree = Servactory::Web::Services::External::TreeBuilder.build(@gem)
end

def show
service_data = Servactory::Web::Services::External::TreeBuilder.find_service(@gem, params[:id])
@base_path = service_data[:base_path]
@file_path = service_data[:file_path]
@service_class = service_data[:service_class]
@source_code = service_data[:source_code]
end

private

def assign_gem
@gem = Bundler.definition.specs.find { |s| s.name == params[:gem_name] }
end
end
end
end
end
22 changes: 22 additions & 0 deletions app/controllers/servactory/web/internal/services_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module Servactory
module Web
module Internal
class ServicesController < ActionController::Base
layout "servactory/web/application"

def index
@app_services_tree = Servactory::Web::Services::Internal::TreeBuilder.build
end

def show
service_data = Servactory::Web::Services::Internal::TreeBuilder.find_service(params[:id])
@file_path = service_data[:file_path]
@service_class = service_data[:service_class]
@source_code = service_data[:source_code]
end
end
end
end
end
22 changes: 0 additions & 22 deletions app/controllers/servactory/web/services_controller.rb

This file was deleted.

19 changes: 19 additions & 0 deletions app/views/servactory/web/external/services/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<% if @gem %>
<%= render Servactory::Web::UiKit::Organisms::PageHeaderComponent.new(
title: "Services: #{@gem.full_name}",
description: "List of all services written using Servactory in the Gem"
) %>
<%= render Servactory::Web::UiKit::Organisms::CardComponent.new(class_name: 'shadow-lg') do %>
<%= render Servactory::Web::UiKit::Organisms::TreeComponent.new(nodes: @gem_services_tree, route_type: :external, gem_name: @gem.name) %>
<% end %>
<% else %>
<%= render Servactory::Web::UiKit::Organisms::PageHeaderComponent.new(
title: "Service Not Found",
description: nil
) %>
<%= render Servactory::Web::UiKit::Organisms::CardComponent.new do %>
<div class="text-gray-500">
No services found for the specified <pre class="inline"><code><%= params[:gem_name] %></code></pre> library
</div>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
empty_message: 'No output attributes defined'
) %>

<%
<%
# Transform stages data to a flat structure for the component
actions_data = {}
if @service_class.info.stages.present?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<%= render Servactory::Web::UiKit::Organisms::PageHeaderComponent.new(
title: 'Services',
description: 'List of all services written using Servactory in the project'
title: 'Services: Application',
description: 'List of all services written using Servactory in the Application'
) %>
<%= render Servactory::Web::UiKit::Organisms::CardComponent.new(class_name: 'shadow-lg') do %>
<%= render Servactory::Web::UiKit::Organisms::TreeComponent.new(nodes: @services_tree) %>
<%= render Servactory::Web::UiKit::Organisms::TreeComponent.new(nodes: @app_services_tree, route_type: :internal) %>
<% end %>
67 changes: 67 additions & 0 deletions app/views/servactory/web/internal/services/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<% if @service_class && @source_code %>
<%= render Servactory::Web::UiKit::Organisms::PageHeaderComponent.new(
title: @service_class.name,
description: 'Detailed information about the service inputs, internals, outputs, and implementation'
) %>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<%= render Servactory::Web::UiKit::Organisms::SectionCardComponent.new(
title: 'Inputs',
items: @service_class.info.inputs,
border_class: 'border-blue-500',
text_class: 'text-blue-700',
bg_class: 'bg-blue-50',
icon_name: :inputs,
empty_message: 'No input attributes defined',
class_name: 'mb-4'
) %>

<%= render Servactory::Web::UiKit::Organisms::SectionCardComponent.new(
title: 'Internals',
items: @service_class.info.internals,
border_class: 'border-purple-500',
text_class: 'text-purple-700',
bg_class: 'bg-blue-50',
icon_name: :internals,
empty_message: 'No internal attributes defined'
) %>

<%= render Servactory::Web::UiKit::Organisms::SectionCardComponent.new(
title: 'Outputs',
items: @service_class.info.outputs,
border_class: 'border-green-500',
text_class: 'text-green-700',
bg_class: 'bg-blue-50',
icon_name: :outputs,
empty_message: 'No output attributes defined'
) %>

<%
# Transform stages data to a flat structure for the component
actions_data = {}
if @service_class.info.stages.present?
@service_class.info.stages.each do |_stage_name, actions|
actions_data.merge!(actions)
end
end
%>

<%= render Servactory::Web::UiKit::Organisms::SectionCardComponent.new(
title: 'Actions',
items: actions_data,
border_class: 'border-orange-500',
text_class: 'text-orange-700',
bg_class: 'bg-orange-50',
icon_name: :actions,
empty_message: 'No actions defined'
) %>
</div>
<%= render Servactory::Web::UiKit::Organisms::CodeBlockComponent.new(code: @source_code, language: "ruby", copy_button: true) %>
<% else %>
<%= render Servactory::Web::UiKit::Organisms::PageHeaderComponent.new(
title: 'Service Not Found',
description: 'The requested service could not be found'
) %>
<%= render Servactory::Web::UiKit::Organisms::CardComponent.new do %>
<p class="text-gray-500">Service not found or source code could not be retrieved.</p>
<% end %>
<% end %>
12 changes: 10 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# frozen_string_literal: true

Servactory::Web::Engine.routes.draw do
root "servactory/web/services#index"
root "servactory/web/internal/services#index"

resources :services, only: %i[index show], controller: "servactory/web/services"
scope :internal, as: :internal do
resources :services, only: %i[index show], controller: "servactory/web/internal/services"
end

scope :external, as: :external do
scope ":gem_name" do
resources :services, only: %i[index show], controller: "servactory/web/external/services"
end
end
end
6 changes: 5 additions & 1 deletion lib/servactory/web/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ module Servactory
module Web
class Configuration
attr_accessor :app_name,
:app_url
:app_url,
:gem_names,
:gem_service_directories

attr_reader :app_services_directory

def initialize
@app_services_directory = Rails.root.join("app/services")
@app_url = nil
@gem_names = []
@gem_service_directories = %w[app/services lib]
end

def app_services_directory=(value)
Expand Down
Loading