<commit_before># ArDocStore

ArDocStore is a gem that makes it easy to handle document-store like behavior in ActiveRecord models that have access to the PostgreSQL JSON data type. You add a json column to your table called "data", include the ArDocStore::Model module, and then add schema-less attributes that get stored in the data column. With Ransack, these attributes are searchable as if they were real columns. 

There is also support for embedding models within models. These embedded models can be accessed from Rails form builders using fields_for.

The use case is primarily when you have a rapidly evolving schema with scores of attributes, where you would like to use composition but don't want to have a bajillion tables for data that fits squarely under the umbrella of its parent entity. For example, a building has entrances and restrooms, and the entrance and restroom each have a door and a route to the door. You could have active record models for the doors, routes, restrooms, and entrances, but you know that you only ever need to access the bathroom door from within the context of the building's bathroom. You don't need to access all the doors as their own entities because their existence is entirely contingent upon the entity within which they are contained. ArDocStore makes this easy.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'ar_doc_store'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install ar_doc_store

## Usage

The first thing you need to do is create a migration that adds a column called "data" of type ":json" to the table you want to turn into a document:

```ruby
change_table :buildings do |t|
	t.json :data
end
```

Then in the model file:

```ruby
class Building < ActiveRecord::Base
	include ArDocStore::Model
end
```

### Attributes

Now there are several ways to play but all boil down to one method on the model:
```ruby
class Building < ActiveRecord::Base
	include ArDocStore::Model
	
	attribute :name, :string
	attribute :width, :float
	attribute :height, as: :float
	attribute :storeys, :integer
	attribute :finished, :boolean
	attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
end
```

Now I can do some fun things:
```ruby
building = Building.new name: '<NAME>', storeys: 45, construction_type: %w{mud brick}, finished: true
building.width = 42.5
building.height = 32.259
building.finished? # => true
building.save
```

You noticed the enumeration type on the construction_type takes an array. That's because I specified multiple: true. Let's say you can't have a mud and a brick building at the same time. Let's take off the multiple: true and see how it behaves:

```ruby
class Building ...
	attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
end
building = Building.new
building.construction_type = 'wood'
building.construction_type  # => 'wood'
```

The strict: true option makes the enumeration strict, meaning only the choices in the enumeration are allowed. Maybe that should be the default choice, but the project in which this took shape had most of the enumerations with an "other" field. We took care of it with this and a custom SimpleForm input.

### Embedding Models

Let's say that a building has a door. The building is not the only thing in our world that has a door, so we want to be able to embed doors in other models, such as skating rinks, comfort stations, and gyms. First let's make an embeddable model called Door:

```ruby
class Door
  include ArDocStore::EmbeddableModel
	
  enumerates :door_type, multiple: true, values: %w{single double french sliding push pull}
  attribute :open_handle,  as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
  attribute :close_handle, as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
  attribute :clear_distance, as: :integer
  attribute :opening_force, as: :integer
  attribute :clear_space, as: :integer
end
```

Now let's put a Door on a Building:

```ruby
class Building ...
  embeds_one :door
end
```

Now let's make a building with a door:
```ruby
building = Building.new
building.build_door
building.door.clear_distance = 30
building.door.opening_force = 20
building.door.open_handle = %w{pull knob}
building.save
```

We probably have a form for the building, so here goes:
```ruby
# in the controller:
def new
	@building = Building.new
end

def resource_params
	params.require(:building).permit :name, :height, door_attributes: [:door_type]
end

= simple_form_for @building do |form|
	= form.input :name, as: :string
	= form.input :height, as: :float
	= form.object.ensure_door
	= form.fields_for :door do |door_form|
		= door_form.input :door_type, as: :check_boxes, collection: Door.door_type_choices
```

What's to see here? Notice that I was able to "ensure_door", which means that if there already is a door, it keeps that one, otherwise it builds a new door object. Also on the door_type input, notice the collection comes from a door_type_choices that came from the enumeration attribute. Also notice that the embedded model conforms to the API for accepts_nested_attributes, for both assignment and validation, only you don't have to specify it because the _attributes= method comes for free.

You can also embeds_many. It works the same way:

