README.md in global-registry-bindings-0.1.3 vs README.md in global-registry-bindings-0.1.4
- old
+ new
@@ -1,10 +1,9 @@
# Global Registry Bindings
Global Registry Bindings are a set of bindings to push ActiveRecord models to the Global Registry.
-
## Installation
Add to your Gemfile:
```ruby
gem 'global-registry-bindings'
@@ -25,11 +24,12 @@
## Usage
To make use of `global-registry-bindings` your model will need a few additional columns.
To push models to Global Registry, you will need a `global_registry_id` column. You additionally need a
-`global_registry_mdm_id` to pull a Global Registry MDM (master data model) id. These columns should be of type
+`global_registry_mdm_id` to pull a Global Registry MDM (master data model) id. Additionally, relationships will also
+require columns to track relationship ids. These columns should be of type
`:string` or `:uuid` and allow null values. Column names are customizable through options.
```ruby
class CreatePeople < ActiveRecord::Migration
def change
add_column :people, :global_registry_id, :string, null: true, default: nil
@@ -45,85 +45,81 @@
end
```
## Options
-You can pass various options to the `global_registry_bindings` method. Configuration options are:
+You can pass various options to the `global_registry_bindings` method. Options will list whether they are valid for
+`:entity`, `:relationship` or both bindings.
* `:binding`: Type of Global Registry binding. Either `:entity` or `:relationship`.
(default: `:entity`)
-
-* `:id_column`: Column used to track the Global Registry ID for the model instance or relationship entity.
-Can be a :string or :uuid column. (default: `:global_registry_id`) **[`:entity`, `:relationship`]**
-* `:type`: Global Registry entity type. Accepts a Symbol or a Proc. Symbol is the name of the entity type, Proc
-is passed the model instance and must return a symbol which is the entity type. Default value is underscored
-name of the model. Ex: ```type: proc { |model| model.name.to_sym }```. When used in a `:relationship`, `:type`
-is a unique name to identify the relationship. **[`:entity`, `:relationship`]**
+* `:id_column`: Column used to track the Global Registry ID for the entity or relationship entity.
+Can be a `:string` or `:uuid` column. (default: `:global_registry_id`) **[`:entity`, `:relationship`]**
+* `:type`: Global Registry Entity Type name. This name should be unique in Global Registry or point to an existing
+Entity Type. When used in a `:relationship` binding, it is required to be unique across all relationships on this
+ActiveRecord class. Accepts a Symbol or a Proc. Symbol is the name of the Entity Type, Proc
+is passed the model instance and must return a symbol which is the Entity Type. Defaults to the underscored
+name of the class. Ex: ```type: proc { |model| model.name.to_sym }```. **[`:entity`, `:relationship`]**
+
* `:push_on`: Array of Active Record lifecycle events used to push changes to Global Registry.
(default: `[:create, :update, :destroy]`) **[`:entity`]**
-* `:parent_association`: Name of the Active Record parent association. Must be defined before calling
-global_registry_bindings in order to determine foreign_key for use in exclude. Used to create a
+* `:parent`: Name of the Active Record parent association (`:belongs_to`, `:has_one` ...). Must be defined
+before calling `global_registry_bindings` in order to determine foreign_key for use in exclude. Used to create a
hierarchy or to push child entity types. (Ex: person -> address) (default: `nil`) **[`:entity`]**
-* `:parent_association_class`: Class name of the parent model. Required if `:parent_association` can not be used
+* `:parent_class`: Active Record Class name of the parent. Required if `:parent` can not be used
to determine the parent class. This can happen if parent is defined by another gem, like `ancestry`.
(default: `nil`) **[`:entity`]**
* `:primary_binding`: Determines what type of global-registry-binding the primary association points to. Defaults
-to `:entity`, but can be set to a `:relationship` type name (ex: `:assignment`) to create a relationship_type
+to `:entity`, but can be set to a `:relationship` type (ex: `:assignment`) to create a relationship_type
between a relationship and an entity. (default: `:entity`) **[`:relationship`]**
-* `:primary_association`: Name of the Active Record primary association. Must be defined before calling
-global_registry_bindings in order to determine foreign_key for use in exclude. (default: `nil`)
-**[`:relationship`]**
+* `:primary`: Name of the Active Record primary association. Must be defined before calling
+global_registry_bindings in order to determine foreign_key for use in exclude. If missing, `:primary` is assumed to be
+the current Active Record model. (default: `nil`) **[`:relationship`]**
-* `:primary_association_class`: Class name of the primary model. Required if `:primary_association` can not be
-used to determine the parent class. This can happen if parent is defined by another gem, like `ancestry`.
+* `:primary_class`: Class name of the primary model. Required if `:primary` can not be
+used to determine the primary class. This can happen if parent is defined by another gem, like `ancestry`.
(default: `self.class`) **[`:relationship`]**
-* `:primary_association_foreign_key`: Foreign Key column for the primary association. Used if foreign_key can
-not be determined from `:primary_association`. (default: `:primary_association.foreign_key`)
-**[`:relationship`]**
+* `:primary_foreign_key`: Foreign Key column for the primary association. Used if foreign_key can
+not be determined from `:primary`. (default: `:primary.foreign_key`) **[`:relationship`]**
-* `:related_association`: Name of the Active Record related association. Active Record association must be
+* `:primary_name`: **Required** Name of primary relationship (Global Registry relationship1). Should be unique
+to prevent ambiguous relationship names. (default: `nil`) **[`:relationship`]**
+
+* `:related`: Name of the Active Record related association. Active Record association must be
defined before calling global_registry_bindings in order to determine the foreign key.
(default: `nil`) **[`:relationship`]**
-* `:related_association_class`: Class name of the related model. Required if `:related_association` can not be
+* `:related_class`: Class name of the related model. Required if `:related_association` can not be
used to determine the related class. (default: `nil`) **[`:relationship`]**
-* `:related_association_foreign_key`: Foreign Key column for the related association. Used if foreign_key can
-not be determined from `:primary_association`. (default: `:primary_association.foreign_key`)
-**[`:relationship`]**
+* `:related_foreign_key`: Foreign Key column for the related association. Used if foreign_key can
+not be determined from `:related`. (default: `:related.foreign_key`) **[`:relationship`]**
-* `:primary_relationship_name`: **Required** Name of primary relationship. Should be unique to prevent
-ambiguous relationship names. (default: `nil`) **[`:relationship`]**
+* `:related_name`: **Required** Name of the related relationship (Global Registry relationship2). Should be
+unique to prevent ambiguous relationship names (default: `nil`) **[`:relationship`]**
-* `:related_relationship_name`: **Required** Name of the related relationship. Should be unique to prevent
-ambiguous relationship names (default: `nil`) **[`:relationship`]**
-
-* `:related_association_type`: Name of the related association entity_type. Required if unable to determined
+* `:related_type`: Name of the related association Entity Type. Required if unable to determined
`:type` from related. (default: `nil`) **[`:relationship`]**
* `:related_global_registry_id`: Global Registry ID of a remote related entity. Proc or Symbol. Implementation
should cache this as it may be requested multiple times. (default: `nil`) **[`:relationship`]**
-* `:ensure_relationship_type`: Ensure Global Registry RelationshipType exists and is up to date.
-(default: `true`) **[`:relationship`]**
+* `:ensure_type`: Ensure Global Registry Entity Type or Relationship Entity Type exists and is up to date.
+(default: `true`) **[`:entity`, `:relationship`]**
-* `:ensure_entity_type`: Ensure Global Registry Entity Type exists and is up to date.
-(default: `true`) **[`:entity`]**
-
* `:client_integration_id`: Client Integration ID for relationship. Proc or Symbol.
-(default: `:primary_association.id`) **[`:relationship`]**
+(default: `:primary.id`) **[`:relationship`]**
* `:include_all_columns`: Include all model columns in the fields to push to Global Registry. If `false`, fields must
-be defined in the `:fields` option. (default: `false`)
-**[`:entity`, `:relationship`]**
+be defined in the `:fields` option. (default: `false`) **[`:entity`, `:relationship`]**
* `:exclude`: Array, Proc or Symbol. Array of Model fields (as symbols) to exclude when pushing to Global
Registry. Array Will additionally include `:mdm_id_column` and `:parent_association` foreign key when defined.
If Proc, is passed type and model instance and should return an Array of the fields to exclude. If Symbol,
this should be a method name the Model instance responds to. It is passed the type and should return an Array
@@ -140,38 +136,233 @@
option is nil or empty. (default: `nil`) **[`:entity`]**
* `:mdm_timeout`: Only pull mdm information at most once every `:mdm_timeout`. (default: `1.minute`)
**[`:entity`]**
-## Values for `fields`
+## Entities
-Values sent to Global Registry are calculated by sending the field `name` to the model. They can be overidden by
-aliasing an existing method, adding a new method to the model or by overriding the `entity_attributes_to_push`
-method. If a model does not respond to a name or raises a `NoMethodError`, the field will be omitted from the request.
+`global-registry-bindings` default bindings is to push an Active Record class as an Entity to Global Registry.
+This can be used to push root level entities, entities with a parent and entities with a hierarchy. You can also
+enable fetching of a Master Data Model from Global Registry.
+See [About Entities](https://github.com/CruGlobal/global_registry_docs/wiki/About-Entities) for more
+information on Global Registry Entities.
+
+### Root Entity
```ruby
class Person < ActiveRecord::Base
- # Person has first_name, last_name and guid columns
- global_registry_bindings fields: {full_name: :string, identity: :uuid, blargh: :integer},
- exclude: %i[guid]
+ global_registry_bindings mdm_id_column: :global_registry_mdm_id
+end
+```
+This will push the Person Active Record model to Global Registry as a `person` Entity Type, storing the resulting id
+value in the `global_registry_id` column, as well as fetching a `master_person` Entity and storing it in the
+`global_registry_mdm_id` column.
+
+### Parent/Child Entity
+```ruby
+class Person < ActiveRecord::Base
+ has_many :addresses, inverse_of: :person
+ global_registry_bindings
+end
+
+class Address < ActiveRecord::Base
+ belongs_to :person
+ global_registry_bindings
+end
+```
+This will push the Person model to Global Registry as a `person` Entity Type, and the Address model as an `address`
+Entity Type that has a parent of `person`.
+
+### Entity Hierarchy
+```ruby
+class Ministry < ActiveRecord::Base
+ has_many :children, class_name: 'Ministry', foreign_key: :parent_id
+ belongs_to :parent, class_name: 'Ministry'
- # Person doesn't respond to 'blargh' so it is omitted from the attributes to push
+ global_registry_bindings parent: :parent
+end
+```
+This will push the Ministry model to Global Registry as well as the parent/child hierarchy. Global Registry only allows
+a single parent, and does not allow circular references. Hierarchy is also EntityType specific, and not saved per
+system in Global Registry, meaning, the last system to push a parent wins (You can accidently override another systems
+hierarchy. This should be avoided and instead pushed as a relationship if needed).
+
+## Relationships
+
+`global-registry-bindings` can also be configured to push relationships between models to Global Registry. All
+relationships in Global Registry are many to many, but by using Active Record associations, we can simulate one to many
+and one to one.
+
+See [About Relationships](https://github.com/CruGlobal/global_registry_docs/wiki/About-Relationships) for more
+information on Global Registry relationships.
+
+### Many-to-Many with join model
+```ruby
+class Ministry < ActiveRecord::Base
+ has_many :assignments
+ has_many :people, through: :assignments
+ global_registry_bindings
+end
- alias_attribute :identity, :guid # Value for identity is aliased to guid
+class Person < ActiveRecord::Base
+ has_many :assignments
+ has_many :ministries, through: :assignments
+ global_registry_bindings
+end
+
+class Assignment < ActiveRecord::Base
+ belongs_to :person
+ belongs_to :ministry
+ global_registry_bindings binding: :relationship,
+ primary: :person,
+ primary_name: :people,
+ related: :ministry,
+ related_name: :ministries
+end
+```
+This will push Ministry and Person to Global Registry as Entities, and Assignment join model as a relationship between
+them, storing the relationship id in the Assignment `global_registry_id` column.
+
+### One-to-Many
+```ruby
+class Person < ActiveRecord::Base
+ has_many :pets
+ global_registry_bindings
+end
+
+class Pet < ActiveRecord::Base
+ belongs_to :person
+ global_registry_bindings binding: :relationship,
+ type: :owner,
+ related: :person
+end
+```
+
+## Fields and Values
+
+Both Entities and Relationships include fields that will be pushed to Global Registry.
+
+### Fields
+
+The fields that are pushed to Global Registry are defined with a combination of the `:fields`, `:exclude` and
+`:include_all_columns` options. The `:fields` option defines the fields and field types to be pushed. If
+`:include_all_columns` is set to `true`,`:fields` are appended to the list of all model columns. `:exclude` option is
+then used to remove fields from the list. If `:ensure_type` is `true`, the Global Registry EntityType or
+RelationshipType will be updated when new fields are defined. If `:ensure_type` is false, and fields are missing
+from the EntityType or RelationshipType, Global Registry will throw an error. It is the developers job to ensure Global
+Registry Entity and Relationship Types are accurate when `:ensure_type` is disabled.
+
+Given an Active Record model:
+```ruby
+create_table :products do |t|
+ t.string :name
+ t.text :description
+ t.string :global_registry_id
+ t.references :supplier, index: true, foreign_key: true
+ t.string :supplier_gr_rel_id, null: true, default: nil
+ t.timestamps null: false
+end
+```
+And the following `global_registry_bindings`:
+```ruby
+class Product < ActiveRecord::Base
+ belongs_to :supplier
+ global_registry_bindings fields: { name: string, description: :text }
+end
+```
+Will result in the following fields `{:name=>:string, :description=>:text}`
+
+```ruby
+class Product < ActiveRecord::Base
+ belongs_to :supplier
+ global_registry_bindings include_all_columns: true,
+ exclude: %i[supplier_id]
+end
+```
+Will result in the following fields `{:name=>:string, :description=>:text}`, `:id`, `:global_registry_id` and timestamp
+fields are excluded by default when `:include_all_columns` is `true`.
+
+You can add additional fields by specifying them in the `:fields` option.
+```ruby
+class Product < ActiveRecord::Base
+ belongs_to :supplier
+ global_registry_bindings include_all_columns: true,
+ exclude: %i[supplier_id],
+ fields: {color: :string}
+end
+```
+Will result in the following fields `{:name=>:string, :description=>:text, :color=>:string}`
+
+Relationships can also include fields:
+```ruby
+class Product < ActiveRecord::Base
+ belongs_to :supplier
+ global_registry_bindings fields: { name: string, description: :text }
+ global_registry_bindings binding: :relationship,
+ type: :supplier,
+ related: :supplier,
+ id_column: :supplier_gr_rel_id,
+ extra: {quantity: :integer}
+end
+```
+Will result in the following fields `{:quantity=>:integer}`
+
+`:fields` and `:exclude` can also be defined as a proc, labmda or symbol. Symbol must point to a method that will
+return either the extra or excluded fields.
+```ruby
+class Product < ActiveRecord::Base
+ belongs_to :supplier
+ global_registry_bindings include_all_columns: true,
+ exclude: ->(type, model) { model.name == 'Sprocket' ? [] : %i[:field1] },
+ fields: :extra_fields
+ def extra_fields(type)
+ # type === :product
+ {field1: :string, field2: :boolean}
+ end
+end
+```
+
+You can debug the current fields that will be pushed using the rails console:
+```ruby
+irb> Product.first.entity_columns_to_push
+=> {:name=>:string, :description=>:text}
+irb> Product.first.relationship_entity_columns(:supplier)
+=> {}
+```
+
+### Values
+
+When a model is pushed to global registry, `global-registry-bindings` will attempt to determined the values for
+each of the fields. This is done by calling the field name on the model. If the model responds, the value will be sent
+with the entity. Model and implement or override values with a few different options. They can use
+`alias_attribute :new_name, :old_name`, define a method `def field_name; "value"; end` or override
+`entity_attributes_to_push` or `relationship_attributes_to_push` respectively. When the `*_attributes_to_push` methods
+are used, you can modify values for other attributes as well as add additional fields and values. This is helpful
+when adding fields and values which may not be tracked directly on this model. An instance of this is adding an
+`authentication: { guid: 'UUID' }` field to a `person` entity_type to utilize Global Registry linked_identities.
+See [Entity Matching](https://github.com/CruGlobal/global_registry_docs/wiki/Entity-Matching).
+
+```ruby
+class Person < ActiveRecord::Base
+ alias_attribute :field1, :name
+
+ global_registry_bindings fields: { name: string, description: :text, field1: :boolean, field2: :integer }
- # Value for full_name
- def full_name
- "#{first_name} #{last_name}"
+ def field2
+ "#{name}:2"
end
- # Override entity_attributes_to_push to add or modify fields and values
def entity_attributes_to_push
- entity_attributes = super
- entity_attributes[:authentication] = { guid: guid }
- entity_attributes
+ attrs = super # Calls super to get field values, then modify them.
+ attrs[:description] = "Huge: #{attrs[:description]}"
+ attrs[:authentication] = { guid: 'UUID' }
+ attrs
end
end
```
+As an example, this would alias `field1` to `name` and use the method `field2` to determine the value for `field2`. It
+subsequently changes the value of `:description` and adds an `:authentication` field using the
+`entity_attributes_to_push` override.
## Example Models
Example models can be found in the [specs](https://github.com/CruGlobal/global-registry-bindings/tree/master/spec/internal/app/models).