post_json 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +129 -99
- data/lib/post_json/base.rb +42 -10
- data/lib/post_json/version.rb +1 -1
- data/spec/models/derived_base_spec.rb +19 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb5fea92951fd8ec7e82b70905b2ae78c5e28071
|
4
|
+
data.tar.gz: 412eaafc6a0b9c802dd02116898dc173f194d454
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4874f99abaec969d3544de9ad4e1db5f651a16d50625ecb9112e2913b2048e7c6183b5ab154efcec482a1d0a6ab957a37a7a8cb22ec33417db76e548e4f16fe0
|
7
|
+
data.tar.gz: 9fc7845cf1082ed2f0670d43aa8ab07ab27780340122d0f3d139b2e2c9f8a1f8d0aae686f073a4e2f44e8191b05f743cdecf98a8efe94f08ff86462ccc6791ff
|
data/README.md
CHANGED
@@ -11,15 +11,16 @@ See example of how we use PostJson as part of <a href="https://github.com/webnut
|
|
11
11
|
|
12
12
|
|
13
13
|
## Getting started
|
14
|
-
1. Add the gem to your Ruby on Rails application `Gemfile`:
|
15
14
|
|
16
|
-
|
15
|
+
### Add the gem to your Ruby on Rails application `Gemfile`:
|
16
|
+
|
17
|
+
gem 'post_json'
|
17
18
|
|
18
|
-
|
19
|
+
### At the command prompt, install the gem, run the generator, and migrate the db:
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
bundle install
|
22
|
+
rails g post_json:install
|
23
|
+
rake db:migrate
|
23
24
|
|
24
25
|
That's it!
|
25
26
|
|
@@ -34,99 +35,114 @@ PostJson is all about collections. All models represent a collection.
|
|
34
35
|
|
35
36
|
Also, __notice you don't have to define model attributes anywhere!__
|
36
37
|
|
37
|
-
|
38
|
+
### Lets create your first model.
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
```ruby
|
41
|
+
class Person < PostJson::Collection["people"]
|
42
|
+
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
`Person` can do the same as any model class inheriting `ActiveRecord::Base`.
|
44
|
+
me = Person.create(name: "Jacob")
|
45
|
+
```
|
46
|
+
|
47
|
+
As you can see it look the same as ActiveRecord, except you define `PostJson::Collection["people"]` instead of
|
48
|
+
`ActiveRecord::Base`.
|
48
49
|
|
49
|
-
|
50
|
+
`Person` can do the same as any model class inheriting `ActiveRecord::Base`.
|
50
51
|
|
51
|
-
|
52
|
-
|
52
|
+
You can also skip the creation of a class:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
people = PostJson::Collection["people"]
|
56
|
+
me = people.create(name: "Jacob")
|
57
|
+
```
|
53
58
|
|
54
|
-
|
59
|
+
### Adding some validation:
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
61
|
+
```ruby
|
62
|
+
class Person < PostJson::Collection["people"]
|
63
|
+
validates :name, presence: true
|
64
|
+
end
|
65
|
+
```
|
59
66
|
|
60
|
-
|
61
|
-
|
67
|
+
PostJson::Collection["people"] returns a class, which is based on `PostJson::Base`, which is based on
|
68
|
+
`ActiveRecord::Base`. So its the exact same validation as you may know.
|
62
69
|
|
63
|
-
|
64
|
-
if you need more information.
|
70
|
+
Read the <a href="http://guides.rubyonrails.org/active_record_validations.html" target="_blank">Rails guide about validation</a> if you need more information.
|
65
71
|
|
66
|
-
|
72
|
+
### Lets create a more complex document and do a query:
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
also_me_2 = Person.where("details.age" => 33).first
|
74
|
-
also_me_3 = Person.where("function(doc) { return doc.details.age == 33; }").first
|
75
|
-
also_me_4 = Person.where("json_details.age = ?", 33).first
|
76
|
-
|
77
|
-
PostJson support filtering on nested attributes as you can see. The two first queries speak for themself.
|
78
|
-
|
79
|
-
The third query is special and show it is possible to use a pure JavaScript function for selecting documents.
|
74
|
+
```ruby
|
75
|
+
me = Person.create(name: "Jacob", details: {age: 33})
|
76
|
+
```
|
77
|
+
|
78
|
+
Now we can make a query and get the document:
|
80
79
|
|
81
|
-
|
82
|
-
|
80
|
+
```ruby
|
81
|
+
# PostJson supports filtering on nested attributes
|
82
|
+
also_me_1 = Person.where(details: {age: 33}).first
|
83
|
+
also_me_2 = Person.where("details.age" => 33).first
|
83
84
|
|
84
|
-
|
85
|
+
# It is possible to use a pure JavaScript function for selecting documents
|
86
|
+
also_me_3 = Person.where("function(doc) { return doc.details.age == 33; }").first
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
puts person.name_changed? # false
|
90
|
-
puts person.name_change # nil
|
88
|
+
# It is also possible to write real SQL queries. Just prefix the JSON attributes with `json_`
|
89
|
+
also_me_4 = Person.where("json_details.age = ?", 33).first
|
90
|
+
```
|
91
91
|
|
92
|
-
|
92
|
+
### Accessing attributes:
|
93
|
+
|
94
|
+
Like you would expect with ActiveRecord:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
person = Person.create(name: "Jacob")
|
98
|
+
puts person.name # => "Jacob"
|
99
|
+
puts person.name_was # => "Jacob"
|
100
|
+
puts person.name_changed? # => false
|
101
|
+
puts person.name_change # => nil
|
102
|
+
|
103
|
+
person.name = "Martin"
|
93
104
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
105
|
+
puts person.name_was # => "Jacob"
|
106
|
+
puts person.name # => "Martin"
|
107
|
+
puts person.name_changed? # => true
|
108
|
+
puts person.name_change # => ["Jacob", "Martin"]
|
98
109
|
|
99
|
-
|
110
|
+
person.save
|
100
111
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
112
|
+
puts person.name # => "Martin"
|
113
|
+
puts person.name_was # => "Martin"
|
114
|
+
puts person.name_changed? # => false
|
115
|
+
puts person.name_change # => nil
|
116
|
+
```
|
117
|
+
|
118
|
+
### Introduction to select and selectors.
|
107
119
|
|
108
|
-
|
120
|
+
Sometimes we need a transformed version of documents. This is very easy with `select`
|
109
121
|
|
110
|
-
|
122
|
+
```ruby
|
123
|
+
me = Person.create(name: "Jacob", details: {age: 33})
|
111
124
|
|
112
|
-
|
125
|
+
other_me = Person.limit(1).select({name: "name", age: "details.age"}).first
|
113
126
|
|
114
|
-
|
115
|
-
|
127
|
+
puts other_me
|
128
|
+
# => {name: "Jacob", age: 33}
|
116
129
|
|
117
|
-
|
130
|
+
```
|
131
|
+
`select` takes a hash as argument and return an array of hashes. The value of each key/value pair in the hash argument is a selector. Selectors can point at attributes at root level, but also nested attributes. Each level of attributes is seperated with a dot (.).
|
118
132
|
|
119
|
-
|
133
|
+
### Check out the initializer at `config/initializers/post_json.rb`
|
120
134
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
135
|
+
```ruby
|
136
|
+
PostJson.setup "people" do |collection|
|
137
|
+
collection.record_timestamps = true # default is 'true'
|
138
|
+
collection.created_at_attribute_name = "created_at" # default is 'created_at'
|
139
|
+
collection.updated_at_attribute_name = "updated_at" # default is 'updated_at'
|
140
|
+
collection.include_version_number = true # default is 'true'
|
141
|
+
collection.version_attribute_name = "version" # default is 'version'
|
142
|
+
collection.use_dynamic_index = true # default is 'true'
|
143
|
+
collection.create_dynamic_index_milliseconds_threshold = 50 # default is '50'
|
144
|
+
end
|
145
|
+
```
|
130
146
|
|
131
147
|
#### All of the following methods are supported
|
132
148
|
|
@@ -141,12 +157,14 @@ We also added `page(page, per_page)`, which translate into `offset((page-1)*per_
|
|
141
157
|
|
142
158
|
On a virtual machine running on a 3 year old laptop we created 100.000 documents:
|
143
159
|
|
144
|
-
|
145
|
-
|
146
|
-
|
160
|
+
```ruby
|
161
|
+
test_model = PostJson::Collection["test"]
|
162
|
+
100000.times { test_model.create(content: SecureRandom.uuid) }
|
163
|
+
content = test_model.last.content
|
147
164
|
|
148
|
-
|
149
|
-
|
165
|
+
result = test_model.where(content: content).count
|
166
|
+
# Rails debug tells me the duration was 975.5ms
|
167
|
+
```
|
150
168
|
|
151
169
|
The duration was above 50ms as you can see.
|
152
170
|
|
@@ -154,8 +172,10 @@ PostJson has a feature called "Dynamic Index". It is enabled by default and work
|
|
154
172
|
|
155
173
|
Now lets see how the performance will be on the second and future queries using 'content':
|
156
174
|
|
157
|
-
|
158
|
-
|
175
|
+
```ruby
|
176
|
+
result = test_model.where(content: content).count
|
177
|
+
# Rails debug tells me the duration was 1.5ms
|
178
|
+
```
|
159
179
|
|
160
180
|
It shows PostgreSQL as a document database combined with indexing has great performance out of the box.
|
161
181
|
|
@@ -183,16 +203,17 @@ you execute a query with `name` the performance will be much improved.
|
|
183
203
|
|
184
204
|
You can adjust the settings:
|
185
205
|
|
186
|
-
|
187
|
-
|
188
|
-
|
206
|
+
```ruby
|
207
|
+
class Person < PostJson::Collection["people"]
|
208
|
+
self.create_dynamic_index_milliseconds_threshold = 75
|
209
|
+
end
|
189
210
|
|
190
|
-
|
211
|
+
# Or you can do:
|
191
212
|
|
192
|
-
|
193
|
-
|
194
|
-
# Now indexes are only created if queries are slower than 75 milliseconds.
|
213
|
+
PostJson::Collection["people"].create_dynamic_index_milliseconds_threshold = 75
|
195
214
|
|
215
|
+
# Now indexes are only created if queries are slower than 75 milliseconds.
|
216
|
+
```
|
196
217
|
|
197
218
|
You might already know this about User Interfaces, but it is usual considered good practice if auto-complete responses are served to the user within 100 milliseconds. Other results are usual okay within 500 milliseconds. So leave room for application processing and network delay.
|
198
219
|
|
@@ -202,23 +223,32 @@ Do not set create_dynamic_index_milliseconds_threshold too low as PostJson will
|
|
202
223
|
|
203
224
|
PostJson assign UUID as primary key (id):
|
204
225
|
|
205
|
-
|
206
|
-
|
207
|
-
|
226
|
+
```ruby
|
227
|
+
me = Person.create(name: "Jacob")
|
228
|
+
|
229
|
+
puts me.id
|
230
|
+
# => "297a2500-a456-459b-b3e9-e876f59602c2"
|
231
|
+
```
|
208
232
|
|
209
233
|
But you also set the primary key yourself:
|
210
234
|
|
211
|
-
|
235
|
+
```ruby
|
236
|
+
john_doe = Person.create(id: "John Doe")
|
237
|
+
```
|
212
238
|
|
213
239
|
Notice the primary key is downcased when doing a query or finding records:
|
214
240
|
|
215
|
-
|
216
|
-
|
217
|
-
|
241
|
+
```ruby
|
242
|
+
found = Person.where(id: "JOhN DoE").first
|
243
|
+
|
244
|
+
puts found.attributes
|
245
|
+
# => {"id"=>"John Doe", "version"=>1, "created_at"=>"2013-10-22T10:42:26.190Z", "updated_at"=>"2013-10-22T10:42:26.190Z"}
|
218
246
|
|
219
|
-
|
220
|
-
|
221
|
-
|
247
|
+
found_again = Person.find("JOhN DoE")
|
248
|
+
|
249
|
+
puts found_again.attributes
|
250
|
+
# => {"id"=>"John Doe", "version"=>1, "created_at"=>"2013-10-22T10:42:26.190Z", "updated_at"=>"2013-10-22T10:42:26.190Z"}
|
251
|
+
```
|
222
252
|
|
223
253
|
## The future
|
224
254
|
|
data/lib/post_json/base.rb
CHANGED
@@ -12,11 +12,32 @@ module PostJson
|
|
12
12
|
include Copyable
|
13
13
|
|
14
14
|
def initialize(*args)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
if args[0]
|
16
|
+
__local__doc__body = HashWithIndifferentAccess.new
|
17
|
+
|
18
|
+
args[0] = args[0].with_indifferent_access.inject(HashWithIndifferentAccess.new('__doc__body' => {})) do |result, (attribute_name, value)|
|
19
|
+
if self.class.primary_key == attribute_name
|
20
|
+
result[attribute_name] = value
|
21
|
+
result['__doc__body'][attribute_name] = value
|
22
|
+
elsif self.class.column_names.include?(attribute_name)
|
23
|
+
result[attribute_name] = value
|
24
|
+
else
|
25
|
+
__local__doc__body[attribute_name] = value
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
super(*args) do |new_record|
|
31
|
+
__local__doc__body.each do |attribute_name, value|
|
32
|
+
new_record.public_send("#{attribute_name}=", value)
|
33
|
+
end
|
34
|
+
|
35
|
+
yield new_record if block_given?
|
36
|
+
end
|
37
|
+
else
|
38
|
+
args[0] = HashWithIndifferentAccess.new('__doc__body' => {})
|
39
|
+
super
|
40
|
+
end
|
20
41
|
end
|
21
42
|
|
22
43
|
def cache_key
|
@@ -26,7 +47,11 @@ module PostJson
|
|
26
47
|
end
|
27
48
|
|
28
49
|
def attributes
|
29
|
-
|
50
|
+
if @new_record != nil
|
51
|
+
(read_attribute('__doc__body') || {}).with_indifferent_access
|
52
|
+
else
|
53
|
+
HashWithIndifferentAccess.new
|
54
|
+
end
|
30
55
|
end
|
31
56
|
|
32
57
|
def to_h
|
@@ -108,6 +133,8 @@ module PostJson
|
|
108
133
|
self.__doc__body_write_attribute(attribute_name, value)
|
109
134
|
end
|
110
135
|
|
136
|
+
alias_method :super_respond_to?, :respond_to?
|
137
|
+
|
111
138
|
def respond_to?(method_symbol, include_all = false)
|
112
139
|
if super
|
113
140
|
true
|
@@ -121,6 +148,8 @@ module PostJson
|
|
121
148
|
method_name[0..-2]
|
122
149
|
elsif method_name.end_with?("_change")
|
123
150
|
method_name[0..-8]
|
151
|
+
elsif method_name.end_with?("_will_change!")
|
152
|
+
method_name[0..-14]
|
124
153
|
else
|
125
154
|
method_name
|
126
155
|
end
|
@@ -138,6 +167,8 @@ module PostJson
|
|
138
167
|
method_name[0..-2]
|
139
168
|
elsif method_name.end_with?("_change")
|
140
169
|
method_name[0..-8]
|
170
|
+
elsif method_name.end_with?("_will_change!")
|
171
|
+
method_name[0..-14]
|
141
172
|
else
|
142
173
|
method_name
|
143
174
|
end
|
@@ -174,10 +205,7 @@ module PostJson
|
|
174
205
|
if @collection_name == nil
|
175
206
|
@collection_name = superclass.collection_name rescue nil
|
176
207
|
end
|
177
|
-
message = "You need to assign a collection name to
|
178
|
-
class #{name}
|
179
|
-
self.collection_name = #{name.underscore.pluralize}
|
180
|
-
end"
|
208
|
+
message = "You need to assign a collection name to \"#{name || 'Class'}.collection_name\""
|
181
209
|
raise ArgumentError, message unless @collection_name.present?
|
182
210
|
@collection_name
|
183
211
|
end
|
@@ -219,6 +247,10 @@ end"
|
|
219
247
|
def #{attribute_name}_change
|
220
248
|
__doc__body_attribute_change('#{attribute_name}')
|
221
249
|
end
|
250
|
+
|
251
|
+
def #{attribute_name}_will_change!
|
252
|
+
(__doc__body_will_change! || {})['#{attribute_name}']
|
253
|
+
end
|
222
254
|
RUBY
|
223
255
|
end
|
224
256
|
end
|
data/lib/post_json/version.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Base model as superclass" do
|
4
|
+
class ChildModel < PostJson::Collection["children"]
|
5
|
+
def name=(value)
|
6
|
+
super(value.to_s.upcase)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { ChildModel }
|
11
|
+
|
12
|
+
its(:collection_name) { should == "children" }
|
13
|
+
|
14
|
+
context "document" do
|
15
|
+
let(:name) { "jacob" }
|
16
|
+
subject { ChildModel.create(name: name) }
|
17
|
+
its(:name) { should == name.upcase }
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: post_json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Madsen and Martin Thoegersen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -80,8 +80,8 @@ dependencies:
|
|
80
80
|
- - ~>
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '2.0'
|
83
|
-
description: Fast and
|
84
|
-
with PLV8
|
83
|
+
description: A Fast and Flexible Document Database by Combining Features of Ruby and
|
84
|
+
PostgreSQL with PLV8
|
85
85
|
email:
|
86
86
|
- hello@webnuts.com
|
87
87
|
executables: []
|
@@ -112,6 +112,7 @@ files:
|
|
112
112
|
- lib/post_json/base.rb
|
113
113
|
- spec/models/base_spec.rb
|
114
114
|
- spec/models/queries_spec.rb
|
115
|
+
- spec/models/derived_base_spec.rb
|
115
116
|
- spec/models/collection_spec.rb
|
116
117
|
- spec/spec_helper.rb
|
117
118
|
- spec/dummy/app/helpers/application_helper.rb
|