ar_doc_store 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2438ac2cf9e760f9b289db586f62688dec2eb781
4
- data.tar.gz: 19817b74ce307e2e7315f93edc8d0fe40f351e6e
3
+ metadata.gz: e38a8f9b053750e7ffcceb4cc75567d1c2b082f7
4
+ data.tar.gz: c7c42d91dd649a304f115fcb682b0bd13bdf65c8
5
5
  SHA512:
6
- metadata.gz: e760c26792d8ef0b527ad0354ed8f27ba793097131d41fa412f68c7e7f713f3394d4ae66e0a395275e0ba7cf270ba3749d72b46944a008d25fe2aa70b478216c
7
- data.tar.gz: 23bc9fe56773dc79d6d70c2149c6fc39f1683b5db7efac20250569742de644c967397d8ece268119a56f09f7deceddad8c30128e935462f809bfafe9be038047
6
+ metadata.gz: 3203c8187eb0a57cbc4975dce49d1f23c84f956f2f97846f293b0f60d7216f306e1a4034344c69e0b49d1cd3dd7f874f71b73c06d5d9549805bde838f5069466
7
+ data.tar.gz: 9125796b99835dfe9f806d05dcace1019065112bd2afea6419e2a50d46179716b3ca911811c6c0792be632a96bf4beae52c059b763ca32671a552772af0aa53a
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ *.gem
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /Gemfile.lock
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # ArDocStore
2
2
 
3
- TODO: Write a gem description
3
+ 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.
4
+
5
+ There is also support for embedding models within models. These embedded models can be accessed from Rails form builders using fields_for.
6
+
7
+ 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.
4
8
 
5
9
  ## Installation
6
10
 
@@ -20,7 +24,202 @@ Or install it yourself as:
20
24
 
21
25
  ## Usage
22
26
 