```ruby
class Room
	include ArDocStore::EmbeddableModel
	attribute :length, as: :float<commit_msg>more doc<commit_after># ArDocStore

ArDocStore is a gem that makes it easy to handle document-store like behavior in ActiveRecord models that have access to the PostgreSQL JSON data type. You add a json column to your table called "data", include the ArDocStore::Model module, and then add schema-less attributes that get stored in the data column. With Ransack, these attributes are searchable as if they were real columns. 

There is also support for embedding models within models. These embedded models can be accessed from Rails form builders using fields_for.

The use case is primarily when you have a rapidly evolving schema with scores of attributes, where you would like to use composition but don't want to have a bajillion tables for data that fits squarely under the umbrella of its parent entity. For example, a building has entrances and restrooms, and the entrance and restroom each have a door and a route to the door. You could have active record models for the doors, routes, restrooms, and entrances, but you know that you only ever need to access the bathroom door from within the context of the building's bathroom. You don't need to access all the doors as their own entities because their existence is entirely contingent upon the entity within which they are contained. ArDocStore makes this easy.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'ar_doc_store'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install ar_doc_store

## Usage

The first thing you need to do is create a migration that adds a column called "data" of type ":json" to the table you want to turn into a document:

```ruby
change_table :buildings do |t|
	t.json :data
end
```

Then in the model file:

```ruby
class Building < ActiveRecord::Base
	include ArDocStore::Model
end
```

### Attributes

Now there are several ways to play but all boil down to one method on the model:
```ruby
class Building < ActiveRecord::Base
	include ArDocStore::Model
	
	attribute :name, :string
	attribute :width, :float
	attribute :height, as: :float # the :as is optional but if you like the consistency with SimpleForm
	attribute :storeys, :integer
	attribute :finished, :boolean
	attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
end
```

Now I can do some fun things:
```ruby
building = Building.new name: 'Big Building', storeys: 45, construction_type: %w{mud brick}, finished: true
building.width = 42.5
building.height = 32.259
building.finished? # => true
building.save
```

You noticed the enumeration type on the construction_type takes an array. That's because I specified multiple: true. Let's say you can't have a mud and a brick building at the same time. Let's take off the multiple: true and see how it behaves:

```ruby
class Building ...
	attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
end
building = Building.new
building.construction_type = 'wood'
building.construction_type  # => 'wood'
```

The strict: true option makes the enumeration strict, meaning only the choices in the enumeration are allowed. Maybe that should be the default choice, but the project in which this took shape had most of the enumerations with an "other" field. We took care of it with this and a custom SimpleForm input.

### Embedding Models

Let's say that a building has a door. The building is not the only thing in our world that has a door, so we want to be able to embed doors in other models, such as skating rinks, comfort stations, and gyms. First let's make an embeddable model called Door:

```ruby
class Door
  include ArDocStore::EmbeddableModel
	
  enumerates :door_type, multiple: true, values: %w{single double french sliding push pull}
  attribute :open_handle,  as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
  attribute :close_handle, as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
  attribute :clear_distance, as: :integer
  attribute :opening_force, as: :integer
  attribute :clear_space, as: :integer
end
```

Now let's put a Door on a Building:

```ruby
class Building ...
  embeds_one :door
  embeds_one :secret_door, class_name: 'Door'
end
```

Now let's make a building with a door:
```ruby
building = Building.new
building.build_door
building.door.clear_distance = 30
building.door.opening_force = 20
building.door.open_handle = %w{pull knob}
building.build_secret_door
building.secret_door.clear_distance = 3
building.save
```

We probably have a form for the building, so here goes:
```ruby
# in the controller:
def new
	@building = Building.new
end

def resource_params
	params.require(:building).permit :name, :height, door_attributes: [:door_type]
end

# in the view, with a bonus plus for Slim templates:
= simple_form_for @building do |form|
	= form.input :name, as: :string
	= form.input :height, as: :float
	= form.object.ensure_door
	= form.fields_for :door do |door_form|
		= door_form.input :door_type, as: :check_boxes, collection: Door.door_type_choices
```

What's to see here? Notice that I was able to "ensure_door", which means that if there already is a door, it keeps that one, otherwise it builds a new door object. Also on the door_type input, notice the collection comes from a door_type_choices that came from the enumeration attribute. Also notice that the embedded model conforms to the API for accepts_nested_attributes, for both assignment and validation, only you don't have to specify it because the _attributes= method comes for free.

You can also embeds_many. It works the same way:

```ruby
class Room
	include ArDocStore::EmbeddableModel
	attribute :length, as: :float
{'fraction_non_alphanumeric': 0.07074926673184606, 'fraction_numerical': 0.0025775486623411253, 'mean_word_length': 4.746680286006129, 'pattern_counts': {'":': 2, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2288554', 'n_tokens_mistral': 3193, 'n_tokens_neox': 3002, 'n_words': 1580}