ar_doc_store 0.2.0 → 0.2.1

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: a3700fe9780b88ba20e28eb918e3b49c307750cc
4
- data.tar.gz: 0ccdb51dd3e3b5b2c27176282b6f4d0fab67953d
3
+ metadata.gz: 389dfcb42c57c087c024326589ff09ca183bd224
4
+ data.tar.gz: ad6f5ac50966c3e2d8979cda69383c28dcaa0908
5
5
  SHA512:
6
- metadata.gz: e1794e597e28722089e23f160a3caf8f3376427ce57dfd9a5e942ccb24b3820cf37d5c886cc87cfc867451a70d29ed2ee8dbdab43f7380025e3f0284d12c271a
7
- data.tar.gz: 8f70729d52793af0a9dc970957ab986970d617792518f80b872c22b5026ead45afc7d641602e63fc6917d77d245f9f93e78c04446a4ac5f9bd72698813aa0fb7
6
+ metadata.gz: 4416430c4d7d03f97294e0b9fb31b3d03af2bb220d097975ff7ecd4104c27dfe8909125039c09c278dbefc633ebf5a303e95cf6f84e2a95fda10e1907a5d5dcd
7
+ data.tar.gz: bc49e9c3ed777a4f7fe140f6811ede9ba42123968545c602d121c4a86ad05e307791b21a2bc537eeee25958756d806592bc39d33bd855929cec62d494a4fab44
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ArDocStore
2
2
 
3
- ArDocStore (pronounced R-Dock-Store) is a gem helps you implement a document store 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.
3
+ ArDocStore (pronounced R-Dock-Store) is a gem helps you implement a document store 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
4
 
5
5
  There is also support for embedding models within models. These embedded models can be accessed from Rails form builders using fields_for.
6
6
 
@@ -8,7 +8,7 @@ The use case is primarily when you have a rapidly evolving schema with scores of
8
8
 
9
9
  Learn more about the JSON column in Postgres and using it as a document store:
10
10
  * The Rails Guide on Postgres: http://edgeguides.rubyonrails.org/active_record_postgresql.html
11
- * Document Store Gymnastics: http://rob.conery.io/2015/03/01/document-storage-gymnastics-in-postgres/
11
+ * Document Store Gymnastics: http://rob.conery.io/2015/03/01/document-storage-gymnastics-in-postgres/
12
12
  * Query JSON with Rails 4.2 and Postgres 9.4: http://robertbeene.com/rails-4-2-and-postgresql-9-4/
13
13
  * PG as NoSQL: http://thebuild.com%5Cpresentations%5Cpg-as-nosql-pgday-fosdem-2013.pdf
14
14
  * Why JSON in PostgreSQL is Awesome: https://functionwhatwhat.com/json-in-postgresql/
@@ -49,21 +49,30 @@ class Building < ActiveRecord::Base
49
49
  end
50
50
  ```
51
51
 
52
+ You don't have to use "data" as the column name. Use something else like do:
53
+ ```ruby
54
+ class Building < ActiveRecord::Base
55
+ include ArDocStore::Model
56
+ self.json_column = :json_data
57
+ end
58
+ ```
59
+
60
+
52
61
  ### Attributes
53
62
 
54
63
  Now there are several ways to play but all boil down to one method on the model:
55
64
  ```ruby
56
65
  class Building < ActiveRecord::Base
57
66
  include ArDocStore::Model
58
-
67
+
59
68
  attribute :name, :string
60
69
  attribute :width, :float
61
70
  attribute :height, as: :float # the :as is optional but if you like the consistency with SimpleForm
62
71
  attribute :storeys, :integer
63
72
  attribute :finished, :boolean
64
- attribute :construction_type, :enumeration,
65
- values: %w{wood plaster mud brick},
66
- multiple: true,
73
+ attribute :construction_type, :enumeration,
74
+ values: %w{wood plaster mud brick},
75
+ multiple: true,
67
76
  strict: true
68
77
  end
69
78
  ```
@@ -81,10 +90,10 @@ You noticed the enumeration type on the construction_type takes an array. That's
81
90
 
82
91
  ```ruby
83
92
  class Building ...
84
- attribute :construction_type,
85
- :enumeration,
86
- values: %w{wood plaster mud brick},
87
- multiple: true,
93
+ attribute :construction_type,
94
+ :enumeration,
95
+ values: %w{wood plaster mud brick},
96
+ multiple: true,
88
97
  strict: true
