### Get Cache Key with Version Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Illustrates how to get a combined cache key and version string. ```ruby publisher.cache_key_with_version # => "post/publishers/1-20240619103000000" ``` -------------------------------- ### Setup Local Development Environment Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Run this command after checking out the repository to install dependencies for local development. ```bash bin/setup ``` -------------------------------- ### Controller Setup for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Example controller actions demonstrating how to find and set up associated objects, including using `before_action` callbacks. ```ruby class Post::PublishersController < ApplicationController def edit @post = Post.find(params[:post_id]) @publisher = @post.publisher end def update @post = Post.find(params[:post_id]) @publisher = @post.publisher # Update logic end end ``` -------------------------------- ### Associated Object Test Setup with FactoryBot Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Provides example test setup code using `FactoryBot.build` to create an instance of a namespaced Associated Object (`Post::Comment::Rating`) for testing. ```ruby class Post::Comment::RatingTest < ActiveSupport::TestCase setup { @rating = posts(:one).comments.first.rating } setup { @rating = FactoryBot.build(:post_comment).rating } test "pretty, pretty, pretty, pretty good" do assert @rating.good? end end ``` -------------------------------- ### Get Cache Key Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Demonstrates how to obtain the cache key for an associated object. ```ruby publisher.cache_key # => "post/publishers/1" Post.new.publisher.cache_key # => "post/publishers/new" ``` -------------------------------- ### Kredis Namespace Format Examples Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Provides examples of the key format used by Kredis for namespacing, illustrating different data types and composite keys. ```text ::: Examples: post:publishers:1:publish_at account:billing:42:plan_expires_at post:comment:ratings:[1, 5]:moderated user:subscription:99:features (set) ``` -------------------------------- ### Get Cache Version Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Shows how to retrieve the cache version from an associated object. ```ruby publisher.cache_version # => 20240619103000000 ``` -------------------------------- ### Install Gem Locally Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Install the gem onto your local machine using rake and bundle exec. ```bash bundle exec rake install ``` -------------------------------- ### Model Declaration Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md An example of a `Post` model file after the `has_object :publisher` declaration has been added by the generator. ```ruby # Original file: app/models/post.rb class Post < ApplicationRecord has_many :comments end # After running generator: class Post < ApplicationRecord has_many :comments has_object :publisher end ``` -------------------------------- ### Associated Object Class Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md An example of a generated Associated Object class file for `Post::Publisher`. ```ruby # Generated file: app/models/post/publisher.rb class Post::Publisher < ActiveRecord::AssociatedObject extension do # Extend Post here end end ``` -------------------------------- ### Associated Object Test Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md An example of a generated test file for `Post::Publisher`. ```ruby # Generated file: test/models/post/publisher_test.rb require "test_helper" class Post::PublisherTest < ActiveSupport::TestCase setup do # @post = posts(:TODO_fixture_name) # @publisher = @post.publisher end end ``` -------------------------------- ### Start Interactive Console Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Use this command to open an interactive Ruby console for experimenting with the gem. ```bash bin/console ``` -------------------------------- ### Install Gem Manually Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md If bundler is not managing dependencies, use this command to install the gem directly. ```bash $ gem install active_record-associated_object ``` -------------------------------- ### Example of Database Transaction Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md This example demonstrates using the `transaction` method to update a `Post` record within a database transaction. ```Ruby Post::Publisher.transaction do post = Post.first post.update! published: true end ``` -------------------------------- ### Add Gem to Gemfile Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/configuration.md To use the gem, add it to your Gemfile and run bundle install. ```ruby # Gemfile gem "active_record-associated_object" ``` -------------------------------- ### Example of Extending Associated Record Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md This example shows how to use the `extension` method to add `has_many` association, a class method `with_contracts`, and an `after_create_commit` callback to the `Post` model via `Post::Publisher`. ```Ruby class Post::Publisher < ActiveRecord::AssociatedObject extension do has_many :contracts, dependent: :destroy def self.with_contracts includes(:contracts) end after_create_commit :publish_later, if: -> { contracts.signed? } end end ``` -------------------------------- ### Example Usage of `associated_via` Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Demonstrates how to define a `Post::Publisher` Associated Object and associate it with the `Post` model using `associated_via`. ```Ruby class Post::Publisher < ActiveRecord::AssociatedObject associated_via(Post) # Called automatically during inheritance end ``` -------------------------------- ### Example Trigger for Record Class Not Found Error Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/errors.md Provides an example of triggering the 'Record class does not exist' error by attempting to generate an associated object for a non-existent model. ```bash # If Organization model class doesn't exist: bin/rails generate associated Organization::Billing # Error: Record class 'Organization' does not exist ``` -------------------------------- ### Kredis Automatic Namespacing Examples Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Demonstrates how Kredis automatically namespaces keys based on the Associated Object's class, ID, and attribute name. ```ruby # For Post::Publisher with post.id = 1: publisher.publish_at.key # => "post:publishers:1:publish_at" # For Post::Comment::Rating with post_id = 1, author_id = 5: rating.moderated.key # => "post:comment:ratings:[1, 5]:moderated" ``` -------------------------------- ### Callback Composition for Post Publisher Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Callbacks are forwarded from the parent model to the Associated Object. This example defines a `before_destroy` callback. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject def before_destroy throw :abort if record.published? end end ``` -------------------------------- ### Usage Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Access the associated object through the generated accessor method. This allows direct interaction with the encapsulated domain logic. ```ruby post = Post.find(1) post.publisher # => ``` ```ruby post = Post.find(1) post.publisher.publish # Publishes the post ``` -------------------------------- ### Composite Primary Keys Example Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/configuration.md Demonstrates how to use the gem with Active Record models that have composite primary keys. No special configuration is needed as composite keys are detected automatically. ```ruby class Post::Comment < ApplicationRecord self.primary_key = [:post_id, :author_id] has_object :rating end class Post::Comment::Rating < ActiveRecord::AssociatedObject end # Works out of the box: rating = Post::Comment.find([1, 5]).rating rating.id # => [1, 5] rating.to_param # => "1-5" rating.to_gid # => "gid://app/Post::Comment::Rating/1/5" ``` -------------------------------- ### Write Tests for Associated Object Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md Write tests for your Associated Object to ensure its functionality. This example demonstrates testing the `publish` method. ```ruby class Post::PublisherTest < ActiveSupport::TestCase setup do @post = posts(:one) @publisher = @post.publisher end test "publish updates published_at" do @publisher.publish assert @post.reload.published? end end ``` -------------------------------- ### Basic Associated Object Setup and Usage Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/00-START-HERE.md This snippet demonstrates the fundamental steps to define and utilize an Associated Object. First, create a class inheriting from `ActiveRecord::AssociatedObject` and define its methods. Then, declare the association on your model using `has_object`. Finally, you can interact with the associated object instance. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject def publish record.update! published_at: Time.current end end class Post < ApplicationRecord has_object :publisher end post = Post.find(1) post.publisher.publish ``` -------------------------------- ### Associated Object View Partial Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Example of a partial view file for rendering an associated object, typically used with the `render` helper. ```erb