23
- TODO: Write usage instructions here
27
+ 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:
28
+
29
+ ```ruby
30
+ change_table :buildings do |t|
31
+ t.json :data
32
+ end
33
+ ``
34
+
35
+ Then in the model file:
36
+
37
+ ```ruby
38
+ class Building < ActiveRecord::Base
39
+ include ArDocStore::Model
40
+ end
41
+ ```
42
+
43
+ ### Attributes
44
+
45
+ Now there are several ways to play but all boil down to one method on the model:
46
+ ```ruby
47
+ class Building < ActiveRecord::Base
48
+ include ArDocStore::Model
49
+
50
+ attribute :name, :string
51
+ attribute :width, :float
52
+ attribute :height, as: :float
53
+ attribute :storeys, :integer
54
+ attribute :finished, :boolean
55
+ attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
56
+ end
57
+ ```
58
+
59
+ Now I can do some fun things:
60
+ ```ruby
61
+ building = Building.new name: 'Big Building', storeys: 45, construction_type: %w{mud brick}, finished: true
62
+ building.width = 42.5
63
+ building.height = 32.259
64
+ building.finished? # => true
65
+ building.save
66
+ ```
67
+
68
+ 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:
69
+
70
+ ```ruby
71
+ class Building ...
72
+ attribute :construction_type, :enumeration, values: %w{wood plaster mud brick}, multiple: true, strict: true
73
+ end
74
+ building = Building.new
75
+ building.construction_type = 'wood'
76
+ building.construction_type # => 'wood'
77
+ ```
78
+
79
+ 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.
80
+
81
+ ### Embedding Models
82
+
83
+ 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:
84
+
85
+ ```ruby
86
+ class Door
87
+ include ArDocStore::EmbeddableModel
88
+
89
+ enumerates :door_type, multiple: true, values: %w{single double french sliding push pull}
90
+ attribute :open_handle, as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
91
+ attribute :close_handle, as: :enumeration, multiple: true, values: %w{push pull plate knob handle}
92
+ attribute :clear_distance, as: :integer
93
+ attribute :opening_force, as: :integer
94
+ attribute :clear_space, as: :integer
95
+ end
96
+ ```
97
+
98
+ Now let's put a Door on a Building:
99
+
100
+ ```ruby
101
+ class Building ...
102
+ embeds_one :door
103
+ end
104
+ ```
105
+
106
+ Now let's make a building with a door:
107
+ ```ruby
108
+ building = Building.new
109
+ building.build_door
110
+ building.door.clear_distance = 30
111
+ building.door.opening_force = 20
112
+ building.door.open_handle = %w{pull knob}
113
+ building.save
114
+ ```
115
+
116
+ We probably have a form for the building, so here goes:
117
+ ```ruby
118
+ # in the controller:
119
+ def new
120
+ @building = Building.new
121
+ end
122
+
123
+ def resource_params
124
+ params.require(:building).permit :name, :height, door_attributes: [:door_type]
125
+ end
126
+
127
+ = simple_form_for @building do |form|
128
+ = form.input :name, as: :string
129
+ = form.input :height, as: :float
130
+ = form.object.ensure_door
131
+ = form.fields_for :door do |door_form|
132
+ = door_form.input :door_type, as: :check_boxes, collection: Door.door_type_choices
133
+ ```
134
+
135
+ 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.
136
+
137
+ You can also embeds_many. It works the same way:
138
+
139
+ ```ruby
140
+ class Room
141
+ include ArDocStore::EmbeddableModel
142
+ attribute :length, as: :float
143
+ attribute :width, as: :float
144
+ attribute :height, as: :float
145
+ enumerates :light_switch_type, %w{flip knob switchplate clapper}
146
+ end
147
+
148
+ class Building ...
149
+ embeds_many :rooms
150
+ end
151
+
152
+ building = Building.new
153
+ building.build_room # a bit different from active record here, I with that has_many used the has_one API for build_association.
154
+ building.ensure_room # if there are no rooms, then add one to the rooms collection, otherwise do nothing
155
+ building.rooms << Room.new(width: 12, height: 18, length: 20, light_switch_type: 'flip')
156
+ building.save # saves the room
157
+ ```
158
+
159
+ ### Searching Models
160
+
161
+ In my dreams, I could type: Building.where(rooms: { length: 20 }). If somebody can guide me toward that dream, please do. In the meantime, there is Ransack. When you call "attribute" on a Model (not yet an embedded model, sorry) and you've got the Ransack gem installed, then you will get a free custom ransacker. So you can still do this, and it's pretty awesome:
162
+
163
+ ```ruby
164
+ Building.ransack name_cont: 'tall', height_lteq: 20
165
+ ```
166
+
167
+ ### Custom attribute types
168
+
169
+ ArDocStore comes with several basic attribute types: array, boolean, enumeration, float, integer, and string. The implementation and extension points are inspired by SimpleForm. You can either create a new attribute type or overwrite an existing one. Forewarned is forestalled, maybe: as with SimpleForm, the custom input system is way easier to use if you were the one who built it, and it's still a little raw. Let's start with the implementation of :integer :
170
+
171
+ ```ruby
172
+ class IntegerAttribute < Base
173
+ def conversion
174
+ :to_i
175
+ end
176
+
177
+ def predicate
178
+ 'int'
179
+ end
180
+ end
181
+ ```
182
+
183
+ Note that the data is getting put into a JSON column, so we want to make sure we get it out in the form that we want it. So the conversion method makes sure that it doesn't go in a integer and come back as a string. The predicate method tells postgres (via the ransacker) how to cast the JSON for searching.
184
+
185
+ Not all attribute types are that simple. Sometimes we have to put all the juice in the build method, and take care to define the getter and setter ourselves. In this example, we want to replace the boolean attribute with a similar boolean that can do Yes, No, and N/A, where N/A isn't simply nil but something the user has to choose. Here goes:
186
+
187
+ ```ruby
188
+ class BooleanAttribute < ArDocStore::AttributeTypes::Base
189
+ def build
190
+ key = attribute.to_sym
191
+ model.class_eval do
192
+ store_accessor :data, key
193
+ define_method "#{key}?".to_sym, -> { key == 1 }
194
+ define_method "#{key}=".to_sym, -> (value) {
195
+ res = nil
196
+ res = 1 if value == 'true' || value == true || value == '1' || value == 1
197
+ res = 0 if value == 'false' || value == false || value == '0' || value == 0
198
+ res = -1 if value == '-1'
199
+ write_store_attribute(:data, key, value)
200
+ }
201
+ add_ransacker(key, 'bool')
202
+ end
203
+ end
204
+ end
205
+
206
+ ArDocStore.mappings.merge! boolean: '::BooleanAttribute'
207
+
208
+ class BooleanInput < SimpleForm::Inputs::CollectionRadioButtonsInput
209
+ def self.boolean_collection
210
+ i18n_cache :boolean_collection do
211
+ [ [I18n.t(:"simple_form.yes", default: 'Yes'), 1],
212
+ [I18n.t(:"simple_form.no", default: 'No'), 0],
213
+ [I18n.t(:"simple_form.na", default: 'N/A'), -1]]
214
+ end
215
+ end
216
+
217
+ def input_type
218
+ :radio_buttons
219
+ end
220
+
221
+ end
222
+ ```
24
223
 
25
224
  ## Contributing
26
225
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["dfurber@gorges.us"]
11
11
  spec.summary = %q{A document storage gem meant for ActiveRecord PostgresQL JSON storage.}
12
12
  spec.description = %q{Provides an easy way to do something that is possible in Rails but still a bit close to the metal using store_accessor: create typecasted, persistent attributes that are not columns in the database but stored in the JSON "data" column. Also supports infinite nesting of embedded models.}
13
- spec.homepage = ""
13
+ spec.homepage = "https://github.com/dfurber/ar_doc_store"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -13,7 +13,6 @@ module ArDocStore
13
13
  res = true if value == 'true' || value == true || value == '1' || value == 1
14
14
  res = false if value == 'false' || value == false || value == '0' || value == 0
15
15
  write_store_attribute(:data, key, res)
16
- data_will_change!
17
16
  }
18
17
  add_ransacker(key, 'bool')
19
18
  end
@@ -1,3 +1,3 @@
1
1
  module ArDocStore
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar_doc_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Furber
@@ -88,7 +88,7 @@ files:
88
88
  - test/embedding_test.rb
89
89
  - test/model_attribute_access_test.rb
90
90
  - test/test_helper.rb
91
- homepage: ''
91
+ homepage: https://github.com/dfurber/ar_doc_store
92
92
  licenses:
93
93
  - MIT
94
94
  metadata: {}