### Ruby: Install BetterService Gem Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Instructions for installing the BetterService gem using Bundler. Ensure 'better_service' is added to your Gemfile and then run 'bundle install'. ```ruby # Gemfile gem 'better_service' # Then run bundle install ``` -------------------------------- ### Ruby: Product CreateService with Schema Validation Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Example of a CreateService for 'Product' that includes a schema for validation. It requires 'name' and 'price', and optionally accepts 'description'. ```ruby class Product::CreateService < BetterService::CreateService schema do required(:name).filled(:string, min_size?: 3) required(:price).filled(:decimal, gt?: 0) optional(:description).maybe(:string) end process_with do |data| { resource: Product.create!(params) } end end ``` -------------------------------- ### Service Initialization and Call (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Demonstrates how to initialize a BetterService object with a user and parameters, and then call the service to execute its logic. ```ruby service = ServiceClass.new(user, params: { ... }) result = service.call ``` -------------------------------- ### Verify BetterService Installation Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Checks if BetterService has been successfully installed by accessing its version constant in the Rails console. This confirms the gem is available and loaded. ```ruby BetterService::VERSION ``` -------------------------------- ### Ruby: Generated Product IndexService with Schema Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Example of a generated IndexService for 'Product', including a schema definition for validation. It defines optional parameters like 'page', 'per_page', and 'search'. ```ruby # app/services/product/index_service.rb module Product class IndexService < BetterService::IndexService model_class Product schema do optional(:page).filled(:integer, gteq?: 1) optional(:per_page).filled(:integer, gteq?: 1, lteq?: 100) optional(:search).maybe(:string) end search_with do { items: model_class.all } end end end ``` -------------------------------- ### Install BetterService Gem Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Adds the BetterService gem to your Rails application's Gemfile and installs it using Bundler. This is the first step to integrate BetterService. ```ruby gem "better_service" ``` -------------------------------- ### Test Service Object Execution Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Provides examples of calling the `Post::CreateService` in a Rails console, demonstrating both successful execution with valid parameters and how invalid parameters trigger a `ValidationError`. ```ruby # In Rails console user = User.first # Valid params - succeeds result = Post::CreateService.new(user, params: { title: "My First Post", body: "This is the content" }).call result[:success] # => true result[:resource] # => result[:metadata] # => { action: :created } # Invalid params - raises exception Post::CreateService.new(user, params: { title: "", # Invalid: must be filled body: "Content" }) # => BetterService::Errors::Runtime::ValidationError: Validation failed ``` -------------------------------- ### Multi-Tenant Onboarding Workflow Setup (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/context7/workflows/04-workflows-patterns.md Sets up a new tenant by creating the tenant, admin user, billing account, sample data, and sending a welcome email. It requires company name and admin email. ```ruby class Tenant::OnboardingWorkflow < BetterService::Workflow schema do required(:company_name).filled(:string) required(:admin_email).filled(:string) end step :create_tenant, with: Tenant::CreateService step :create_admin, with: User::CreateAdminService step :setup_billing, with: Billing::SetupService step :create_sample_data, with: SampleData::CreateService step :send_welcome, with: Email::TenantWelcomeService end ``` -------------------------------- ### Ruby: BetterService Workflow for Chaining Services Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Shows the recommended approach for calling services from within other services, using 'BetterService::Workflow'. This example chains 'Order::CreateService' and 'Payment::ChargeService'. ```ruby # ❌ WRONG class Order::CreateService < BetterService::CreateService process_with do |data| order = Order.create!(params) Payment::ChargeService.new(user, params: {}).call # ❌ DON'T end end # ✅ CORRECT - Use Workflow class Order::CheckoutWorkflow < BetterService::Workflow step :create_order, with: Order::CreateService step :charge_payment, with: Payment::ChargeService end ``` -------------------------------- ### Install Better Service Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/configuration.md Generates the initial configuration file `config/initializers/better_service.rb` with all options commented out. This is the first step to customizing BetterService. ```bash rails generate better_service:install ``` -------------------------------- ### Ruby: ActionService for Custom Business Actions Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Provides a framework for custom business actions. This example shows an 'ApproveService' for orders, demonstrating how to define an action name and custom processing logic. ```ruby class Order::ApproveService < BetterService::ActionService model_class Order action_name :approve search_with do { resource: model_class.find(params[:id]) } end process_with do |data| data[:resource].update!(status: 'approved') { resource: data[:resource] } end end ``` -------------------------------- ### Implement Customer Onboarding Workflow Source: https://github.com/alessiobussolari/better_service/blob/main/context7/generators/03-workflow-generator-examples.md Demonstrates a multi-tenant customer onboarding workflow. It requires company name and admin email, and includes steps for tenant and admin creation, billing setup, sample data, and welcome emails. ```ruby class Tenant::OnboardWorkflow < BetterService::Workflow schema do required(:company_name).filled(:string) required(:admin_email).filled(:string) end step :create_tenant, with: Tenant::CreateService step :create_admin, with: User::CreateAdminService step :setup_billing, with: Billing::SetupService step :create_sample_data, with: SampleData::CreateService step :send_welcome, with: Email::TenantWelcomeService end ``` -------------------------------- ### Configure BetterService Options Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Configures BetterService features like logging, stats collection, and instrumentation within the initializer file. It allows tailoring BetterService behavior based on the environment. ```ruby BetterService.configure do |config| # Enable logging for development config.log_subscriber_enabled = Rails.env.development? config.log_subscriber_level = :info # Enable stats collection config.stats_subscriber_enabled = true # Enable instrumentation (recommended) config.instrumentation_enabled = true end ``` -------------------------------- ### Ruby: CreateService for Creating New Resources Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md This service handles the creation of new resources. It requires a 'Product' model and uses 'process_with' to define the creation logic using provided parameters. ```ruby class Product::CreateService < BetterService::CreateService model_class Product process_with do |data| { resource: model_class.create!(params) } end end ``` -------------------------------- ### Use Setup for Common Data in Ruby Tests Source: https://github.com/alessiobussolari/better_service/blob/main/docs/testing.md Demonstrates the use of the `setup` method in test classes to define common data or state required by multiple tests. This follows the DRY (Don't Repeat Yourself) principle, reducing redundancy and improving test readability. ```ruby # ✅ Good - DRY class Product::CreateServiceTest < ActiveSupport::TestCase setup do @user = users(:admin) @valid_params = { name: "Product", price: 99.99 } end test "creates product" do result = Product::CreateService.new(@user, params: @valid_params).call # ... end end # ❌ Bad - repetitive test "creates product" do user = users(:admin) params = { name: "Product", price: 99.99 } # ... end test "updates product" do user = users(:admin) # Duplicated params = { name: "Product", price: 99.99 } # Duplicated # ... end ``` -------------------------------- ### BetterService 5-Phase Execution Flow Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Illustrates the standard 5-phase execution flow of a BetterService object: VALIDATE, AUTHORIZE, SEARCH, PROCESS, and RESPOND. Each phase has a specific purpose and occurs sequentially. ```text ┌─────────────┐ │ 1. VALIDATE │ Validate params against schema (during initialize) └──────┬──────┘ │ ┌──────▼────────┐ │ 2. AUTHORIZE │ Check user permissions (optional) └──────┬────────┘ │ ┌──────▼────────┐ │ 3. SEARCH │ Load data from database/APIs └──────┬────────┘ │ ┌──────▼────────┐ │ 4. PROCESS │ Transform data, business logic └──────┬────────┘ │ ┌──────▼────────┐ │ 5. RESPOND │ Format final response └───────────────┘ ``` -------------------------------- ### Authorize by Ownership (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Demonstrates how to authorize actions based on resource ownership or administrator privileges using a Ruby block. This pattern ensures that only the owner or an admin can perform certain operations. ```ruby authorize_with do resource.user_id == user.id || user.admin? end ``` -------------------------------- ### Define Data Search Logic (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Defines the data fetching logic using the `search_with` block. This is where you load necessary data from databases or external APIs. ```ruby search_with do { post: Post.find(params[:id]) } end ``` -------------------------------- ### Ruby: IndexService for Listing/Filtering Resources Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Defines a service to list and filter resources with pagination. It requires a 'Product' model and uses 'search_with' to specify the data fetching logic. ```ruby class Product::IndexService < BetterService::IndexService model_class Product search_with do { items: model_class.all } end end ``` -------------------------------- ### Define Service Schema and Process Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Defines the input schema for validation and the processing logic for a `Post::CreateService`. It specifies required and optional parameters and outlines the creation of a new post resource. ```ruby class Post::CreateService < BetterService::Services::CreateService # 1. Define what parameters are required/optional schema do required(:title).filled(:string) required(:body).filled(:string) optional(:published).maybe(:bool) end # 2. Process phase - create the post process_with do |data| post = user.posts.create!( title: params[:title], body: params[:body], published: params[:published] || false ) { resource: post } end end ``` -------------------------------- ### Bash: Generate Complete CRUD Services with Options Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Command to generate all 5 CRUD services (Create, Read, Update, Delete) for a 'Product' model, with options to include caching and authorization. ```bash rails g serviceable:scaffold Product --cache --authorize ``` -------------------------------- ### Implement Business Logic (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Implements the core business logic and data transformation within the `process_with` block. This block receives data from the search phase and returns processed data. ```ruby process_with do |data| post = data[:post] post.update!(published: true, published_at: Time.current) { resource: post } end ``` -------------------------------- ### Ruby: ShowService for Displaying a Single Resource Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md A service designed to display a single resource by its ID. It depends on the 'Product' model and uses 'search_with' to find the specific resource. ```ruby class Product::ShowService < BetterService::ShowService model_class Product search_with do { resource: model_class.find(params[:id]) } end end ``` -------------------------------- ### Complete ProductRepository Example in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/context7/repository/04-custom-repository-examples.md A comprehensive example of a `ProductRepository` implementing scopes, filters, ordering, combined queries, aggregations, and search functionality using BetterService. ```ruby class ProductRepository < BetterService::Repository::BaseRepository def initialize super(Product) end # Scopes def published model.published end def unpublished model.unpublished end def featured model.featured end # Filters def by_user(user_id) where(user_id: user_id) end def by_category(category_id) where(category_id: category_id) end def in_price_range(min_price, max_price) where(price: min_price..max_price) end # Ordering def newest_first model.order(created_at: :desc) end def cheapest_first model.order(price: :asc) end def most_expensive_first model.order(price: :desc) end # Combined queries def published_by_category(category_id) published.where(category_id: category_id) end def featured_under_price(max_price) featured.where("price <= ?", max_price) end # Aggregations def average_price model.average(:price) end def total_value model.sum(:price) end # Search def search_by_name(query) where("name ILIKE ?", "%#{query}%") end end ``` -------------------------------- ### Workflow Example: User Onboarding Source: https://github.com/alessiobussolari/better_service/blob/main/docs/workflows/01_workflows_introduction.md This Ruby code defines a user onboarding workflow using BetterService::Workflow. It outlines the sequence of services involved in creating a user account, profile, and sending welcome emails. ```ruby class User::OnboardingWorkflow < BetterService::Workflow step :create_account, with: User::CreateService step :create_profile, with: Profile::CreateService step :setup_preferences, with: Preferences::CreateService step :send_welcome_email, with: Email::WelcomeService step :send_verification, with: Email::VerificationService step :create_sample_data, with: SampleData::CreateService end ``` -------------------------------- ### Product Presenter Implementation Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/03_show_service.md An example implementation of a `ProductPresenter` class. The `present` class method takes a product object and returns a formatted hash containing its details, category information, image URLs, and review statistics. ```ruby class ProductPresenter def self.present(product) { id: product.id, name: product.name, description: product.description, price: product.price.to_f, category: { id: product.category.id, name: product.category.name }, images: product.images.map { |img| img.url }, rating: { average: product.reviews.average(:rating)&.round(1), count: product.reviews.count } } end end ``` -------------------------------- ### Format Output Using a Presenter (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/02-index-service-examples.md Shows how to format the output data using a dedicated presenter class (`ProductPresenter`). The `presenter` method specifies the presenter, which should have a `present` class method. Dependencies include BetterService, Product model, and ProductPresenter. ```ruby class Product::IndexService < BetterService::IndexService model_class Product presenter ProductPresenter search_with do { items: model_class.includes(:category).all } end end # ProductPresenter class ProductPresenter def self.present(product) { id: product.id, name: product.name, price: product.price.to_f, category: product.category.name } end end ``` -------------------------------- ### Initialize ProductRepository in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/context7/repository/02-base-repository-examples.md Demonstrates how to initialize a repository for the Product model, either by explicitly passing the model class or by relying on auto-derivation based on the repository name. ```ruby class ProductRepository < BetterService::Repository::BaseRepository def initialize super(Product) # Pass the model class end end # Or with auto-derivation (ProductRepository -> Product) class ProductRepository < BetterService::Repository::BaseRepository # Model class derived from repository name end ``` -------------------------------- ### Using Error Methods for Structured Logging in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/error-handling.md Illustrates how to use the `#to_h` method on BetterService errors to get a structured hash representation, which is ideal for logging. This example demonstrates logging the error details to Rails logger in JSON format. ```ruby begin service.call rescue BetterService::BetterServiceError => e # Structured logging Rails.logger.error(e.to_h.to_json) # => { # "error_class": "BetterService::Errors::Runtime::ValidationError", # "message": "Validation failed", # "code": "validation_failed", # "timestamp": "2025-11-11T10:30:00Z", # "context": { "service": "MyService", ... }, # "original_error": { "class": "StandardError", ... }, # "backtrace": [...] # } end ``` -------------------------------- ### Basic Custom Repository Example in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/context7/repository/04-custom-repository-examples.md Demonstrates a basic custom repository for a Product model, initializing with the model and providing methods to delegate to model scopes and perform custom queries. ```ruby class ProductRepository < BetterService::Repository::BaseRepository def initialize super(Product) end # Delegate to model scope def published model.published end def unpublished model.unpublished end # Custom query def by_user(user_id) where(user_id: user_id) end end ``` -------------------------------- ### Validate Date Range Relationships Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/08-schema-validation.md Validates that date fields fall within a correct chronological order. This example ensures that an end date is after a start date. ```ruby schema do required(:start_date).filled(:date) required(:end_date).filled(:date) rule(:start_date, :end_date) do if values[:start_date] && values[:end_date] if values[:start_date] > values[:end_date] key(:end_date).failure('must be after start date') end end end end ``` -------------------------------- ### Content Type Processing Pipeline Source: https://github.com/alessiobussolari/better_service/blob/main/docs/workflows/05_workflow_examples.md Defines a pipeline for processing content based on its type. This example shows the start of a Ruby method or block designed to handle different content processing logic. ```ruby class ContentProcessor def initialize(content) @content = content end def process case @content.type when 'article' process_article when 'video' process_video else process_other end end private def process_article # Article processing logic puts 'Processing article...' end def process_video # Video processing logic puts 'Processing video...' end def process_other # Default processing logic puts 'Processing other content type...' end end ``` -------------------------------- ### ShowService process_with Examples Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/03_show_service.md Demonstrates various implementations of the 'process_with' block, which enriches or transforms the resource data after it has been retrieved. Examples include adding metadata, tracking views by incrementing a counter, and fetching related data. ```ruby # Add metadata process_with do |data| resource = data[:resource] { resource: resource, metadata: { views: resource.view_count, last_updated: resource.updated_at } } end # Track view process_with do |data| resource = data[:resource] resource.increment!(:view_count) { resource: resource } end # Add related data process_with do |data| resource = data[:resource] { resource: resource, related_products: Product.where(category: resource.category).limit(5) } end ``` -------------------------------- ### Configure BetterService default settings Source: https://github.com/alessiobussolari/better_service/blob/main/context7/advanced/06-configuration-examples.md Basic setup for BetterService with all features enabled. This configuration includes service params in event payloads by default and enables built-in subscribers for stats and logging. No services are excluded from instrumentation in this setup. ```ruby # config/initializers/better_service.rb BetterService.configure do |config| # Master switch - enable instrumentation config.instrumentation_enabled = true # default: true # Include service params in event payloads config.instrumentation_include_args = true # default: true # Include service results in completed events config.instrumentation_include_result = false # default: false # No services excluded by default config.instrumentation_excluded_services = [] # default: [] # Enable built-in subscribers config.stats_subscriber_enabled = true # default: true config.log_subscriber_enabled = true # default: true end ``` -------------------------------- ### BetterService Configuration Error: InvalidSchemaError Example in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/error-handling.md Demonstrates the InvalidSchemaError in BetterService, which is triggered by incorrect syntax within the schema definition block. It provides an example of an invalid schema and advises on using valid Dry::Schema syntax. ```ruby schema do required(:email).filled(:invalid_type) # Invalid type end # => BetterService::Errors::Configuration::InvalidSchemaError # Fix: Use valid Dry::Schema types and predicates. ``` -------------------------------- ### Example Product Presenter Implementation Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/02_index_service.md Provides an example implementation of a ProductPresenter class. This class defines a `present` class method that takes a product object and returns a hash with formatted attributes, including related data like category name and average rating. ```ruby class ProductPresenter def self.present(product) { id: product.id, name: product.name, price: product.price.to_f, category: product.category.name, rating: product.reviews.average(:rating)&.round(1) } end end ``` -------------------------------- ### BetterService Configuration Error: NilUserError Example in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/error-handling.md Shows the NilUserError in BetterService, raised when a service requires a user but receives nil. It provides an example of the error and demonstrates how to resolve it by passing a valid user or explicitly allowing nil users. ```ruby MyService.new(nil, params: {}) # => BetterService::Errors::Configuration::NilUserError # Fix: class MyService < BetterService::Base self._allow_nil_user = true end ``` -------------------------------- ### Access Service Statistics Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/configuration.md Provides an example of how to access the collected statistics from the `StatsSubscriber`. Useful for performance monitoring and analysis. ```ruby stats = BetterService::Subscribers::StatsSubscriber.stats # Get total calls puts stats[:total_calls] # Get stats for a specific service product_service_stats = stats[:services]["Product::CreateService"] if product_service_stats puts "Product::CreateService calls: #{product_service_stats[:calls]}" puts "Product::CreateService avg duration: #{product_service_stats[:avg_duration]}ms" end ``` -------------------------------- ### Format Created Resource Using a Presenter Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/04-create-service-examples.md Demonstrates how to use a presenter to format the created resource for output with BetterService::CreateService. The `presenter ProductPresenter` line specifies the presenter class to be used. The service validates name and price, creates the resource, invalidates cache, and returns the formatted resource. ```ruby class Product::CreateService < BetterService::CreateService model_class Product presenter ProductPresenter schema do required(:name).filled(:string) required(:price).filled(:decimal) end process_with do |data| resource = model_class.create!(params.merge(user: user)) invalidate_cache_for(user) { resource: resource } end end ``` -------------------------------- ### Initialization Source: https://github.com/alessiobussolari/better_service/blob/main/context7/repository/02-base-repository-examples.md Demonstrates how to initialize a repository, either by explicitly passing the model class or by relying on auto-derivation. ```APIDOC ## Initialization Demonstrates how to initialize a repository, either by explicitly passing the model class or by relying on auto-derivation. ### Usage ```ruby class ProductRepository < BetterService::Repository::BaseRepository def initialize super(Product) # Pass the model class end end # Or with auto-derivation (ProductRepository -> Product) class ProductRepository < BetterService::Repository::BaseRepository # Model class derived from repository name end ``` ``` -------------------------------- ### Complete Product Creation Service Example Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/04_create_service.md A comprehensive example of a `Product::CreateService` using `BetterService::CreateService`. It includes schema definition, authorization, search context, transaction processing, and cache invalidation, along with usage instructions. ```ruby module Product class CreateService < BetterService::CreateService model_class Product cache_contexts :products schema do required(:name).filled(:string) required(:price).filled(:decimal, gt?: 0) required(:category_id).filled(:integer) optional(:description).maybe(:string) end authorize_with do user.admin? || user.seller? end search_with do category = Category.find(params[:category_id]) { category: category } end process_with do |data| resource = model_class.create!( params.merge(user: user) ) invalidate_cache_for(user) { resource: resource } end end end # Usage result = Product::CreateService.new(current_user, params: { name: "Gaming Laptop", price: 1299.99, category_id: 5, description: "High-performance laptop" }).call product = result[:resource] # => # ``` -------------------------------- ### Ruby: DestroyService for Deleting Resources Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Handles the deletion of resources. It finds the resource using 'search_with' and then removes it using 'process_with'. ```ruby class Product::DestroyService < BetterService::DestroyService model_class Product search_with do { resource: model_class.find(params[:id]) } end process_with do |data| data[:resource].destroy! { resource: data[:resource] } end end ``` -------------------------------- ### Invalidate a Specific Cache Key in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/cache-invalidation.md Provides an example of how to delete a single, precisely identified cache entry using its unique key. ```ruby BetterService::CacheService.invalidate_key("products_index:user_123:abc:products") ``` -------------------------------- ### Resetting StatsSubscriber Statistics in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/context7/advanced/03-stats-subscriber-examples.md This example runs services to collect stats and then resets them using reset! method for starting a new collection period. Requires StatsSubscriber enabled; inputs are initial service executions, outputs cleared stats hash. Useful for periodic metric resets without restarting the application. ```ruby # Execute services Product::CreateService.new(user, params: { name: "A", price: 10 }).call Product::IndexService.new(user, params: {}).call # Stats are collected stats = BetterService::Subscribers::StatsSubscriber.stats stats.keys # => ["Product::CreateService", "Product::IndexService"] # Reset all statistics BetterService::Subscribers::StatsSubscriber.reset! ``` -------------------------------- ### Show resource with eager loading (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/03-show-service-examples.md Loads a Product along with its associated category, reviews, and images to avoid N+1 queries. The schema remains the same, but the search step uses ActiveRecord includes. Returns the fully loaded resource. ```ruby class Product::ShowService < BetterService::ShowService model_class Product schema do required(:id).filled(:integer) end search_with do resource = model_class .includes(:category, :reviews, :images) .find(params[:id]) { resource: resource } end end ``` -------------------------------- ### Define Response Formatting (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Defines how the final response is formatted using the `respond_with` block. This block receives the processed data and constructs the success response. ```ruby respond_with do |data| success_result("Post published successfully", data) end ``` -------------------------------- ### Service Usage Example Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/07_action_service.md Demonstrates how to instantiate and call the Article::PublishService with parameters for scheduling, immediate publishing, and subscriber notification control. ```ruby # Usage result = Article::PublishService.new(current_user, params: { id: 123, publish_at: 2.hours.from_now, notify_subscribers: true }).call ``` -------------------------------- ### Ruby: Test Nested Workflow Branches Source: https://github.com/alessiobussolari/better_service/blob/main/docs/testing.md Tests decision-making in nested branches within a workflow. This example verifies that both outer and inner branch decisions are correctly evaluated. ```ruby test "nested branch decisions" do result = Document::ApprovalWorkflow.new(@user, params: { document_id: high_value_contract.id }).call assert result[:success] # Verify both outer and inner branch decisions assert_equal 2, result[:metadata][:branches_taken].count assert_includes result[:metadata][:branches_taken], "branch_1:on_1" # Contract type assert_includes result[:metadata][:branches_taken], "nested_branch_1:on_1" # High value end ``` -------------------------------- ### Basic Product Listing Service Example Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/02_index_service.md A complete example of a Product::IndexService for basic product listing. It defines the model class, sets up a schema for pagination parameters, and specifies how to retrieve active products in descending order of creation time. ```ruby module Product class IndexService < BetterService::IndexService model_class Product schema do optional(:page).filled(:integer, gteq?: 1) optional(:per_page).filled(:integer, gteq?: 1, lteq?: 100) end search_with do { items: model_class.active.order(created_at: :desc) } end end end # Usage result = Product::IndexService.new(current_user, params: { page: 1, per_page: 20 }).call products = result[:items] ``` -------------------------------- ### Ruby: Test Conditional Steps Skipping Source: https://github.com/alessiobussolari/better_service/blob/main/docs/testing.md Verifies that optional steps in a workflow are skipped when their conditions are not met. This example checks if a premium email is skipped for a small cart. ```ruby test "skips optional steps when condition not met" do cart = carts(:small_cart) # Total < $100 result = Order::CheckoutWorkflow.new(@user, params: { cart_id: cart.id }).call assert result[:success] assert_includes result[:metadata][:steps_skipped], :send_premium_email end ``` -------------------------------- ### BetterService Success Response Structure (JSON) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Describes the typical structure of a successful response from a BetterService execution, including a success flag, message, resource data, and metadata. ```json { "success": true, "message": "Operation successful", "resource": , # or items: [...] "metadata": { "action": :created # or :updated, :deleted, :show, :index } } ``` -------------------------------- ### Complete Advanced Search Example Source: https://github.com/alessiobussolari/better_service/blob/main/context7/repository/02-base-repository-examples.md Presents a comprehensive example combining multiple advanced search options: filtering with predicates, custom pagination, eager loading of associations, and ordering. This showcases the power and flexibility of the `search` method. ```ruby results = repo.search( { published_eq: true, price_gteq: 50, name_cont: "premium" }, page: 1, per_page: 20, includes: [:category, :reviews], order: { created_at: :desc } ) ``` -------------------------------- ### Use Service Object in Controller Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Demonstrates how to instantiate and call a `Post::CreateService` from a Rails controller. It includes handling potential validation and database errors raised by the service. ```ruby class PostsController < ApplicationController def create result = Post::CreateService.new(current_user, params: post_params).call render json: result, status: :created rescue BetterService::Errors::Runtime::ValidationError => e render json: { error: "Validation failed", errors: e.context[:validation_errors] }, status: :unprocessable_entity rescue BetterService::Errors::Runtime::DatabaseError => e render json: { error: e.message }, status: :unprocessable_entity end private def post_params params.require(:post).permit(:title, :body, :published) end end ``` -------------------------------- ### Execute User Registration Workflow Source: https://github.com/alessiobussolari/better_service/blob/main/docs/workflows/05_workflow_examples.md This Ruby code demonstrates how to instantiate and call the User::RegistrationWorkflow. It initializes the workflow with `nil` context and provides parameters for the registration process, including email, password, names, referral code, and newsletter preference. After successful execution, it sets the user's session ID and redirects to the dashboard. ```ruby # Usage result = User::RegistrationWorkflow.new(nil, params: { email: "john@example.com", password: "SecurePass123", password_confirmation: "SecurePass123", first_name: "John", last_name: "Doe", referral_code: "FRIEND20", newsletter: true }).call session[:user_id] = result[:user].id redirect_to dashboard_path ``` -------------------------------- ### Ruby: Product DestroyService with Authorization Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/01-services-overview.md Illustrates a DestroyService for 'Product' with custom authorization logic. It ensures the user is an admin or owns the product before allowing deletion. ```ruby class Product::DestroyService < BetterService::DestroyService authorize_with do product = Product.find(params[:id]) user.admin? || product.user_id == user.id end search_with do { resource: Product.find(params[:id]) } end process_with do |data| data[:resource].destroy! { resource: data[:resource] } end end ``` -------------------------------- ### Workflow Example: Content Publishing Source: https://github.com/alessiobussolari/better_service/blob/main/docs/workflows/01_workflows_introduction.md A BetterService::Workflow demonstrating a content publishing process. This includes steps for content validation, SEO generation, image optimization, publishing, indexing, and notifications. ```ruby class Article::PublishWorkflow < BetterService::Workflow step :validate_content, with: Article::ValidateService step :generate_seo, with: Article::GenerateSEOService step :optimize_images, with: Article::OptimizeImagesService step :publish_article, with: Article::PublishService step :index_search, with: Article::IndexSearchService step :notify_subscribers, with: Article::NotifySubscribersService step :post_to_social, with: Article::PostToSocialService, if: :share_on_social? end ``` -------------------------------- ### Basic Product Show Service with Schema Source: https://github.com/alessiobussolari/better_service/blob/main/docs/services/03_show_service.md Demonstrates a basic `Product::ShowService` that includes a schema definition to validate the presence and type of the `id` parameter. It also defines how to search for the resource using `search_with`. ```ruby module Product class ShowService < BetterService::ShowService model_class Product schema do required(:id).filled(:integer) end search_with do { resource: model_class.includes(:category, :reviews).find(params[:id]) } end end end # Usage result = Product::ShowService.new(current_user, params: { id: 123 }).call product = result[:resource] ``` -------------------------------- ### Synchronous Cache Invalidation Example (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/cache-invalidation.md Demonstrates synchronous cache invalidation for a user's 'products' data. This method ensures immediate consistency but can block the request. ```ruby BetterService::CacheService.invalidate_for_context(user, "products") ``` -------------------------------- ### Content Processing Workflow Usage Example Source: https://github.com/alessiobussolari/better_service/blob/main/docs/workflows/05_workflow_examples.md Demonstrates how to instantiate and call the `Content::ProcessingWorkflow` with specific parameters and shows how to access metadata about the branches taken during execution. ```ruby # Usage result = Content::ProcessingWorkflow.new(current_user, params: { content_id: 789, priority: 'high', auto_publish: false }).call # Branch taken result[:metadata][:branches_taken] ``` -------------------------------- ### Define Validation Schema (Ruby) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Defines the validation schema for input parameters using BetterService's `schema` block. It specifies required and optional fields with their types and constraints. ```ruby schema do required(:email).filled(:string) required(:age).filled(:integer, gteq?: 18) optional(:newsletter).maybe(:bool) end ``` -------------------------------- ### Access StatsSubscriber Statistics in Ruby Source: https://github.com/alessiobussolari/better_service/blob/main/docs/advanced/instrumentation.md Demonstrates how to access and retrieve statistics collected by the StatsSubscriber in Ruby. Includes examples for getting all stats, stats for a specific service, a summary, and resetting stats. ```ruby # Access all statistics stats = BetterService::Subscribers::StatsSubscriber.stats # => { # "Product::CreateService" => { # executions: 150, # successes: 148, # failures: 2, # total_duration: 4500.0, # avg_duration: 30.0, # cache_hits: 0, # cache_misses: 0, # errors: { # "ActiveRecord::RecordInvalid" => 2 # } # }, # "Product::IndexService" => { ... } # } # Get statistics for a specific service service_stats = BetterService::Subscribers::StatsSubscriber.stats_for("Product::CreateService") # => { executions: 150, successes: 148, ... } # Get aggregated summary across all services summary = BetterService::Subscribers::StatsSubscriber.summary # => { # total_services: 5, # total_executions: 1250, # total_successes: 1235, # total_failures: 15, # success_rate: 98.8, # avg_duration: 28.5, # cache_hit_rate: 75.5 # } # Reset all statistics (useful for testing or periodic reset) BetterService::Subscribers::StatsSubscriber.reset! ``` -------------------------------- ### Generate All CRUD Services (Bash) Source: https://github.com/alessiobussolari/better_service/blob/main/docs/start/getting-started.md Generates a full set of CRUD (Create, Read, Update, Delete) service objects for a given model using the `serviceable:scaffold` Rails generator. ```bash rails generate serviceable:scaffold Product ``` -------------------------------- ### Create Resource with Validation using BetterService Source: https://github.com/alessiobussolari/better_service/blob/main/context7/services/04-create-service-examples.md Demonstrates basic resource creation using BetterService::CreateService with schema validation for required and optional fields. It takes user and params as input and returns the created resource. Dependencies include the BetterService gem and the model class (e.g., Product). ```ruby class Product::CreateService < BetterService::CreateService model_class Product schema do required(:name).filled(:string) required(:price).filled(:decimal, gt?: 0) optional(:description).maybe(:string) end process_with do |data| resource = model_class.create!(params) { resource: resource } end end # Usage result = Product::CreateService.new(current_user, params: { name: "Laptop", price: 999.99 }).call product = result[:resource] ```