<%= link_to_unless @publisher.new_record?, "Published", post_path(@publisher.post) %>

Published at: <%= @publisher.post.published_at&.strftime('%B %d, %Y') %>

``` -------------------------------- ### Model Declaration with Callback Forwarding Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md This example shows how to declare an associated object on a model, enabling callback forwarding. The `before_destroy: true` option ensures the callback is processed. ```ruby class Post < ApplicationRecord has_object :publisher, before_destroy: true end ``` -------------------------------- ### Get Partial View Path with to_partial_path Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Returns the string path to the associated partial view for rendering. ```ruby publisher.to_partial_path # => "post/publishers/publisher" ``` -------------------------------- ### Implement Associated Object Methods Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md After generation, implement the necessary methods within the Associated Object class. This example shows how to define a `publish` method that updates a record and notifies subscribers. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject extension do has_many :distribution_logs, dependent: :destroy end def publish transaction do record.update! published_at: Time.current record.subscribers.notify_published end end end ``` -------------------------------- ### Get URL Parameter String with to_param Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Returns the primary key value as a string, suitable for use in URL parameters. ```ruby publisher.to_param # => "1" ``` -------------------------------- ### Include Shared Behavior with Modules Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Use modules to share behavior across different associated objects. This example shows how to include a `Billable` module to add `monthly_cost` functionality. ```ruby # app/models/billable.rb module Billable def self.included(object) object.extension do # Add billing methods to the record end end def monthly_cost record.billing_rate * 30 end end # app/models/account/billing.rb class Account::Billing < ActiveRecord::AssociatedObject include Billable end # app/models/user/billing.rb class User::Billing < ActiveRecord::AssociatedObject include Billable end # Usage: account.billing.monthly_cost user.billing.monthly_cost ``` -------------------------------- ### Rendering Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Examples of rendering associated objects using Rails conventions. It demonstrates rendering a `Post::Publisher` and a `Post::Comment::Rating` by their conventional partial paths. ```erb <%# With a Post::Publisher, this renders app/views/post/publishers/_publisher.html.erb %> ``` ```erb <%= render publisher %> ``` ```erb <%# With a Post::Comment::Rating, this renders app/views/post/comment/ratings/_rating.html.erb %> ``` ```erb <%= render rating %> ``` -------------------------------- ### Single Responsibility for Post Publisher Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Each Associated Object class encapsulates a single domain concept. This example shows a Post::Publisher class. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject def publish; end def retract; end end ``` -------------------------------- ### Get Cache Key with Version Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Returns the cache key combined with the cache version. A hyphen separates the key and version, unless no version exists. ```ruby def cache_key_with_version ``` ``` -------------------------------- ### Associated Object Test Template Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md This is the template content for the generated Associated Object test file. It includes a setup block for initializing fixtures. ```ruby require "test_helper" class <%= name %>Test < ActiveSupport::TestCase setup do # @<%= record_path %> = <%= record_path.pluralize %>(:TODO_fixture_name) # @<%= associated_object_path %> = @<%= record_path %>.<%= associated_object_path %> end end ``` -------------------------------- ### Example Trigger for Cache Versioning RuntimeError Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/errors.md Demonstrates how to trigger the RuntimeError by disabling cache versioning on a Post model and then attempting to access the cache key of its publisher. ```ruby class Post < ApplicationRecord self.cache_versioning = false # Disable cache versioning end post = Post.first post.publisher.cache_key # Raises RuntimeError ``` -------------------------------- ### Gem File Tree Structure Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/MANIFEST.md This tree displays the organization of files within the gem's documentation. It includes the main README, this manifest file, API references, type definitions, configuration guides, error catalogs, and integration patterns. ```text output/ ├── README.md (Main entry point and overview) ├── MANIFEST.md (This file) ├── api-index.md (Complete alphabetical API index) ├── api-reference/ │ ├── associated-object.md (Base class API) │ ├── has-object.md (Macro documentation) │ └── generator.md (Generator API) ├── types.md (Type definitions) ├── configuration.md (Configuration guide) ├── errors.md (Error catalog) └── integration-guide.md (Integration patterns) ``` -------------------------------- ### Single Responsibility for Post Archiver Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Each Associated Object class encapsulates a single domain concept. This example shows a Post::Archiver class. ```ruby class Post::Archiver < ActiveRecord::AssociatedObject def archive; end def restore; end end ``` -------------------------------- ### API Reference: has_object Macro Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/MANIFEST.md Documents the `has_object` macro, explaining its signature, parameters, behavior, and how it generates methods for associations. Includes numerous examples covering various association scenarios and error handling. ```APIDOC ## has_object Macro Documentation ### Description This section provides a complete guide to the `has_object` macro, detailing its usage, configuration, and the methods it generates for managing associated objects. It includes extensive examples and covers error handling. ### Contents - Full macro signature and parameters - Detailed behavior description - Object name resolution logic - Method generation explanation - Callback forwarding documentation - Comprehensive examples covering various association types and configurations - Error handling and edge cases - Instance variable management - Supported callbacks list - Generator integration - Related documentation ``` -------------------------------- ### API Reference: Associated Object Class Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/MANIFEST.md Provides comprehensive documentation for the base Associated Object class, including all class and instance methods, their signatures, parameters, return values, and usage examples. It also covers integrations with Active Model, Kredis, and GlobalID. ```APIDOC ## Associated Object Class API Reference ### Description This section details the `AssociatedObject` class, the core component of the gem. It covers all public methods available for class and instance usage, providing in-depth explanations and practical examples. ### Contents - Class definition and inheritance - All class methods with full signatures - All instance methods with full signatures - Method parameters, returns, and raises - Usage examples for each method - Caching methods documentation - Active Model integration details - Automatic integrations (Kredis, GlobalID, Active Job) - Ruby version requirements - Related classes ``` -------------------------------- ### Call Associated Object Method in Application Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md Integrate the Associated Object's functionality into your application code by calling its methods. This example shows how to call the `publish` method on a post's publisher. ```ruby post = Post.find(params[:id]) post.publisher.publish ``` -------------------------------- ### Define a basic Post model Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md This snippet shows the basic definition of an Active Record model named Post. It serves as a starting point for demonstrating associated objects. ```ruby class Post < ApplicationRecord end ``` -------------------------------- ### Automatic Kredis Integration for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Shows how to use Kredis types directly within an Associated Object, similar to Active Record. The `kredis_datetime` example uses a namespaced key based on the Associated Object's context. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject kredis_datetime :publish_at # Uses a namespaced "post:publishers::publish_at" key. end ``` -------------------------------- ### Associated Object with Acronyms Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/has-object.md Illustrates how to handle acronyms in associated object names by defining them in Rails inflections. `Provider::OAuthScopes` is used as an example. ```ruby # config/initializers/inflections.rb ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym "OAuth" end # Then in your model: class Provider < ApplicationRecord has_object :oauth_scopes # Looks for Provider::OAuthScopes end ``` -------------------------------- ### Callback Forwarding Configuration Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/has-object.md Example of configuring Active Record callback forwarding to an associated object using the has_object macro. This allows callbacks like 'after_touch' or 'before_destroy' to be handled by the associated object. ```ruby has_object :publisher, after_touch: true, before_destroy: :prevent_errant_post_destroy ``` -------------------------------- ### Get Associated Object File Path Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md A private helper method that returns the underscore-cased name, representing the file path for the Associated Object. ```ruby def associated_object_path ``` -------------------------------- ### Delegation via Extension in Post Publisher Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Model extensions for an Associated Object are defined within an extension block. This example adds associations and scopes to Post::Publisher. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject extension do has_many :distribution_logs, dependent: :destroy scope :published, -> { where(published_at: ...Time.current) } end end ``` -------------------------------- ### Define Composite Primary Key and has_object Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Example of defining a model with a composite primary key and using the has_object association. This allows direct access to associated objects using composite keys. ```ruby class Post::Comment < ApplicationRecord self.primary_key = [:post_id, :author_id] has_object :rating end # In production: rating = Post::Comment.find([1, 5]).rating # Works ``` -------------------------------- ### Initialization & Configuration Methods Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Methods for initializing and configuring Associated Objects. ```APIDOC ## `initialize(record)` ### Description Creates an instance of AssociatedObject with the given record. ### Class AssociatedObject ### Module Instance --- ## `associated_via(record)` ### Description Configures the association for the given record. ### Class AssociatedObject ### Module Class --- ## `has_object(*names, **callbacks)` ### Description Declares an associated object on a model. ### Class (Active Record) ### Module Class --- ## `init_internals` ### Description Initializes internal variables for the Associated Object. ### Class (Active Record) ### Module Instance --- ## `extension(&block)` ### Description Extends the record class associated with this object. ### Class AssociatedObject ### Module Class ``` -------------------------------- ### View Generator Help Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md Use this command to view detailed help information for the associated object generator. ```bash bin/rails generate associated --help ``` -------------------------------- ### Advanced `performs` Configuration with Options Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Demonstrates advanced configuration for the `performs` macro, including specifying `queue_as`, `discard_on`, and `retry_on` options for custom job behavior. ```ruby performs :publish, queue_as: :important, discard_on: SomeError do retry_on TimeoutError, wait: :exponentially_longer end ``` -------------------------------- ### Get Gem Version Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/configuration.md Retrieves the current version of the ActiveRecord::AssociatedObject gem. ```ruby module ActiveRecord class AssociatedObject VERSION = "1.0.0" end end ``` ```ruby ActiveRecord::AssociatedObject::VERSION # => "1.0.0" ``` -------------------------------- ### initialize(record) Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Creates a new Associated Object instance, storing a reference to the associated record. This is the constructor for the AssociatedObject. ```APIDOC ## initialize(record) ### Description Creates a new Associated Object instance, storing a reference to the associated record. ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - None ### Method `initialize` ### Endpoint N/A (Instance method) ### Request Example ```ruby associated_object = ActiveRecord::AssociatedObject.new(record_instance) ``` ### Response #### Success Response - `nil` (Implicit return of constructor) #### Response Example ```ruby # No explicit return value for initialization ``` ``` -------------------------------- ### Get Associated Record Path Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md A private helper method that returns the underscore-cased path to the associated record model. ```ruby def record_path ``` -------------------------------- ### initialize Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Creates a new instance of the Associated Object, storing a reference to the associated Active Record model. ```APIDOC ## `initialize(record)` ### Description Creates a new instance of the Associated Object, storing a reference to the associated Active Record model. ### Parameters #### Path Parameters - **record** (`ActiveRecord::Base`) - Required - An instance of the associated Active Record class ### Returns None ### Example: ```ruby post = Post.find(1) publisher = Post::Publisher.new(post) ``` ``` -------------------------------- ### Get Primary Key Array with to_key Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Returns an array of primary key values for the object. Useful for identifying records. ```ruby publisher.to_key # => [1] rating.to_key # => [1] # For composite keys with post_id=1, author_id=5 ``` -------------------------------- ### Get Associated Object Class Name Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md A private helper method that returns the unmodulized class name of the Associated Object. ```ruby def associated_object_class ``` -------------------------------- ### Configure Kredis with Redis URL Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Configure Kredis to connect to Redis using a URL from the environment. This initializer should be placed in config/initializers/kredis.rb. ```ruby Kredis.configure do |config| config.redis = Redis.new(url: ENV['REDIS_URL']) end ``` -------------------------------- ### Get Associated Record ID Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Delegates to the associated record's id method to retrieve the primary key value. ```ruby delegate :id, to: :record ``` ``` -------------------------------- ### Controller for Publishing Posts Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md This controller demonstrates how to use an associated object for publishing actions. It finds the associated publisher and uses its `publish` method. ```ruby class Post::PublishersController < ApplicationController before_action :set_publisher def new end def create @publisher.publish params.expect(publisher: :toast) redirect_back_or_to root_url, notice: "Out it goes!" end private def set_publisher # Associated Objects are POROs, so behind the scenes we're really doing `Post.find(…).publisher`. @publisher = Post::Publisher.find(params[:id]) end end ``` -------------------------------- ### Implementing Per-User Data Isolation Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Demonstrates how to implement per-user data isolation for Kredis values, as default namespacing is by record ID, not user. ```ruby # This namespaces by record ID, not by user: publisher1.publish_at.value = Time.current publisher2.publish_at.value = Time.current # If you need per-user isolation, implement it: class Post::Publisher < ActiveRecord::AssociatedObject def user_publish_at(user) # Use Redis directly with user-specific key end end ``` -------------------------------- ### Get Associated Record Class Name Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md A private helper method that returns the unmodulized and camelized class name of the associated record. ```ruby def record_klass ``` -------------------------------- ### Get Associated Record Updated On Date Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Delegates to the associated record's updated_on method to retrieve the date of the last update. ```ruby delegate :updated_on, to: :record ``` ``` -------------------------------- ### Release New Version Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Follow these steps to update the version, commit, tag, and release the gem to rubygems.org. ```bash bundle exec rake release ``` -------------------------------- ### Controller Integration with Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Illustrates controller logic for interacting with associated objects, including publishing actions and HTTP caching with `fresh_when`. ```ruby class Post::PublishersController < ApplicationController before_action :set_post before_action :set_publisher def create # @publisher is already cached on @post @publisher.publish redirect_to @post, notice: "Published!" end def show # Works with fresh_when for HTTP caching fresh_when @publisher end private def set_post @post = Post.find(params[:post_id]) end def set_publisher @publisher = @post.publisher end end ``` -------------------------------- ### Get Associated Record Updated At Timestamp Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Delegates to the associated record's updated_at method to retrieve the timestamp of the last update. ```ruby delegate :updated_at, to: :record ``` ``` -------------------------------- ### Run Tests Locally Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Execute this command to run the test suite for the gem in your local development environment. ```bash rake test ``` -------------------------------- ### Signing and Verifying GlobalIDs Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Explains how to create a signed GlobalID for an associated object with an expiration time and how to later verify and locate the object using the signed ID. ```ruby # Create signed GlobalID: signed_gid = @publisher.to_signed_gid(expires_in: 1.hour) # Later verify and find: GlobalID::Locator.locate_signed(signed_gid) ``` -------------------------------- ### Associated Object with Callbacks Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Integrates Rails callbacks into an associated object to trigger actions before or after record events. This example uses `after_create_commit` and `before_destroy` callbacks. ```ruby class Post < ApplicationRecord has_object :publisher, after_create_commit: :schedule_announcement, before_destroy: :prevent_destroy_if_published end class Post::Publisher < ActiveRecord::AssociatedObject def schedule_announcement AnnouncementJob.perform_later(self) end def prevent_destroy_if_published throw :abort if record.published? end end ``` -------------------------------- ### Get Associated Object Name with `attribute_name` Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md The `attribute_name` attribute reader returns the symbol representing the name of the Associated Object, derived from its class name. ```Ruby attr_reader :attribute_name ``` -------------------------------- ### Testing an Associated Object Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Demonstrates how to test an associated object, like a Post::Publisher, using ActiveSupport::TestCase. Ensure your test file follows the naming convention app/models/post.rb and app/models/post/publisher.rb to test post/publisher_test.rb. ```ruby class Post::PublisherTest < ActiveSupport::TestCase # You can use Fixtures/FactoryBot to get a `post` and then extract its `publisher`: setup { @publisher = posts(:one).publisher } setup { @publisher = FactoryBot.build(:post).publisher } test "publish updates the post" do @publisher.publish assert @publisher.post.reload.published? end end ``` -------------------------------- ### Form Integration for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Shows how to integrate forms for associated objects using `form_with` in Rails. Includes example of updating a field related to the associated object. ```erb <%= form_with model: @publisher, local: true do |form| %> <%= form.text_field :note, value: @publisher.post.notes %> <%= form.submit "Update" %> <% end %> ``` -------------------------------- ### Check Method Responsiveness Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Implement `respond_to_missing?` to indicate whether a method can be handled by `method_missing`, aiding in dynamic method dispatch. ```ruby def self.respond_to_missing?(meth, ...) end ``` -------------------------------- ### Instance Methods on Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Illustrates instance methods for associated objects, including accessing the parent record, ID, record status (new_record?, persisted?), timestamps, caching utilities, and methods for model conversion and comparison. ```ruby publisher = post.publisher publisher.record # => post publisher.id # => 1 publisher.new_record? # => false publisher.persisted? # => true publisher.updated_at # => 2024-06-19 10:30:00 UTC publisher.transaction { } # => executes transaction publisher.cache_key # => "post/publishers/1" publisher.cache_version # => 20240619103000000 publisher.cache_key_with_version # => "post/publishers/1-20240619103000000" publisher.to_model # => publisher publisher.to_key # => [1] publisher.to_param # => "1" publisher.to_partial_path # => "post/publishers/publisher" publisher == other_publisher # => true/false based on class and id ``` -------------------------------- ### Associated Object Class Method Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md This private Ruby method returns the demodulized class name of the associated object. For example, input 'Post::Publisher' yields 'Publisher'. ```ruby def associated_object_class name.camelize.demodulize end ``` -------------------------------- ### Under-the-hood implementation of Associated Object Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md This snippet reveals the internal mechanism of how ActiveRecord::AssociatedObject works. It demonstrates the initializer and the instance variable management for associated objects. ```ruby class Post::Publisher attr_reader :post def initialize(post) = @post = post end class Post < ApplicationRecord def publisher = (@associated_objects ||= {})[:publisher] ||= Post::Publisher.new(self) end ``` -------------------------------- ### Alternative using ActiveSupport::Concern Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Presents a traditional approach using `ActiveSupport::Concern` to integrate features into an Active Record model. This serves as a comparison to the `extension` block method, highlighting differences in file structure and code organization. ```ruby class Post < ApplicationRecord include Published end # app/models/post/published.rb module Post::Published extend ActiveSupport::Concern included do has_many :contracts, dependent: :destroy do def signed? = all?(&:signed?) end has_object :publisher after_create_commit :publish_later, if: -> { contracts.signed? } end class_methods do def with_contracts = includes(:contracts) end # An integrating method that operates on `publisher`. private def publish_later = publisher.publish_later end ``` -------------------------------- ### Manual Active Job Implementation for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md This snippet shows the manual implementation of Active Job classes (`Job`, `PublishJob`, `RetractJob`) that `performs` macro abstracts away. It highlights the GlobalID integration for passing the Associated Object to the job. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject # `performs` without a method defines a general job to share between method jobs. class Job < ApplicationJob queue_as :important end # Individual method jobs inherit from the `Post::Publisher::Job` defined above. class PublishJob < Job # Here's the GlobalID integration again, i.e. we don't have to do `post.publisher`. def perform(publisher, *, **) = publisher.publish(*, **) end class RetractJob < Job def perform(publisher, *, **) = publisher.retract(*, **) end def publish_later(*, **) = PublishJob.perform_later(self, *, **) def retract_later(*, **) = RetractJob.perform_later(self, *, **) end ``` -------------------------------- ### Generate Associated Object Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md Example of generating an associated object named 'Organization::Seats'. This command creates model and test files and modifies the associated record to include a `has_object` association. ```bash rails generate associated Organization::Seats ``` -------------------------------- ### All Available Callbacks for has_object Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Demonstrates the full range of Active Record callbacks that can be forwarded using the `has_object` macro. ```ruby class Post < ApplicationRecord has_object :publisher, after_initialize: true, after_find: true, before_save: true, after_save: true, before_create: true, after_create: true, before_update: true, after_update: true, before_destroy: true, after_destroy: true, after_touch: true, after_commit: true, after_create_commit: true, after_update_commit: true, after_destroy_commit: true end ``` -------------------------------- ### Callback Overhead Comparison Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Compares the usage of `after_touch` with forwarded callbacks to the equivalent explicit callback definition. Forwarded callbacks offer minimal overhead. ```ruby class Post < ApplicationRecord has_object :publisher, after_touch: true end # This is equivalent to: class Post < ApplicationRecord after_touch { publisher.after_touch } end ``` -------------------------------- ### Record Class Method Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md This private Ruby method returns the camelized class name of the associated model, excluding the demodulized part. For example, input 'Post::Publisher' yields 'Post'. ```ruby def record_klass name.camelize.deconstantize end ``` -------------------------------- ### Basic Kredis Methods for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Defines various Kredis-backed attributes (datetime, string, counter, flag, list, set, hash) on an Associated Object and shows their basic usage. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject kredis_datetime :publish_at kredis_string :slug kredis_counter :view_count kredis_flag :featured kredis_list :keywords kredis_set :tags kredis_hash :metadata end # Usage: publisher = post.publisher publisher.publish_at.value = Time.current publisher.slug.value = "my-post" publisher.view_count.increment publisher.featured.mark publisher.keywords.push "ruby", "rails" publisher.tags.add "important" publisher.metadata[:author] = "John" ``` -------------------------------- ### Reducing Active Job Boilerplate with `performs` Macro Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Utilize the `performs` macro from `active_job-performs` to simplify Active Job definitions for Associated Objects. This example shows defining jobs for `publish` and `retract` actions. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject performs queue_as: :important performs :publish performs :retract def publish end def retract(reason:) end end ``` -------------------------------- ### ActiveModel Naming and Cache Key for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/types.md Associated Objects provide ActiveModel naming methods. Use `model_name` to get the name instance, `element` for the singular form, and `cache_key` for the cache key prefix. ```ruby Post::Publisher.model_name # => ActiveModel::Name for Post::Publisher Post::Publisher.model_name.element # => :publisher Post::Publisher.model_name.cache_key # => "post/publishers" ``` -------------------------------- ### Implementing Authorization for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Shows how to implement authorization logic within an Associated Object. Authorization is not inherited and must be explicitly defined. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject def publish raise "Unauthorized" unless current_user.can?(:publish, record) record.update! published: true end end ``` -------------------------------- ### Generate Cache Key with Version Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/associated-object.md Combines the cache key and version string for the Associated Object. If no version exists, it returns only the cache key. ```ruby def cache_key_with_version "#{cache_key}-#{cache_version}".tap { _1.delete_suffix!("-") } end ``` -------------------------------- ### API Reference: Rails Generator Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/MANIFEST.md Details the Rails generator provided by the gem, including command syntax, parameter descriptions, generated file content, and method specifics. Offers usage examples for different association types and error handling scenarios. ```APIDOC ## Rails Generator Documentation ### Description This section covers the Rails generator used to scaffold associated object functionality. It explains how to use the generator, its parameters, and the code it produces, along with examples and error handling. ### Contents - Command syntax - Parameters description - Generated files with template content - Method details for generator-specific functions - Usage examples for simple and nested associations, plural names, and multiple objects - Error handling and edge cases - Integration with development workflows - Help command reference - Configuration requirements - Related documentation ``` -------------------------------- ### Kredis Key Naming Convention Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Illustrates how each Kredis attribute generates a distinct Redis key, namespaced by the associated object's record ID. ```ruby publisher.publish_at # Redis key: "post:publishers:1:publish_at" publisher.featured # Redis key: "post:publishers:1:featured" ``` -------------------------------- ### respond_to_missing?(meth, ...) Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Reports whether a method can be handled by `method_missing`. ```APIDOC ## `respond_to_missing?(meth, ...)` ### Description Reports whether a method can be handled by `method_missing`. ### Method `self.respond_to_missing?(meth, ...)` ### Parameters #### Path Parameters - **meth** (Symbol) - Method name - **...** (Array) - Additional arguments ### Returns `Boolean` ``` -------------------------------- ### Responder Integration for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Demonstrates how to use Rails responders to handle different formats (HTML, JSON) for associated objects in a controller action. ```ruby class Post::PublishersController < ApplicationController respond_to :html, :json def show @publisher = Post.find(params[:post_id]).publisher respond_with @publisher end end ``` -------------------------------- ### Routing Configuration for Associated Objects Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Configure routes to support Associated Objects by defining resources within the appropriate namespace. ```ruby # config/routes.rb namespace :post do resources :publishers # Works with Associated Objects end ``` -------------------------------- ### Active Job Manual Serialization with GlobalID Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Demonstrates manual conversion of an Associated Object to a GlobalID string for serialization and subsequent deserialization. ```ruby publisher = post.publisher # Convert to GlobalID: gid = publisher.to_gid # => # gid_string = gid.to_s # => "gid://app/Post::Publisher/1" # Later, deserialize: Publisher = GlobalID.find(gid_string) # => publisher # Or with locate_many: publishers = GlobalID::Locator.locate_many( [gid1, gid2, gid3], ignore_missing: true ) ``` -------------------------------- ### Generate Simple Associated Object Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-reference/generator.md Use this command to generate a basic associated object for a model. It creates the model and test files and adds the `has_object` declaration to the parent model. ```bash bin/rails generate associated Post::Publisher ``` -------------------------------- ### Associated Object Usage with Inflections Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/configuration.md Demonstrates how to use Associated Objects after configuring inflections. The `has_object` method will correctly look for capitalized acronyms. ```ruby class Provider < ApplicationRecord has_object :oauth_credentials # Looks for Provider::OAuthCredentials (not Provider::OauthCredentials) has_object :api_keys # Looks for Provider::APIKeys (not Provider::ApiKeys) end ``` -------------------------------- ### Forwarding Callbacks with has_object Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Demonstrates how to forward Active Record callbacks like `after_create_commit`, `after_touch`, and `before_destroy` to an associated object. This allows the associated object to handle lifecycle events of the main record. ```ruby class Post < ActiveRecord::Base # Passing `true` forwards the same name, e.g. `after_touch`. has_object :publisher, after_touch: true, after_create_commit: :publish, before_destroy: :prevent_errant_post_destroy # The above is the same as writing: after_create_commit { publisher.publish } after_touch { publisher.after_touch } before_destroy { publisher.prevent_errant_post_destroy } end class Post::Publisher < ActiveRecord::AssociatedObject def publish end def after_touch # Respond to the after_touch on the Post. end def prevent_errant_post_destroy # Passed callbacks can throw :abort too, and in this example prevent post.destroy. throw :abort if haha_business? end end ``` -------------------------------- ### Rails Helper Integration with ActiveModel::Conversion Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/types.md Demonstrates how ActiveModel::Conversion methods are used by Rails helpers for associated objects. These methods facilitate integration with Rails' routing, forms, and rendering. ```ruby post_publisher_path(@publisher) # Uses to_param form_with(model: @publisher) # Uses model_name and to_key render @publisher # Uses to_partial_path ``` -------------------------------- ### Active Job Serialization with GlobalID Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md Demonstrates how Associated Objects with `GlobalID::Identification` automatically support Active Job serialization. The `PublishJob` can directly perform actions on the `Post::Publisher` PORO. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject class PublishJob < ApplicationJob def perform(publisher) = publisher.publish end def publish_later PublishJob.perform_later self # We're passing this PORO to the job! end def publish # … end end ``` -------------------------------- ### Handle Missing Associated Objects Gracefully Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/integration-guide.md Implement a `begin-rescue` block to catch errors when calling methods on potentially missing associated objects. This logs the error and allows for fallback logic. ```ruby def safe_publisher_call publisher = post.publisher begin publisher.publish rescue => e Rails.logger.error("Publishing failed: #{e.message}") # Fallback logic end end ``` -------------------------------- ### Comparison & Equality Methods Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/api-index.md Methods for comparing Associated Objects. ```APIDOC ## `==(other)` ### Description Compares the Associated Object with another object for equality. ### Class AssociatedObject ### Module Instance ``` -------------------------------- ### Associated Object with publish method Source: https://github.com/kaspth/active_record-associated_object/blob/main/README.md This snippet demonstrates a concrete use case for an Associated Object. The Post::Publisher includes a 'publish' method that interacts with the associated Post record and its subscribers. ```ruby class Post::Publisher < ActiveRecord::AssociatedObject def publish # `transaction` is syntactic sugar for `post.transaction` here. transaction do post.update! published: true post.subscribers.post_published post # There's also a `record` alias available if you prefer the more general reading version: # record.update! published: true # record.subscribers.post_published record end end end ``` -------------------------------- ### Railtie Initialization for Integrations Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/configuration.md The Railtie hooks into Rails initializers to automatically include necessary modules for optional gem integrations like Kredis and GlobalID. ```ruby class ActiveRecord::AssociatedObject::Railtie < Rails::Railtie initializer "integrations.include" do config.after_initialize do ActiveRecord::AssociatedObject.include Kredis::Attributes if defined?(Kredis) ActiveRecord::AssociatedObject.include GlobalID::Identification if defined?(GlobalID) end end initializer "active_job.performs" do require "active_job/performs" ActiveRecord::AssociatedObject.extend ActiveJob::Performs if defined?(ActiveJob::Performs) rescue LoadError # Continues without performs if active_job-performs isn't available end initializer "object_association.setup" do ActiveSupport.on_load :active_record do require "active_record/associated_object/object_association" include ActiveRecord::AssociatedObject::ObjectAssociation end end end ``` -------------------------------- ### Class Methods on Associated Object Classes Source: https://github.com/kaspth/active_record-associated_object/blob/main/_autodocs/README.md Demonstrates common class methods available on associated object classes, such as accessing the associated record, attribute name, primary key, and performing database operations like unscoped, transaction, and find. ```ruby Post::Publisher.record # => Post Post::Publisher.attribute_name # => :publisher Post::Publisher.primary_key # => "id" Post::Publisher.unscoped # => Post::Publisher.transaction { } # => executes transaction Post::Publisher.extension { } # => extends Post class Post::Publisher.find(id) # => finds publisher by delegating to Post.find Post::Publisher.where(id: val) # => finds publishers by delegating to Post.where ```