post_json 1.0.10 → 1.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|