### Install Dependencies and Run Tests Source: https://github.com/ruby-grape/grape-entity/blob/master/RELEASING.md Installs project dependencies and runs the test suite locally before a release. ```bash bundle install rake ``` -------------------------------- ### Grape API Integration Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Shows a complete example of integrating Grape Entities within a Grape API module, including versioning and conditional presentation. ```ruby module API class Users < Grape::API version 'v1' get '/users' do present User.all, with: UserEntity end get '/users/:id' do user = User.find(params[:id]) present user, with: UserEntity, admin: current_user.admin? end end end The `present` helper automatically uses `Entity.represent`. ``` -------------------------------- ### Delegator.new Examples Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Demonstrates creating delegator instances for different object types: Hash, plain Ruby object, and OpenStruct. ```ruby # Hash object delegator = Delegator.new({ name: 'John', age: 30 }) # => HashObject instance # Plain Ruby object user = User.new(name: 'Jane') delegator = Delegator.new(user) # => PlainObject instance # OpenStruct person = OpenStruct.new(name: 'Bob') delegator = Delegator.new(person) # => OpenStructObject instance ``` -------------------------------- ### BlockExposure Usage Examples Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Demonstrates BlockExposure with one and two parameters, and using the proc option for computed or conditional fields. ```ruby class UserEntity < Grape::Entity # One parameter - receives only object expose :full_name do |user| "#{user.first_name} #{user.last_name}" end # Two parameters - receives object and options expose :email_display do |user, options| options[:masked] ? user.email.gsub(/(.{2}).*(@.*)/, '\1***\2') : user.email end # Using Proc option expose :initials, proc: ->(user, _opts) { "#{user.first_name[0]}#{user.last_name[0]}" } end ``` -------------------------------- ### DelegatorExposure Usage Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Example of using DelegatorExposure to expose simple object attributes like id, name, and email. ```ruby class UserEntity < Grape::Entity expose :id, :name, :email # All use DelegatorExposure end ``` -------------------------------- ### Example Usage of Hash Access Strategies Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Demonstrates how entities behave with different hash key types based on the configured hash_access strategy. ```ruby # Symbol keys (default) response = { status: 'ok', code: 200 } ApiResponseEntity.represent(response) # => { status: 'ok', code: 200 } # String keys (with :to_s) config = { 'api_key' => 'secret', 'environment' => 'prod' } ConfigEntity.represent(config) # => { api_key: 'secret', environment: 'prod' } ``` -------------------------------- ### Grape API Integration Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/README.md Demonstrates how to use Grape::Entity within a Grape API endpoint to present user data. ```ruby module API class Users < Grape::API get '/users/:id' do user = User.find(params[:id]) present user, with: UserEntity, admin: current_user.admin? end end end ``` -------------------------------- ### Install Grape::Entity Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Add the grape-entity gem to your Gemfile and execute bundle to install. ```ruby # Gemfile gem 'grape-entity' # Execute bundle ``` -------------------------------- ### Full Minimal Grape Entity Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md A minimal, self-contained example defining a Grape Entity with various configurations including root element, custom formatter, nested entities, and conditional exposure. ```ruby # Define entity class PostEntity < Grape::Entity root 'posts', 'post' format_with(:timestamp) { |dt| dt.iso8601 } expose :id, :title, :body expose :author, using: UserEntity expose :created_at, format_with: :timestamp expose :published, if: { admin: true } end ``` -------------------------------- ### FormatterBlockExposure Usage Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Example of using FormatterBlockExposure for inline, one-off transformations, such as formatting currency amounts or currency codes. ```ruby class PriceEntity < Grape::Entity expose :amount, format_with: ->(value) { "$%.2f" % value } expose :currency, format_with: ->(code) { code.upcase } end ``` -------------------------------- ### Formatter Inheritance Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Demonstrates how formatters defined in a base entity are inherited by its subclasses. This promotes code reuse and consistency. ```ruby class BaseEntity < Grape::Entity format_with(:timestamp) { |dt| dt.strftime('%Y-%m-%d') } end class ExtendedEntity < BaseEntity expose :created_at, format_with: :timestamp # Uses parent formatter end ``` -------------------------------- ### Entity Inheritance Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Illustrates how Grape Entities support inheritance, allowing for base entities and specialized derived entities. ```ruby class BaseEntity < Grape::Entity expose :id expose :created_at end class UserEntity < BaseEntity expose :name, :email expose :admin, if: { show_admin: true } end class UserDetailedEntity < UserEntity expose :phone, :address unexpose :email end ``` -------------------------------- ### PlainObject Delegator Delegatable? Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Example of using `delegatable?` to check attribute accessibility on a `PlainObject` delegator. ```ruby delegator = Delegator::PlainObject.new(user) delegator.delegatable?(:name) # => true delegator.delegatable?(:unknown) # => false ``` -------------------------------- ### PlainObject Delegator Delegate Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Example of using `PlainObject` delegator to access attributes on a custom Ruby object. ```ruby class User attr_reader :name, :email def initialize(name, email) @name = name @email = email end end user = User.new('John', 'john@example.com') delegator = Delegator::PlainObject.new(user) delegator.delegate(:name) # => 'John' delegator.delegate(:email) # => 'john@example.com' delegator.delegate(:unknown) # => NoMethodError ``` -------------------------------- ### Factory Pattern Examples Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Illustrates the use of the Factory pattern for polymorphic object creation within Grape Entity, specifically for exposures, delegators, and conditions. ```ruby - Exposure.new — Select exposure type based on options - Delegator.new — Select delegator based on object type - Condition.new_if/new_unless — Select condition type based on spec ``` -------------------------------- ### FormatterExposure Usage Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Example of using FormatterExposure to apply a named formatter, like iso_timestamp, to multiple fields such as created_at and updated_at. ```ruby class ArticleEntity < Grape::Entity format_with(:iso_timestamp) { |dt| dt.iso8601 } expose :created_at, format_with: :iso_timestamp expose :updated_at, format_with: :iso_timestamp end ``` -------------------------------- ### RepresentExposure Usage Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Example of using RepresentExposure to nest other entities, like exposing a post's author using UserEntity and its comments using CommentEntity. ```ruby class PostEntity < Grape::Entity expose :title expose :author, using: UserEntity # Single object expose :comments, using: CommentEntity # Auto-detects arrays end # Equivalent to: # represent post.author, with: UserEntity # represent post.comments, with: CommentEntity ``` -------------------------------- ### Documenting Entities with Grape Entity Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/patterns.md Use the `documentation` option within `expose` to define metadata for API documentation generation. This includes type, description, and example values for each field. ```ruby class UserEntity < Grape::Entity expose :id, documentation: { type: 'Integer', desc: 'User ID', required: true, example: 42 } expose :name, documentation: { type: 'String', desc: 'User full name', required: true } expose :email, documentation: { type: 'String', desc: 'Email address', required: true, example: 'john@example.com' } end # Use in API desc 'Get user', { params: UserEntity.documentation } get '/users/:id' do present User.find(params[:id]), with: UserEntity end ``` -------------------------------- ### Hash Condition Use Cases Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Examples demonstrating the use of HashCondition for conditional exposures based on matching runtime options. ```ruby expose :admin_panel, if: { admin: true } expose :public_data, unless: { internal_only: true } expose :detailed, if: { detail_level: :full } ``` -------------------------------- ### Serialization Step 3: Hash Generation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Outlines the process of generating the serializable hash, starting with merging runtime options and then calling the `serializable_value` on the root exposure. ```ruby serializable_hash(runtime_options = {}) ↓ opts = options.merge(runtime_options) ↓ root_exposure.serializable_value(self, opts) ``` -------------------------------- ### Complex Hash Condition Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Demonstrates a complex scenario with multiple HashConditions applied to different exposures within a Grape Entity, showing how combined conditions affect serialization. ```ruby class UserEntity < Grape::Entity expose :id expose :email expose :admin_notes, if: { admin: true } expose :email_verified, if: { show_verification: true } expose :phone, if: { admin: true, show_contact: true } # Both must be true end # Usage UserEntity.represent(user, admin: true) # Shows admin_notes, not phone UserEntity.represent(user, admin: true, show_contact: true) # Shows admin_notes and phone ``` -------------------------------- ### Exposure Inheritance Example Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Demonstrates how Grape Entity exposures and formatters are inherited by child classes. The 'unexpose' method can be used to remove inherited exposures. ```ruby class BaseEntity < Grape::Entity expose :id, :created_at format_with(:timestamp) { |dt| dt.iso8601 } end class UserEntity < BaseEntity expose :name, :email end class AdminEntity < UserEntity expose :last_login_ip unexpose :email end ``` -------------------------------- ### Attribute Path Tracking Example Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Illustrates how Grape Entity tracks attribute paths, accessible via `options[:attr_path]`, for nested exposures. This is useful for dynamic field inclusion/exclusion. ```ruby class Status < Grape::Entity expose :user # path is [:user] expose :foo, as: :bar # path is [:bar] expose :a do expose :b, as: :xx do expose :c # path is [:a, :xx, :c] end end end ``` -------------------------------- ### Serialization Step 1: Instantiation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Details the object instantiation process when calling the represent method, including the creation of the Options object. ```ruby UserEntity.represent(user, admin: true) ↓ new(user, Options.new(admin: true)) ↓ @object = user @options = Options instance @delegator = Delegator.new(user) ``` -------------------------------- ### Clone and Set Up Project Source: https://github.com/ruby-grape/grape-entity/blob/master/CONTRIBUTING.md Clone the Grape-Entity project from GitHub and set up the upstream remote for future updates. ```bash git clone https://github.com/contributor/grape-entity.git cd grape-entity git remote add upstream https://github.com/ruby-grape/grape-entity.git ``` -------------------------------- ### Entry Point for Serialization Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Shows the initial call to represent a user entity, passing runtime options. ```ruby UserEntity.represent(user, admin: true) ``` -------------------------------- ### Initialize Options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Creates a new Options instance from a hash of runtime options. Can be initialized with specific options or as an empty instance. It also supports copying options from an existing Options object. ```ruby options = Options.new(admin: true, version: 'v2') options = Options.new new_options = Options.new(options.opts_hash) ``` -------------------------------- ### Prepare for Next Release in Changelog Source: https://github.com/ruby-grape/grape-entity/blob/master/RELEASING.md Adds a placeholder for the next release in the changelog, including a section for contributions. ```text Next Release ============ * Your contribution here. ``` -------------------------------- ### Serialization Step 5: Root Wrapping Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Describes the final step where the serialized output might be wrapped under a root key, depending on the configuration. ```ruby root_element_result = inner ↓ If root configured: { root_element => inner } If root: false passed: inner Else: inner ``` -------------------------------- ### Optional and Default Fields Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Shows how to handle optional fields, fields that might be nil, and fields with default values. ```ruby expose :optional_field, safe: true expose :sometimes_nil, expose_nil: false expose :fallback_field, default: 'N/A' ``` -------------------------------- ### Options Core Methods Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Illustrates the primary methods available on the Options class for managing runtime configurations, including merging, field filtering, and nested options. ```ruby Options.new(opts_hash) ↓ ├── merge(new_opts) → Returns new Options with merged hash ├── should_return_key?(key) → Field included in output? ├── only_fields(for_key) → Get :only filters ├── except_fields(for_key) → Get :except filters ├── for_nesting(key) → Create nested entity options └── with_attr_path(part) { yield } → Track attr path ``` -------------------------------- ### Presenting Entity with Additional Options Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Demonstrates how to present an object `s` using the `Status` entity, passing an additional `user` option. ```ruby present s, with: Status, user: current_user ``` -------------------------------- ### Get Except Fields Specification Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Retrieves the parsed `:except` filter specification. Can be used for nested entities by providing a key. ```ruby def except_fields(for_key = nil) end ``` ```ruby options = Options.new( except: [ :password, :password_hash, { user: [:password] } ] ) options.except_fields() # => { password: true, password_hash: true, ... } options.except_fields(:user) # => [:password] ``` -------------------------------- ### Delegating Symbol Keys from Hash Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Demonstrates delegating a symbol key from a hash using Delegator::HashObject. ```ruby hash = { name: 'John', email: 'john@example.com' } delegator = Delegator::HashObject.new(hash) delegator.delegate(:name, hash_access: :to_sym) # => 'John' ``` -------------------------------- ### Compiling Conditions from Options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Explains the internal process of compiling conditions from exposure options. It combines direct conditions with extra conditions provided via `with_options`. ```ruby def self.new(attribute, options) conditions = compile_conditions(attribute, options) # ... creates appropriate exposure with conditions end def self.compile_conditions(attribute, options) if_conditions = [ options[:if_extras], # Extra conditions from with_options options[:if] # Direct condition ].compact.flatten.map { |cond| Condition.new_if(cond) } unless_conditions = [ options[:unless_extras], options[:unless] ].compact.flatten.map { |cond| Condition.new_unless(cond) } # Special handling for expose_nil: false unless_conditions << expose_nil_condition(...) if options[:expose_nil] == false if_conditions + unless_conditions end ``` -------------------------------- ### Complex Condition Combination Example in Entity Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Demonstrates combining multiple conditions, including hash, symbol, and lambda, within a Grape Entity class. ```ruby class PostEntity < Grape::Entity # Always exposed expose :id, :title # Exposed to admins expose :author_notes, if: { admin: true } # Exposed unless collection expose :full_text, unless: { collection: true } # Complex logic expose :comments, if: lambda { |post, opts| !opts[:collection] && opts[:include_comments] } # Symbol key expose :draft_preview, if: :show_drafts # Combined: with_options + direct condition with_options(if: { detail: :high }) do expose :metadata expose :stats, if: :with_stats # Both conditions must be true end end ``` -------------------------------- ### Global Configuration for Formatters Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Sets up a global formatter that can be reused across multiple entities. ```ruby Grape::Entity.format_with(:iso_ts) { |dt| dt.iso8601 } ``` -------------------------------- ### Options Constructor Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Initializes a new Options instance with a hash of runtime options. This controls conditional exposure visibility and field filtering during entity serialization. ```APIDOC ## Constructor ### `def initialize(opts_hash = {})` Creates a new Options instance from a hash of runtime options. #### Parameters - **opts_hash** (Hash) - Optional - The options hash containing runtime configuration. Defaults to an empty hash. #### Example ```ruby # Basic initialization options = Options.new(admin: true, version: 'v2') # Empty options options = Options.new # From existing options (copying) new_options = Options.new(options.opts_hash) ``` ``` -------------------------------- ### Symbol Condition Use Cases Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Examples showcasing the use of SymbolCondition to conditionally expose attributes based on the presence of a symbol key in the runtime options. ```ruby expose :draft_status, if: :include_draft expose :comments, if: :with_comments expose :internal_id, unless: :public_view ``` -------------------------------- ### UserEntity with Nested Exposures Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Example demonstrating how to define nested 'address' and 'contact_info' hashes within a Grape Entity. This organizes related fields for better structure. ```ruby class UserEntity < Grape::Entity expose :id, :name # Creates a nested "address" hash expose :address do expose :street expose :city expose :zip end # Creates a nested "contact_info" hash expose :contact_info do expose :email expose :phone expose :website end end # Output: # { # id: 1, # name: "John", # address: { street: "123 Main", city: "Portland", zip: "97214" }, # contact_info: { email: "john@example.com", phone: "555-0123", website: "..." } # } ``` -------------------------------- ### Serialization Methods Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Demonstrates various methods for serializing entity objects into different formats like Hash, JSON, and XML. ```ruby entity = UserEntity.new(user, options) entity.serializable_hash # Hash representation entity.as_json # Alias for serializable_hash entity.to_json # JSON string entity.to_xml # XML string entity.inspect # Human-readable (excludes data) # Represent returns Entity instance by default UserEntity.represent(user, serializable: false) # Returns Entity UserEntity.represent(user, serializable: true) # Returns Hash ``` -------------------------------- ### Accessing Entity Documentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md The `documentation` method retrieves all documentation declared for exposures within an entity. This is useful for generating API documentation. ```ruby class UserEntity < Grape::Entity expose :name, documentation: { type: "String", desc: "User name" } expose :email, documentation: { type: "String", desc: "Email address" } end UserEntity.documentation # => { name: { type: "String", desc: "User name" }, email: { type: "String", desc: "Email address" } } ``` -------------------------------- ### Option Merging Logic Explanation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Outlines the logic for merging options, detailing how conditions like `:if` and `:unless` are handled, including hash merging and storing multiple conditions. ```text merge_options(new_opts) ↓ For :if and :unless: If both hash → merge hashes If mixed → store one as primary, other in _extras Multiple conditions → all must pass ↓ For other keys: new value overrides existing ``` -------------------------------- ### Get Only Fields Specification Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Returns the parsed `:only` filter specification, normalizing various input formats into a standard hash. Returns nil if `:only` was not specified. Can also retrieve fields for a nested entity. ```ruby # Format for nested entities options = Options.new( only: [ :id, :name, { user: [:id, :name, :email] }, # Only these fields on nested 'user' entity { comments: [:id, :text] } ] ) # Top-level only fields options.only_fields() # => { id: true, name: true, user: [...], comments: [...] } # Nested entity options.only_fields(:user) # => [:id, :name, :email] ``` -------------------------------- ### Delegating String Keys from Hash Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Demonstrates delegating a string key from a hash using Delegator::HashObject with :to_s strategy. ```ruby hash = { 'name' => 'Jane', 'email' => 'jane@example.com' } delegator = Delegator::HashObject.new(hash) delegator.delegate(:name, hash_access: :to_s) # => 'Jane' ``` -------------------------------- ### Handle Duplicate Exposure Names with Conditions (First Case) Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Entities with duplicate exposure names and conditions can silently overwrite each other. This example shows a scenario where only field_a is exposed when object.check equals 'foo', but both field_b and foo are exposed when object.check equals 'bar'. ```ruby module API module Entities class Status < Grape::Entity expose :field_a, :foo, if: lambda { |object, options| object.check == "foo" } expose :field_b, :foo, if: lambda { |object, options| object.check == "bar" } end end end ``` -------------------------------- ### Performance Tip: Preload Associations Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Improves performance by preloading associated records using `includes` before presenting a collection of objects. ```ruby users = User.includes(:profile, :comments).all present users, with: UserEntity ``` -------------------------------- ### Usage of Collection Presentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Shows how to use the configured collection presentation entity to output a collection with associated metadata like total count and page number. ```ruby users = User.all present users, with: Users, page: 1 # Output: { "users" => [...], "total_count" => 100, "page" => 1 } ``` -------------------------------- ### Create Entity Instance (Instance Method) Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Creates an entity instance for the current object. Runtime options can be passed to control behavior. ```ruby def entity(options = {}) end ``` ```ruby product = Product.find(1) entity = product.entity(show_pricing: true) entity.serializable_hash ``` -------------------------------- ### Merging Options with Block Syntax Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Demonstrates how to merge options, specifically the `if` condition, using a block. Options defined within the block are applied to all exposures inside it. ```ruby with_options(if: { admin: true }) do expose :field1 # Gets if: { admin: true } expose :field2 # Gets if: { admin: true } end expose :field3, if: :detailed # In block: gets if: { admin: true } AND if: :detailed # stored as if_extras: [:detailed], if: { admin: true } ``` -------------------------------- ### Pass Custom Options to Entities Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Demonstrates how to pass arbitrary custom options during entity representation and access them within exposures. ```ruby # In API endpoint present user, with: UserEntity, admin: current_user.admin?, level: 'detailed' # Available in exposures expose :internal_field, if: lambda { |user, opts| opts[:admin] } expose :details do |user, opts| case opts[:level] when 'summary' then user.summary when 'detailed' then user.full_details else user.basic_info end end ``` -------------------------------- ### Handle Multi-Attribute Exposure Restrictions with ArgumentError Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/errors.md Shows how to correctly use options like `:as` with single attribute exposures to prevent ArgumentError. Multi-attribute exposures have specific restrictions. ```ruby class UserEntity < Grape::Entity expose :id, :name, as: :identifier # => ArgumentError: You may not use the :as option on multi-attribute exposures. end ``` ```ruby expose :id, as: :identifier expose :name ``` -------------------------------- ### Entity Initialization Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Creates a new entity instance wrapping a given object. Runtime options can be provided to control conditional exposures. ```APIDOC ## initialize ### Description Creates a new entity instance wrapping the given object. ### Method `initialize(object, options = {})` ### Parameters #### Object - **object** (Object) - Required - The object being represented #### Options - **options** (Hash, Options) - Optional - Runtime options controlling conditional exposures ### Example ```ruby user = User.find(1) entity = UserEntity.new(user, admin: true, version: 'v2') ``` ``` -------------------------------- ### format_with Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Registers a reusable formatter that can be applied to multiple exposures. It takes a name and a block that defines how a value should be formatted. ```APIDOC ## format_with ### Description Registers a reusable formatter that can be applied to multiple exposures. It takes a name and a block that defines how a value should be formatted. ### Method `self.format_with(name, &block)` ### Parameters #### Path Parameters - **name** (Symbol) - Required - Name of the formatter - **block** (Proc) - Required - Block receiving a single value argument, returns formatted value ### Example: ```ruby # Define at class level class UserEntity < Grape::Entity format_with(:iso_timestamp) { |dt| dt.iso8601 } expose :created_at, format_with: :iso_timestamp end # Define at global level (available to all entities) Grape::Entity.format_with(:timestamp) { |dt| dt.strftime('%m/%d/%Y') } ``` ``` -------------------------------- ### Compare Options Instances Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Compares two Options instances for equality based on their configurations. Can also compare against a Hash. ```ruby def ==(other) end ``` ```ruby opts1 = Options.new(admin: true, version: 'v2') opts2 = Options.new(admin: true, version: 'v2') opts3 = Options.new(admin: false) opts1 == opts2 # => true opts1 == opts3 # => false opts1 == { admin: true, version: 'v2' } # => true ``` -------------------------------- ### Impact of Root Element Configuration on Output Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Illustrates the JSON output structure with and without singular root elements defined, showing how the 'root' configuration affects wrapping. ```json # With root # UserEntity.represent([user1, user2]) # => { "users" => [{ id: 1, ... }, { id: 2, ... }] } # UserEntity.represent(user1) # => { "user" => { id: 1, ... } } # Without singular root (PostEntity) # PostEntity.represent(post1) # => { id: 1, title: "..." } # No wrapping ``` -------------------------------- ### Apply Options to Multiple Exposures with with_options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Use `with_options` to apply common options like `if` or `format_with` to multiple subsequent exposures. ```ruby class UserEntity < Grape::Entity # Both exposures get :if: { admin: true } with_options(if: { admin: true }) do expose :password_hash expose :email_verified_at end # Both expose with formatter with_options(format_with: :iso_timestamp) do expose :created_at expose :updated_at end end ``` -------------------------------- ### Serialization Step 2: Serialization Logic Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Explains the decision point in serialization: whether to proceed with `serializable_hash` or return the entity instance directly based on the `:serializable` option. ```ruby entity.presented (checks :serializable option) ↓ If :serializable → serializable_hash If !:serializable → entity instance ``` -------------------------------- ### Creating Entity Instance (Instance Method) Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md The `entity` instance method, available on objects that include `Grape::Entity::DSL`, creates an entity instance for that object, optionally accepting runtime options. ```APIDOC ## entity (instance method) ### Description Creates an entity instance for this object. ### Method `entity(options = {})` ### Parameters #### Options - **options** (Hash) - Optional - Runtime options ### Return Type Entity instance ### Example ```ruby product = Product.find(1) entity = product.entity(show_pricing: true) entity.serializable_hash ``` ``` -------------------------------- ### Exposing Documentation with Fields Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Include documentation metadata directly with field exposures using the 'documentation' option. This metadata can be used by API documentation generation tools. ```ruby expose :text, documentation: { type: "String", desc: "Status update text." } ``` -------------------------------- ### Hash Access Compatibility Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/types.md Demonstrates hash key access differences between symbol and string keys in different Ruby versions. ```ruby # Symbol key hash (default, use :to_sym) { name: 'John' }[: name] # => 'John' # String key hash (use :to_s) { 'name' => 'John' }['name'] # => 'John' ``` -------------------------------- ### Performance Tip: Avoid N+1 Queries in Block Exposures Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Prevents N+1 query issues in block exposures by preloading data using `includes` instead of executing queries per object. ```ruby # Bad: expose :comment_count do |post| post.comments.count # Query per post end # Good: expose :comment_count # Preload with includes ``` -------------------------------- ### Base Exposure - Determining Output Key Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Returns the key name for the output. It uses the `:as` alias if provided, or the original attribute name. If `:as` is a proc, it evaluates the proc to determine the key. ```ruby def key(entity = nil) ``` -------------------------------- ### Define an Entity with Conditional Exposure (Proc) Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/README.md Shows how to use a lambda (Proc) for complex conditional logic based on the object and options. ```ruby if: lambda { |obj, opts| obj.active? } ``` -------------------------------- ### Initialize Entity Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Creates a new entity instance wrapping a given object. Runtime options can control conditional exposures. ```ruby def initialize(object, options = {}) end ``` ```ruby user = User.find(1) entity = UserEntity.new(user, admin: true, version: 'v2') ``` -------------------------------- ### Accessing Entity Documentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Retrieve all documentation metadata defined for an entity class. ```ruby UserEntity.documentation # => { # name: { type: "String", desc: "User's full name", required: true }, # age: { type: "Integer", desc: "Age in years", default: 0 } # } ``` -------------------------------- ### Base Exposure Constructor Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/exposure.md Initializes a base exposure instance with the attribute name, configuration options, and compiled conditions. This serves as the foundation for all specific exposure types. ```ruby def initialize(attribute, options, conditions) ``` -------------------------------- ### Stage and Commit Changes Source: https://github.com/ruby-grape/grape-entity/blob/master/CONTRIBUTING.md Stage your modified files and commit them to your local repository. ```bash git add ... git commit ``` -------------------------------- ### Handle Duplicate Exposure Names Safely with respond_to? Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Using `respond_to?` is a safer approach when dealing with mixed collections and potential duplicate exposure names to avoid unexpected overwrites. ```ruby module API module Entities class Status < Grape::Entity expose :field_a, if: lambda { |object, options| object.check == "foo" } expose :field_b, if: lambda { |object, options| object.check == "bar" } expose :foo, if: lambda { |object, options| object.respond_to?(:foo) } end end end ``` -------------------------------- ### Using Grape Entity DSL within a Class Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Demonstrates defining entities directly within a class using `Grape::Entity::DSL`. This allows for concise definition of exposures, including conditional ones. ```ruby class Status include Grape::Entity::DSL entity :text, :user_id do expose :detailed, if: :conditional end end ``` -------------------------------- ### Condition Factory Usage Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Demonstrates how to create conditions using the factory methods. The type of condition created depends on the type of the provided specification. ```ruby Condition.new_if(spec) ↓ ├─ Is Hash? → HashCondition ├─ Is Symbol? → SymbolCondition ├─ Is Proc? → BlockCondition └─ Default → None (spec becomes value) ``` -------------------------------- ### documentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Returns all documentation declared for exposures in this entity. This is useful for introspection and generating API documentation. ```APIDOC ## documentation ### Description Returns all documentation declared for exposures in this entity. This is useful for introspection and generating API documentation. ### Method `self.documentation` ### Return Type: Hash (key => { type, desc, ... }) ### Example: ```ruby class UserEntity < Grape::Entity expose :name, documentation: { type: "String", desc: "User name" } expose :email, documentation: { type: "String", desc: "Email address" } end UserEntity.documentation # => { name: { type: "String", desc: "User name" }, email: { type: "String", desc: "Email address" } } ``` ``` -------------------------------- ### Performance Tip: Use :safe for Optional Fields Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Enhances performance by using `safe: true` for optional fields, avoiding the need for explicit nil checks within the entity. ```ruby expose :optional_field, safe: true ``` -------------------------------- ### Safe Exposure Handling Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/delegator.md Demonstrates using `safe: true` to prevent errors when an attribute is missing. ```ruby class UserEntity < Grape::Entity expose :optional_field, safe: true end ``` -------------------------------- ### Update Changelog for New Version Source: https://github.com/ruby-grape/grape-entity/blob/master/RELEASING.md Formats the changelog entry for a new release, including the version number and date. ```text 0.4.0 (2014-01-27) ================== ``` -------------------------------- ### Combining Multiple Conditions with `with_options` Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/conditions.md Demonstrates how to apply a common condition to multiple exposures within a block using `with_options`. All exposures within the block inherit the specified condition. ```ruby class UserEntity < Grape::Entity with_options(if: { admin: true }) do expose :password_hash expose :internal_notes end end ``` -------------------------------- ### Adding Documentation Metadata to Exposures Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/types.md Attach metadata for API documentation generation to exposures, including type, description, and required status. ```ruby expose :name, documentation: { type: "String", desc: "User's name", required: true } expose :age, documentation: { type: "Integer", desc: "Age in years" } ``` -------------------------------- ### Role-Based Access Control Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Demonstrates how to expose fields based on user roles using a lambda condition with runtime options. ```ruby expose :admin_field, if: lambda { |obj, opts| opts[:current_user]&.admin? } # Usage present user, with: UserEntity, current_user: current_user ``` -------------------------------- ### Per-Field Documentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Add detailed documentation metadata to an exposed field, including type, description, and requirements. ```ruby expose :name, documentation: { type: "String", desc: "User's full name", required: true } ``` -------------------------------- ### Custom Delegator Implementation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Demonstrates how to create a custom delegator by subclassing `Grape::Entity::Delegator::Base` and overriding the `delegate` method for custom attribute access logic. ```ruby class CustomObjectDelegator < Grape::Entity::Delegator::Base def delegate(attribute, **options) # Custom access logic end end ``` -------------------------------- ### Field Filtering with :only (Basic) Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Shows how to use the `:only` option to specify which fields should be exposed when representing an entity. ```ruby class UserEntity < Grape::Entity expose :id, :name, :email, :password_hash, :phone end # Only expose id and name result = UserEntity.represent(user, only: [:id, :name]) # => { id: 1, name: 'John' } ``` -------------------------------- ### Template Method Pattern Implementation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Illustrates the Template Method pattern where a base class defines a skeleton algorithm, and subclasses override specific steps. Used here for defining how values are exposed. ```ruby class Base def value(entity, options) raise NotImplementedError end end class DelegatorExposure < Base def value(entity, options) entity.delegate_attribute(attribute) end end ``` -------------------------------- ### Builder Pattern for Output Accumulation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Demonstrates the Builder pattern used by OutputBuilder to construct the final output hash by adding values from exposures, handling merges, and resolving collisions. ```ruby OutputBuilder.add(exposure, value) ├── Normal case → output[key] = value ├── Merge case → merge fields into output └── Collision case → apply merge resolver ``` -------------------------------- ### Integration with Grape APIs Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Demonstrates how to use Grape Entity within Grape API endpoints using the `present` method to serialize objects. ```APIDOC ## Integration with Grape APIs ### Description When used with the Grape framework, use `present` in your endpoints. ### Example ```ruby module API class Products < Grape::API get '/products/:id' do product = Product.find(params[:id]) present product, with: ProductEntity, type: current_user.admin? ? :full : :default end get '/products' do products = Product.all present products, with: ProductEntity end end end ``` ``` -------------------------------- ### Per-Field Documentation Metadata Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Define documentation details for fields, including type, description, and default values. ```ruby expose :name, documentation: { type: "String", desc: "User's full name", required: true } expose :age, documentation: { type: "Integer", desc: "Age in years", default: 0 } ``` -------------------------------- ### Handle Block and format_with Conflict with ArgumentError Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/errors.md Demonstrates the conflict between providing a block and using `format_with` simultaneously, which raises an ArgumentError. Choose one approach for formatting. ```ruby expose :field, format_with: ->(v) { v.upcase } { |obj| obj.field } # => ArgumentError: You may not use block-setting when also using format_with ``` ```ruby # Option 1: Use format_with expose :field, format_with: ->(v) { v.upcase } # Option 2: Use proc in block expose :field do |obj| obj.field.upcase end ``` -------------------------------- ### == Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Compares two Options instances for equality. It checks if two Options objects represent the same configuration, allowing comparison with another Options instance or a Hash. ```APIDOC ## == (equality) ### Description Compares two Options instances to determine if they are equivalent. This method allows for checking if two sets of options are identical. ### Method `==(other)` ### Parameters #### Path Parameters - **other** (Options, Hash) - Required - Options to compare against ### Return Type Boolean ### Example: ```ruby opts1 = Options.new(admin: true, version: 'v2') opts2 = Options.new(admin: true, version: 'v2') opts3 = Options.new(admin: false) opts1 == opts2 # => true opts1 == opts3 # => false opts1 == { admin: true, version: 'v2' } # => true ``` ``` -------------------------------- ### Present Entities in Grape API Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/entity.md Demonstrates how to use `present` within Grape API endpoints to serialize and return entity data. ```ruby module API class Products < Grape::API get '/products/:id' do product = Product.find(params[:id]) present product, with: ProductEntity, type: current_user.admin? ? :full : :default end get '/products' do products = Product.all present products, with: ProductEntity end end end ``` -------------------------------- ### Represent an Object with Options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/README.md Represents a user object, applying specific options like 'admin' flag and filtering fields to 'id' and 'name'. ```ruby UserEntity.represent(user, admin: true, only: [:id, :name]) ``` -------------------------------- ### Performance Tip: Limit Fields with :only Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Optimizes performance for large collections by specifying a subset of fields to expose using the `:only` option. ```ruby present users, with: UserEntity, only: [:id, :name] ``` -------------------------------- ### Representing Objects with Options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Shows how to represent objects using a Grape Entity, specifying options like 'only' for whitelisting fields and custom parameters. ```ruby UserEntity.represent(objects, options) ``` ```ruby UserEntity.represent( user, only: [:id, :name], admin: true, version: 'v2' ) ``` -------------------------------- ### Combining Block and Direct Options Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Apply options at both the block level and the individual exposure level, with block options being inherited. ```ruby with_options(if: { detailed: true }) do expose :email, if: :include_contact # Both conditions must be true expose :phone, documentation: { type: 'String' } # Inherits :if end ``` -------------------------------- ### Conditional Exposure in Conditions Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Demonstrates various ways to specify conditions for exposing fields using hashes and lambdas. ```ruby expose :email, if: { include_contact: true } expose :phone, if: :with_phone expose :ssn, if: lambda { |person, opts| opts[:admin] } ``` -------------------------------- ### Grape Entity Module Structure Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Provides an overview of the Grape Entity module's organization, showing the main class, its sub-modules, and dependencies. ```text Grape ├── Entity (main class) │ ├── Exposure (module with factory) │ │ ├── Base (abstract) │ │ ├── DelegatorExposure │ │ ├── BlockExposure │ │ ├── RepresentExposure │ │ ├── FormatterExposure │ │ ├── FormatterBlockExposure │ │ └── NestingExposure │ │ ├── NestedExposures (collection) │ │ └── OutputBuilder │ ├── Delegator (module with factory) │ │ ├── Base (abstract) │ │ ├── PlainObject │ │ ├── HashObject │ │ └── OpenStructObject │ ├── Condition (module with factory) │ │ ├── Base (abstract) │ │ ├── HashCondition │ │ ├── SymbolCondition │ │ └── BlockCondition │ ├── Options (options management) │ └── DSL (mixin for model integration) ├── GrapeEntity (version module) └── Dependencies └── ActiveSupport (core_ext) ``` -------------------------------- ### Visitor Pattern in Exposure Processing Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/architecture.md Shows how the Visitor pattern is applied during serialization, where the root exposure processes nested exposures and an OutputBuilder accumulates the results. ```ruby root_exposure ├── nested_exposures[] │ ├── exposure1 → value │ ├── exposure2 → value │ └── exposure3 → value └── OutputBuilder accumulates results ``` -------------------------------- ### Passing Options to Nested Exposures Source: https://github.com/ruby-grape/grape-entity/blob/master/README.md Shows how to pass additional options, like `full_format`, to nested exposures to control their rendering. The `API::Address.represent` call merges new options. ```ruby # api/contact.rb expose :contact_info do expose :phone expose :address do |instance, options| # use `#merge` to extend options and then pass the new version of options to the nested entity API::Entities::Address.represent instance.address, options.merge(full_format: instance.need_full_format?) end expose :email, if: lambda { |instance, options| options[:type] == :full } end ``` -------------------------------- ### Configure Collection Presentation Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/configuration.md Enables presenting a collection as a single entity instance, where the collection itself is an exposed attribute. This is useful for including metadata alongside the collection. ```ruby class Users < Grape::Entity present_collection true, :collection_name expose :collection_name, using: UserEntity, as: 'users' expose :total_count expose :page def total_count object[:collection_name].length end def page options[:page] || 1 end end ``` -------------------------------- ### Delegate Hash Methods to opts_hash Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/api-reference/options.md Extends Options with common Hash methods like `dig`, `key?`, `fetch`, `[]`, and `empty?` for direct access and manipulation. ```ruby extend Forwardable def_delegators :opts_hash, :dig, :key?, :fetch, :[], :empty? ``` ```ruby options = Options.new(admin: true, nested: { role: 'admin' }) # Direct access options[:admin] # => true # Key checking options.key?(:admin) # => true # Nested access options.dig(:nested, :role) # => 'admin' # Fetch with default options.fetch(:unknown, 'default') # => 'default' # Empty check options.empty? # => false ``` -------------------------------- ### Configure Git User Information Source: https://github.com/ruby-grape/grape-entity/blob/master/CONTRIBUTING.md Set your global Git user name and email address for commit tracking. ```bash git config --global user.name "Your Name" git config --global user.email "contributor@example.com" ``` -------------------------------- ### Entity Documentation Integration Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/quick-reference.md Defines documentation metadata for an entity's fields and shows how to integrate this documentation with Grape API endpoint descriptions. ```ruby class UserEntity < Grape::Entity expose :name, documentation: { type: 'String', desc: 'User name', required: true } end # In Grape API desc 'Get user', { params: UserEntity.documentation } get '/users/:id' do # ... end ``` -------------------------------- ### Formatter Registration: By Proc Source: https://github.com/ruby-grape/grape-entity/blob/master/_autodocs/types.md Apply an inline formatter using a Proc directly within the 'format_with' option. The Proc is executed on the exposed value during serialization. ```ruby expose :price, format_with: ->(value) { "$%.2f" % value } ```