mongoid-cached-json 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +51 -6
- data/lib/mongoid-cached-json/cached_json.rb +24 -9
- data/lib/mongoid-cached-json/config.rb +30 -3
- data/lib/mongoid-cached-json/version.rb +1 -1
- metadata +19 -19
data/README.md
CHANGED
@@ -3,7 +3,7 @@ Mongoid::CachedJson [![Build Status](https://secure.travis-ci.org/dblock/mongoid
|
|
3
3
|
|
4
4
|
Typical *as_json* definitions may involve lots of database point queries and method calls. When returning collections of objects, a single call may yield hundreds of database queries that can take seconds. This library mitigates the problem by implementing a module called *CachedJson*.
|
5
5
|
|
6
|
-
CachedJson enables returning multiple JSON formats from a single class and provides some rules for returning embedded or referenced data. It then uses a scheme where fragments of JSON are cached for a particular (class, id) pair containing only the data that doesn't involve references/embedded documents. To get the full JSON for an instance, CachedJson will combine fragments of JSON from the instance with fragments representing the JSON for its references. In the best case, when all of these fragments are cached, this falls through to a few cache lookups followed by a couple Ruby hash merges to create the JSON.
|
6
|
+
CachedJson enables returning multiple JSON formats and versions from a single class and provides some rules for returning embedded or referenced data. It then uses a scheme where fragments of JSON are cached for a particular (class, id) pair containing only the data that doesn't involve references/embedded documents. To get the full JSON for an instance, CachedJson will combine fragments of JSON from the instance with fragments representing the JSON for its references. In the best case, when all of these fragments are cached, this falls through to a few cache lookups followed by a couple Ruby hash merges to create the JSON.
|
7
7
|
|
8
8
|
Using Mongoid::CachedJson we were able to cut our JSON API average response time by about a factor of 10.
|
9
9
|
|
@@ -78,8 +78,16 @@ Mongoid::CachedJson.configure do |config|
|
|
78
78
|
end
|
79
79
|
```
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
The default JSON version returned from `as_json` is `:unspecified`. If you wish to redefine this, set `Mongoid::CachedJson.config.default_version`.
|
82
|
+
|
83
|
+
``` ruby
|
84
|
+
Mongoid::CachedJson.configure do |config|
|
85
|
+
config.default_version = :v2
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
Defining Fields
|
90
|
+
---------------
|
83
91
|
|
84
92
|
Mongoid::CachedJson supports the following options.
|
85
93
|
|
@@ -90,6 +98,42 @@ Mongoid::CachedJson field definitions support the following options.
|
|
90
98
|
* `:definition` can be a symbol or an anonymous function, eg. `:description => { :definition => :name }` or `:description => { :definition => lambda { |instance| instance.name } }`
|
91
99
|
* `:type` can be `:reference`, required for referenced objects
|
92
100
|
* `:properties` can be one of `:short`, `:public`, `:all`, in this order
|
101
|
+
* `:version` can be a single version for this field to appear in
|
102
|
+
* `:versions` can be an array of versions for this field to appear in
|
103
|
+
|
104
|
+
Versioning
|
105
|
+
----------
|
106
|
+
|
107
|
+
You can set an optional `version` or `versions` attribute on JSON fields. Consider the following definition where the first version defined `:name`, then split it into `:first`, `:middle` and `:last` in version `:v2` and introduced a date of birth in `:v3`.
|
108
|
+
|
109
|
+
``` ruby
|
110
|
+
class Person
|
111
|
+
include Mongoid::Document
|
112
|
+
include Mongoid::CachedJson
|
113
|
+
|
114
|
+
field :first
|
115
|
+
field :last
|
116
|
+
|
117
|
+
def name
|
118
|
+
[ first, middle, last ].compact.join(" ")
|
119
|
+
end
|
120
|
+
|
121
|
+
json_fields \
|
122
|
+
:first => { :versions => [ :v2, :v3 ] },
|
123
|
+
:last => { :versions => [ :v2, :v3 ] },
|
124
|
+
:middle => { :versions => [ :v2, :v3 ] },
|
125
|
+
:born => { :versions => :v3 },
|
126
|
+
:name => { :definition => :name }
|
127
|
+
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
``` ruby
|
132
|
+
person = Person.create({ :first => "John", :middle => "F.", :last => "Kennedy", :born => "May 29, 1917" })
|
133
|
+
person.as_json # { :name => "John F. Kennedy" }
|
134
|
+
person.as_json({ :version => :v2 }) # { :first => "John", :middle => "F.", :last => "Kennedy", :name => "John F. Kennedy" }
|
135
|
+
person.as_json({ :version => :v3 }) # { :first => "John", :middle => "F.", :last => "Kennedy", :name => "John F. Kennedy", :born => "May 29, 1917" }
|
136
|
+
```
|
93
137
|
|
94
138
|
Transformations
|
95
139
|
---------------
|
@@ -98,6 +142,7 @@ You can define global transformations on all JSON values with `Mongoid::CachedJs
|
|
98
142
|
|
99
143
|
``` ruby
|
100
144
|
class Widget
|
145
|
+
include Mongoid::Document
|
101
146
|
include Mongoid::CachedJson
|
102
147
|
|
103
148
|
field :name
|
@@ -128,11 +173,11 @@ You can set `Mongoid::CachedJson.config.disable_caching = true`. It may be a goo
|
|
128
173
|
Contributing
|
129
174
|
------------
|
130
175
|
|
131
|
-
Fork the project. Make your feature addition or bug fix with tests. Send a pull request. Bonus points for
|
176
|
+
Fork the project. Make your feature addition or bug fix with tests. Send a pull request. Bonus points for topic branches.
|
132
177
|
|
133
178
|
Copyright and License
|
134
179
|
---------------------
|
135
180
|
|
136
|
-
MIT License, see [LICENSE](LICENSE.md) for details.
|
181
|
+
MIT License, see [LICENSE](https://github.com/dblock/mongoid-cached-json/blob/master/LICENSE.md) for details.
|
137
182
|
|
138
|
-
(c) 2012 [Art.sy Inc.](http://artsy.github.com) and [Contributors](HISTORY.md)
|
183
|
+
(c) 2012 [Art.sy Inc.](http://artsy.github.com) and [Contributors](https://github.com/dblock/mongoid-cached-json/blob/master/HISTORY.md)
|
@@ -5,6 +5,7 @@ module Mongoid
|
|
5
5
|
|
6
6
|
included do
|
7
7
|
class_attribute :all_json_properties
|
8
|
+
class_attribute :all_json_versions
|
8
9
|
class_attribute :cached_json_field_defs
|
9
10
|
class_attribute :cached_json_reference_defs
|
10
11
|
class_attribute :hide_as_child_json_when
|
@@ -16,13 +17,17 @@ module Mongoid
|
|
16
17
|
#
|
17
18
|
# @param [ hash ] defs JSON field definition.
|
18
19
|
#
|
19
|
-
# @since 1.0
|
20
|
+
# @since 1.0
|
20
21
|
def json_fields(defs)
|
21
22
|
self.hide_as_child_json_when = defs.delete(:hide_as_child_json_when) || lambda { |a| false }
|
22
23
|
self.all_json_properties = [:short, :public, :all]
|
23
24
|
cached_json_defs = Hash[defs.map { |k,v| [k, { :type => :callable, :properties => :short, :definition => k }.merge(v)] }]
|
24
25
|
self.cached_json_field_defs = {}
|
25
26
|
self.cached_json_reference_defs = {}
|
27
|
+
# Collect all versions for clearing cache
|
28
|
+
self.all_json_versions = cached_json_defs.map do |field, definition|
|
29
|
+
[ :unspecified, definition[:version], Array(definition[:versions]) ]
|
30
|
+
end.flatten.compact.uniq
|
26
31
|
self.all_json_properties.each_with_index do |property, i|
|
27
32
|
self.cached_json_field_defs[property] = Hash[cached_json_defs.find_all do |field, definition|
|
28
33
|
self.all_json_properties.find_index(definition[:properties]) <= i and definition[:type] == :callable
|
@@ -62,6 +67,9 @@ module Mongoid
|
|
62
67
|
nil
|
63
68
|
else
|
64
69
|
Hash[clazz.cached_json_field_defs[options[:properties]].map do |field, definition|
|
70
|
+
# version match
|
71
|
+
versions = ([definition[:version] ] | Array(definition[:versions])).compact
|
72
|
+
next unless versions.empty? or versions.include?(options[:version])
|
65
73
|
json_value = (definition[:definition].is_a?(Symbol) ? object_reference.send(definition[:definition]) : definition[:definition].call(object_reference))
|
66
74
|
Mongoid::CachedJson.config.transform.each do |t|
|
67
75
|
json_value = t.call(field, definition, json_value)
|
@@ -85,9 +93,9 @@ module Mongoid
|
|
85
93
|
|
86
94
|
# Cache key.
|
87
95
|
def cached_json_key(options, cached_class, cached_id)
|
88
|
-
"as_json/#{cached_class}/#{cached_id}/#{options[:properties]}/#{!!options[:is_top_level_json]}"
|
96
|
+
"as_json/#{options[:version]}/#{cached_class}/#{cached_id}/#{options[:properties]}/#{!!options[:is_top_level_json]}"
|
89
97
|
end
|
90
|
-
|
98
|
+
|
91
99
|
# If the reference is a symbol, we may be lucky and be able to figure out the as_json
|
92
100
|
# representation by the (class, id) pair definition of the reference. That is, we may
|
93
101
|
# be able to load the as_json representation from the cache without even getting the
|
@@ -116,17 +124,24 @@ module Mongoid
|
|
116
124
|
|
117
125
|
end
|
118
126
|
|
119
|
-
def as_json(options = {
|
120
|
-
|
121
|
-
|
122
|
-
|
127
|
+
def as_json(options = {})
|
128
|
+
if options[:properties] and ! self.all_json_properties.member?(options[:properties])
|
129
|
+
raise ArgumentError.new("Unknown properties option: #{options[:properties]}")
|
130
|
+
end
|
131
|
+
self.class.materialize_json({
|
132
|
+
:properties => :short, :is_top_level_json => true, :version => Mongoid::CachedJson.config.default_version
|
133
|
+
}.merge(options), { :object => self })
|
123
134
|
end
|
124
135
|
|
125
136
|
# Expire all JSON entries for this class.
|
126
137
|
def expire_cached_json
|
127
138
|
self.all_json_properties.each do |properties|
|
128
139
|
[true, false].each do |is_top_level_json|
|
129
|
-
|
140
|
+
self.all_json_versions.each do |version|
|
141
|
+
Mongoid::CachedJson.config.cache.delete(self.class.cached_json_key({
|
142
|
+
:properties => properties, :is_top_level_json => is_top_level_json, :version => version
|
143
|
+
}, self.class, self.id))
|
144
|
+
end
|
130
145
|
end
|
131
146
|
end
|
132
147
|
end
|
@@ -148,4 +163,4 @@ module Mongoid
|
|
148
163
|
end
|
149
164
|
|
150
165
|
end
|
151
|
-
end
|
166
|
+
end
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
module Mongoid
|
3
|
-
module CachedJson
|
2
|
+
module Mongoid
|
3
|
+
module CachedJson
|
4
4
|
module Config
|
5
5
|
extend self
|
6
6
|
include ActiveSupport::Callbacks
|
7
7
|
|
8
|
-
|
8
|
+
# Current configuration settings.
|
9
|
+
attr_accessor :settings
|
10
|
+
|
11
|
+
# Default configuration settings.
|
12
|
+
attr_accessor :defaults
|
13
|
+
|
9
14
|
@settings = {}
|
10
15
|
@defaults = {}
|
11
16
|
|
@@ -36,8 +41,30 @@ module Mongoid
|
|
36
41
|
RUBY
|
37
42
|
end
|
38
43
|
|
44
|
+
# Disable caching.
|
39
45
|
option :disable_caching, { :default => false }
|
40
46
|
|
47
|
+
# Returns the default JSON version
|
48
|
+
#
|
49
|
+
# @example Get the default JSON version
|
50
|
+
# config.default_version
|
51
|
+
#
|
52
|
+
# @return [ Version ] The default JSON version.
|
53
|
+
def default_version
|
54
|
+
settings[:default_version] = :unspecified unless settings.has_key?(:default_version)
|
55
|
+
settings[:default_version]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sets the default JSON version.
|
59
|
+
#
|
60
|
+
# @example Set the default version.
|
61
|
+
# config.default_version = :v2
|
62
|
+
#
|
63
|
+
# @return [ Version ] The newly set default version.
|
64
|
+
def default_version=(default_version)
|
65
|
+
settings[:default_version] = default_version
|
66
|
+
end
|
67
|
+
|
41
68
|
# Returns the default cache store, which is either a Rails logger of stdout logger
|
42
69
|
#
|
43
70
|
# @example Get the default cache store
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-cached-json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.1'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,11 +11,11 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-03-13 00:00:00.000000000Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activesupport
|
18
|
-
requirement: &
|
18
|
+
requirement: &82900940 !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ! '>='
|
@@ -23,10 +23,10 @@ dependencies:
|
|
23
23
|
version: '0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements: *
|
26
|
+
version_requirements: *82900940
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mongoid
|
29
|
-
requirement: &
|
29
|
+
requirement: &82886650 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
31
|
requirements:
|
32
32
|
- - ! '>='
|
@@ -34,10 +34,10 @@ dependencies:
|
|
34
34
|
version: '0'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
|
-
version_requirements: *
|
37
|
+
version_requirements: *82886650
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: bson_ext
|
40
|
-
requirement: &
|
40
|
+
requirement: &82886190 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ! '>='
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
version: '0'
|
46
46
|
type: :runtime
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *82886190
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: hpricot
|
51
|
-
requirement: &
|
51
|
+
requirement: &82885630 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ! '>='
|
@@ -56,10 +56,10 @@ dependencies:
|
|
56
56
|
version: '0'
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
|
-
version_requirements: *
|
59
|
+
version_requirements: *82885630
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: rspec
|
62
|
-
requirement: &
|
62
|
+
requirement: &82885050 !ruby/object:Gem::Requirement
|
63
63
|
none: false
|
64
64
|
requirements:
|
65
65
|
- - ~>
|
@@ -67,10 +67,10 @@ dependencies:
|
|
67
67
|
version: '2.5'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
|
-
version_requirements: *
|
70
|
+
version_requirements: *82885050
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: bundler
|
73
|
-
requirement: &
|
73
|
+
requirement: &82884260 !ruby/object:Gem::Requirement
|
74
74
|
none: false
|
75
75
|
requirements:
|
76
76
|
- - ~>
|
@@ -78,10 +78,10 @@ dependencies:
|
|
78
78
|
version: '1.0'
|
79
79
|
type: :development
|
80
80
|
prerelease: false
|
81
|
-
version_requirements: *
|
81
|
+
version_requirements: *82884260
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: jeweler
|
84
|
-
requirement: &
|
84
|
+
requirement: &82883690 !ruby/object:Gem::Requirement
|
85
85
|
none: false
|
86
86
|
requirements:
|
87
87
|
- - ~>
|
@@ -89,10 +89,10 @@ dependencies:
|
|
89
89
|
version: '1.6'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
|
-
version_requirements: *
|
92
|
+
version_requirements: *82883690
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: yard
|
95
|
-
requirement: &
|
95
|
+
requirement: &82883090 !ruby/object:Gem::Requirement
|
96
96
|
none: false
|
97
97
|
requirements:
|
98
98
|
- - ~>
|
@@ -100,7 +100,7 @@ dependencies:
|
|
100
100
|
version: '0.6'
|
101
101
|
type: :development
|
102
102
|
prerelease: false
|
103
|
-
version_requirements: *
|
103
|
+
version_requirements: *82883090
|
104
104
|
description: Cached-json is a DSL for describing JSON representations of Mongoid models.
|
105
105
|
email: dblock@dblock.org
|
106
106
|
executables: []
|
@@ -130,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
130
|
version: '0'
|
131
131
|
segments:
|
132
132
|
- 0
|
133
|
-
hash:
|
133
|
+
hash: -206724813
|
134
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
135
|
none: false
|
136
136
|
requirements:
|