89
98
  end
90
99
  building = Building.new
@@ -101,17 +110,17 @@ Let's say that a building has a door. The building is not the only thing in our
101
110
  ```ruby
102
111
  class Door
103
112
  include ArDocStore::EmbeddableModel
104
-
105
- enumerates :door_type,
106
- multiple: true,
113
+
114
+ enumerates :door_type,
115
+ multiple: true,
107
116
  values: %w{single double french sliding push pull}
108
117
  attribute :open_handle,
109
- as: :enumeration,
110
- multiple: true,
118
+ as: :enumeration,
119
+ multiple: true,
111
120
  values: %w{push pull plate knob handle}
112
- attribute :close_handle,
113
- as: :enumeration,
114
- multiple: true,
121
+ attribute :close_handle,
122
+ as: :enumeration,
123
+ multiple: true,
115
124
  values: %w{push pull plate knob handle}
116
125
  attribute :clear_distance, as: :integer
117
126
  attribute :opening_force, as: :integer
@@ -1,20 +1,20 @@
1
1
  module ArDocStore
2
2
  module AttributeTypes
3
-
3
+
4
4
  class ArrayAttribute < BaseAttribute
5
5
 
6
6
  def build
7
7
  key = attribute.to_sym
8
8
  default_value = default
9
9
  model.class_eval do
10
- store_accessor :data, key
10
+ store_accessor json_column, key
11
11
  define_method key, -> {
12
- value = read_store_attribute(:data, key)
12
+ value = read_store_attribute(json_column, key)
13
13
  value or default_value
14
14
  }
15
15
  define_method "#{key}=".to_sym, -> (value) {
16
16
  value = nil if value == ['']
17
- write_store_attribute(:data, key, value)
17
+ write_store_attribute(json_column, key, value)
18
18
  }
19
19
  add_ransacker(key, 'text')
20
20
  end
@@ -28,4 +28,3 @@ module ArDocStore
28
28
 
29
29
  end
30
30
  end
31
-
@@ -26,7 +26,7 @@ module ArDocStore
26
26
  model.class_eval do
27
27
  add_ransacker(attribute, predicate)
28
28
  define_method attribute.to_sym, -> {
29
- value = read_store_attribute(:data, attribute)
29
+ value = read_store_attribute(json_column, attribute)
30
30
  if value
31
31
  value.public_send(typecast_method)
32
32
  elsif default_value
@@ -36,9 +36,9 @@ module ArDocStore
36
36
  }
37
37
  define_method "#{attribute}=".to_sym, -> (value) {
38
38
  if value == '' || value.nil?
39
- write_store_attribute :data, attribute, nil
39
+ write_store_attribute json_column, attribute, nil
40
40
  else
41
- write_store_attribute(:data, attribute, value.public_send(typecast_method))
41
+ write_store_attribute(json_column, attribute, value.public_send(typecast_method))
42
42
  end
43
43
  }
44
44
  end
@@ -5,11 +5,11 @@ module ArDocStore
5
5
  def build
6
6
  key = attribute.to_sym
7
7
  model.class_eval do
8
- store_accessor :data, key
8
+ store_accessor json_column, key
9
9
  define_method "#{key}?".to_sym, -> { public_send(key) == true }
10
10
  define_method "#{key}=".to_sym, -> (value) {
11
11
  res = ArDocStore.convert_boolean(value)
12
- write_store_attribute(:data, key, res)
12
+ write_store_attribute(json_column, key, res)
13
13
  }
14
14
  add_ransacker(key, 'bool')
15
15
  end
@@ -1,9 +1,9 @@
1
1
  module ArDocStore
2
-
2
+
3
3
  class EmbeddedCollection < Array
4
4
  attr_accessor :parent
5
5
  end
6
-
6
+
7
7
  module AttributeTypes
8
8
 
9
9
  class EmbedsManyAttribute < BaseAttribute
@@ -11,7 +11,7 @@ module ArDocStore
11
11
  def build
12
12
  assn_name = attribute.to_sym
13
13
  class_name = options[:class_name] || attribute.to_s.classify
14
- model.store_accessor :data, assn_name
14
+ model.store_accessor model.json_column, assn_name
15
15
  create_reader_for assn_name, class_name
16
16
  create_writer_for assn_name, class_name
17
17
  create_build_method_for assn_name, class_name
@@ -19,23 +19,23 @@ module ArDocStore
19
19
  create_embeds_many_attributes_method(class_name, assn_name)
20
20
  create_embeds_many_validation(assn_name)
21
21
  end
22
-
22
+
23
23
  private
24
-
24
+
25
25
  def add_method(method, block)
26
26
  model.class_eval do
27
27
  define_method method, block
28
28
  end
29
29
  end
30
-
30
+
31
31
  def create_reader_for(assn_name, class_name)
32
32
  add_method assn_name.to_sym, -> {
33
33
  ivar = "@#{assn_name}"
34
34
  instance_variable_get(ivar) || begin
35
35
  my_class_name = class_name.constantize
36
- items = read_store_attribute(:data, assn_name)
36
+ items = read_store_attribute(json_column, assn_name)
37
37
  if items.is_a?(Array) || items.is_a?(ArDocStore::EmbeddedCollection)
38
- items = ArDocStore::EmbeddedCollection.new items.map { |item| my_class_name.build(item) }
38
+ items = ArDocStore::EmbeddedCollection.new items.map { |item| item.is_a?(my_class_name) ? item : my_class_name.build(item) }
39
39
  else
40
40
  items ||= ArDocStore::EmbeddedCollection.new
41
41
  end
@@ -60,10 +60,10 @@ module ArDocStore
60
60
  items = []
61
61
  end
62
62
  items.parent = self
63
- instance_variable_set "@#{assn_name}", write_store_attribute(:data, assn_name, items)
63
+ instance_variable_set "@#{assn_name}", write_store_attribute(json_column, assn_name, items)
64
64
  }
65
65
  end
66
-
66
+
67
67
  def create_build_method_for(assn_name, class_name)
68
68
  add_method "build_#{assn_name.to_s.singularize}", -> (attributes=nil) {
69
69
  assns = self.public_send assn_name
@@ -74,18 +74,18 @@ module ArDocStore
74
74
  item
75
75
  }
76
76
  end
77
-
77
+
78
78
  def create_ensure_method_for(assn_name)
79
79
  method = -> { public_send "build_#{assn_name.to_s.singularize}" if self.public_send(assn_name).blank? }
80
80
  add_method "ensure_#{assn_name.to_s.singularize}", method
81
81
  add_method "ensure_#{assn_name}", method
82
82
  end
83
-
83
+
84
84
  def create_embeds_many_attributes_method(class_name, assn_name)
85
85
  add_method "#{assn_name}_attributes=", -> (values) {
86
86
  return if values.blank?
87
87
  # if it's a single item then wrap it in an array but how to tell?
88
-
88
+
89
89
  if values.respond_to?(:each)
90
90
  if values.respond_to?(:values)
91
91
  values = values.values
@@ -103,9 +103,9 @@ module ArDocStore
103
103
  validate_method = "validate_embedded_record_for_#{assn_name}"
104
104
  define_method validate_method, -> { validate_embeds_many assn_name }
105
105
  validate validate_method
106
- end
106
+ end
107
107
  end
108
-
108
+
109
109
 
110
110
  end
111
111
 
@@ -113,7 +113,7 @@ module ArDocStore
113
113
  attr_reader :models, :assn_name, :parent, :class_name
114
114
  def initialize(parent, class_name, assn_name, models, values)
115
115
  @parent, @class_name, @assn_name, @models, @values = parent, class_name, assn_name, models, values
116
- values.each { |value|
116
+ values.each { |value|
117
117
  value = value.symbolize_keys
118
118
  if value.key?(:id)
119
119
  process_existing_model(value)
@@ -122,22 +122,22 @@ module ArDocStore
122
122
  end
123
123
  }
124
124
  end
125
-
125
+
126
126
  private
127
-
127
+
128
128
  attr_writer :models, :values
129
129
  attr_reader :values, :assn_name
130
-
130
+
131
131
  def process_existing_model(value)
132
132
  return false unless value.key?(:id)
133
133
  model = find_model_by_value(value)
134
134
  model && destroy_or_update(model, value) or add(value)
135
135
  end
136
-
136
+
137
137
  def destroy_or_update(model, value)
138
138
  destroy(model, value) or update_attributes(model, value)
139
139
  end
140
-
140
+
141
141
  def add(value)
142
142
  models << class_name.constantize.new(value)
143
143
  end
@@ -145,15 +145,15 @@ module ArDocStore
145
145
  def destroy(model, value)
146
146
  wants_to_die?(value) && models.delete(model)
147
147
  end
148
-
148
+
149
149
  def update_attributes(model, value)
150
150
  model.attributes = value
151
151
  end
152
-
152
+
153
153
  def wants_to_die?(value)
154
154
  value.key?(:_destroy) && ArDocStore.convert_boolean(value[:_destroy])
155
155
  end
156
-
156
+
157
157
  def find_model_by_value(value)
158
158
  models.detect { |item| item.id == value[:id] }
159
159
  end
@@ -15,7 +15,7 @@ module ArDocStore
15
15
  model.class_eval <<-CODE, __FILE__, __LINE__ + 1
16
16
  def #{attribute}
17
17
  @#{attribute} || begin
18
- item = read_store_attribute :data, :#{attribute}
18
+ item = read_store_attribute json_column, :#{attribute}
19
19
  item = #{class_name}.build(item) unless item.is_a?(#{class_name})
20
20
  @#{attribute} = item
21
21
  end
@@ -29,7 +29,7 @@ module ArDocStore
29
29
  end
30
30
  value = #{class_name}.build value
31
31
  @#{attribute} = value
32
- write_store_attribute :data, :#{attribute}, value
32
+ write_store_attribute json_column, :#{attribute}, value
33
33
  end
34
34
  CODE
35
35
  end
@@ -67,7 +67,7 @@ module ArDocStore
67
67
  validate :validate_embedded_record_for_#{attribute}
68
68
  CODE
69
69
  end
70
-
70
+
71
71
  end
72
72
 
73
73
  end
@@ -7,17 +7,17 @@ module ArDocStore
7
7
  def build
8
8
  key = attribute.to_sym
9
9
  model.class_eval do
10
- store_accessor :data, key
10
+ store_accessor json_column, key
11
11
  define_method key, -> {
12
- value = read_store_attribute(:data, key)
12
+ value = read_store_attribute(json_column, key)
13
13
  unless value
14
14
  value = SecureRandom.uuid
15
- write_store_attribute :data, key, value
15
+ write_store_attribute json_column, key, value
16
16
  end
17
17
  value
18
18
  }
19
19
  define_method "#{key}=".to_sym, -> (value) {
20
- write_store_attribute(:data, key, value)
20
+ write_store_attribute(json_column, key, value)
21
21
  }
22
22
  add_ransacker(key, 'text')
23
23
  end
@@ -21,17 +21,17 @@ module ArDocStore
21
21
 
22
22
  class_attribute :virtual_attributes
23
23
  self.virtual_attributes ||= HashWithIndifferentAccess.new
24
-
24
+
25
25
  delegate :as_json, to: :attributes
26
-
26
+
27
27
  attribute :id, :uuid
28
28
 
29
29
  end
30
30
 
31
31
  end
32
-
32
+
33
33
  module InstanceMethods
34
-
34
+
35
35
  def initialize(attrs=HashWithIndifferentAccess.new)
36
36
  @_initialized = true
37
37
  initialize_attributes attrs
@@ -61,7 +61,7 @@ module ArDocStore
61
61
  end
62
62
  self
63
63
  end
64
-
64
+
65
65
  def persisted?
66
66
  false
67
67
  end
@@ -98,16 +98,16 @@ module ArDocStore
98
98
  end
99
99
 
100
100
  end
101
-
101
+
102
102
  module ClassMethods
103
-
103
+
104
104
  #:nodoc:
105
105
  def store_accessor(store, key)
106
106
  self.virtual_attributes ||= HashWithIndifferentAccess.new
107
107
  self.virtual_attributes = virtual_attributes.merge key => true
108
108
  key = key.to_sym
109
- define_method key, -> { read_store_attribute(:data, key) }
110
- define_method "#{key}=".to_sym, -> (value) { write_store_attribute :data, key, value }
109
+ define_method key, -> { read_store_attribute(json_column, key) }
110
+ define_method "#{key}=".to_sym, -> (value) { write_store_attribute json_column, key, value }
111
111
  end
112
112
 
113
113
  def build(attrs=HashWithIndifferentAccess.new)
@@ -124,4 +124,3 @@ module ArDocStore
124
124
 
125
125
  end
126
126
  end
127
-
@@ -1,20 +1,26 @@
1
1
  module ArDocStore
2
2
  module Storage
3
-
3
+
4
4
  def self.included(mod)
5
-
5
+
6
+ mod.class_attribute :json_column
7
+ mod.json_column ||= :data
6
8
  mod.class_attribute :virtual_attributes
7
9
  mod.virtual_attributes ||= HashWithIndifferentAccess.new
8
-
10
+
9
11
  mod.send :include, InstanceMethods
10
12
  mod.send :extend, ClassMethods
11
13
  end
12
-
14
+
13
15
  module InstanceMethods
14
-
16
+
17
+ def json_column
18
+ self.class.json_column
19
+ end
20
+
15
21
  def write_attribute(name, value)
16
22
  if is_stored_attribute?(name)
17
- write_store_attribute :data, attribute_name_from_foreign_key(name), value
23
+ write_store_attribute json_column, attribute_name_from_foreign_key(name), value
18
24
  else
19
25
  super
20
26
  end
@@ -22,14 +28,14 @@ module ArDocStore
22
28
 
23
29
  def read_attribute(name)
24
30
  if is_stored_attribute?(name)
25
- read_store_attribute :data, attribute_name_from_foreign_key(name)
31
+ read_store_attribute json_column, attribute_name_from_foreign_key(name)
26
32
  else
27
33
  super
28
34
  end
29
35
  end
30
36
 
31
37
  private
32
-
38
+
33
39
  def is_stored_attribute?(name)
34
40
  name = name.to_sym
35
41
  is_store_accessor_method?(name) || name =~ /data\-\>\>/
@@ -37,7 +43,7 @@ module ArDocStore
37
43
 
38
44
  def is_store_accessor_method?(name)
39
45
  name = name.to_sym
40
- self.class.stored_attributes[:data] && self.class.stored_attributes[:data].include?(name)
46
+ self.class.stored_attributes[json_column] && self.class.stored_attributes[json_column].include?(name)
41
47
  end
42
48
 
43
49
  def attribute_name_from_foreign_key(name)
@@ -58,9 +64,9 @@ module ArDocStore
58
64
  super clean_attribute_names_for_arel(attribute_names)
59
65
  end
60
66
  end
61
-
67
+
62
68
  module ClassMethods
63
-
69
+
64
70
  def attribute(name, *args)
65
71
  type = args.shift if args.first.is_a?(Symbol)
66
72
  options = args.extract_options!
@@ -147,8 +153,8 @@ module ArDocStore
147
153
  attribute field, options
148
154
  end
149
155
 
150
-
156
+
151
157
  end
152
-
158
+
153
159
  end
154
- end
160
+ end
@@ -1,3 +1,3 @@
1
1
  module ArDocStore
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/test/test_helper.rb CHANGED
@@ -45,8 +45,8 @@ class ARDuck
45
45
 
46
46
  def self.store_accessor(store, key)
47
47
  key = key.to_sym
48
- define_method key, -> { read_store_attribute(:data, key) }
49
- define_method "#{key}=".to_sym, -> (value) { write_store_attribute :data, key, value }
48
+ define_method key, -> { read_store_attribute(json_column, key) }
49
+ define_method "#{key}=".to_sym, -> (value) { write_store_attribute json_column, key, value }
50
50
  end
51
51
 
52
52
  def read_store_attribute(store, key)
@@ -113,7 +113,7 @@ class Restroom
113
113
  include ArDocStore::EmbeddableModel
114
114
  embeds_one :route
115
115
  embeds_one :door
116
-
116
+
117
117
  enumerates :restroom_type, values: %w{single double dirty nasty clean}
118
118
 
119
119
  attribute :is_restroom_provided, as: :boolean
@@ -121,9 +121,9 @@ class Restroom
121
121
 
122
122
  embeds_one :stall_area_dimensions, class_name: 'Dimensions'
123
123
  embeds_one :sink_area_dimensions, class_name: 'Dimensions'
124
-
124
+
125
125
  validates :restroom_type, presence: true
126
-
126
+
127
127
  end
128
128
 
129
129
  class Building < ActiveRecord::Base
@@ -141,4 +141,3 @@ class Building < ActiveRecord::Base
141
141
  embeds_many :entrances
142
142
  embeds_many :restrooms
143
143
  end
144
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ar_doc_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Furber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-23 00:00:00.000000000 Z
11
+ date: 2016-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord