kashmir 0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +39 -33
- data/kashmir.gemspec +1 -4
- data/lib/kashmir.rb +1 -0
- data/lib/kashmir/caching.rb +9 -11
- data/lib/kashmir/patches/active_record.rb +4 -2
- data/lib/kashmir/plugins/ar_relation.rb +5 -1
- data/lib/kashmir/version.rb +1 -1
- data/test/caching_test.rb +6 -0
- metadata +6 -35
- data/Gemfile.lock +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e10560235dc4ce72083facd7ea74c69b3bedb255
|
4
|
+
data.tar.gz: b418df0a0a818e6f00683e5c0115b2c619c0134d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a63bfb22aad18c97ed7a477a35a752fbe82c8d90bb09caf1d9b3651312dae3fe9430e2cc7770423454a98776475279fa12b031406721f2c3dac7461c66e08847
|
7
|
+
data.tar.gz: c4723de817322bea9e4ab0069424a3d8144f3c2ec6bb314ea8c6d62688b143b4483b10bcad8dfe591e36c9f078b19bb636afd10da9c171bf3d6c3d54c37e8708
|
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
@@ -1,18 +1,20 @@
|
|
1
|
-
|
1
|
+
[![Open Source at IFTTT](http://ifttt.github.io/images/open-source-ifttt.svg)](http://ifttt.github.io)
|
2
2
|
|
3
|
-
Kashmir
|
4
|
-
Kashmir will turn these ruby objects into `Hash`es that represent the dependency tree you just described.
|
3
|
+
![Kashmir](https://raw.githubusercontent.com/IFTTT/kashmir/images/images/kashmirbanner.jpg?token=AAIf5wn0aFvxx1oNOO6GVw7SO4vENFW4ks5VuSaLwA%3D%3D "Kashmir")
|
5
4
|
|
6
|
-
|
5
|
+
Kashmir is a DSL built to allow developers to describe representations of Ruby objects.
|
6
|
+
Kashmir will turn these Ruby objects into hashes that represent the dependency tree you just described.
|
7
7
|
|
8
|
-
`Kashmir::
|
9
|
-
|
8
|
+
`Kashmir::ActiveRecord` will also optimize and try to balance `ActiveRecord` queries so your application hits the database as little as possible.
|
9
|
+
|
10
|
+
`Kashmir::Caching` builds a dependency tree for complex object representations and caches each level of this tree separately. Kashmir will do so by creating cache views of each level as well as caching a complete tree.
|
11
|
+
The caching engine is smart enough to fill holes in the cache tree with fresh data from your data store.
|
10
12
|
|
11
13
|
Combine `Kashmir::Caching` + `Kashmir::ActiveRecord` for extra awesomeness.
|
12
14
|
|
13
15
|
### Example:
|
14
16
|
|
15
|
-
For example, a `Person` with
|
17
|
+
For example, a `Person` with `name` and `age` attributes:
|
16
18
|
```ruby
|
17
19
|
class Person
|
18
20
|
include Kashmir
|
@@ -34,18 +36,21 @@ could be represented as:
|
|
34
36
|
```
|
35
37
|
|
36
38
|
Representing an object is as simple as:
|
37
|
-
|
38
|
-
|
39
|
+
|
40
|
+
1. Add `include Kashmir` to the target class.
|
41
|
+
2. Whitelist all the fields you want to include in a representation.
|
42
|
+
|
39
43
|
```ruby
|
40
|
-
#
|
44
|
+
# Add fields and methods you want to be visible to Kashmir
|
41
45
|
representations do
|
42
|
-
rep(:name)
|
46
|
+
rep(:name)
|
43
47
|
rep(:age)
|
44
48
|
end
|
45
49
|
```
|
50
|
+
|
46
51
|
3. Instantiate an object and `#represent` it.
|
47
52
|
```ruby
|
48
|
-
#
|
53
|
+
# Pass in an array with all the fields you want included
|
49
54
|
Person.new('Netto Farah', 26).represent([:name, :age])
|
50
55
|
=> {:name=>"Netto Farah", :age=>"26"}
|
51
56
|
```
|
@@ -65,11 +70,11 @@ And then execute:
|
|
65
70
|
## Usage
|
66
71
|
Kashmir is better described with examples.
|
67
72
|
|
68
|
-
### Basic
|
73
|
+
### Basic Representations
|
69
74
|
|
70
|
-
#### Describing an
|
75
|
+
#### Describing an Object
|
71
76
|
Only whitelisted fields can be represented by Kashmir.
|
72
|
-
This is done so
|
77
|
+
This is done so sensitive fields (like passwords) cannot be accidentally exposed to clients.
|
73
78
|
|
74
79
|
``` ruby
|
75
80
|
class Recipe < OpenStruct
|
@@ -84,17 +89,17 @@ end
|
|
84
89
|
|
85
90
|
Instantiate a `Recipe`:
|
86
91
|
```ruby
|
87
|
-
recipe = Recipe.new(title: 'Beef
|
92
|
+
recipe = Recipe.new(title: 'Beef Stew', preparation_time: 60)
|
88
93
|
```
|
89
94
|
|
90
|
-
Kashmir automatically adds a `#represent` method to every instance of `Recipe
|
91
|
-
`#represent`
|
95
|
+
Kashmir automatically adds a `#represent` method to every instance of `Recipe`.
|
96
|
+
`#represent` takes an `Array` with all the fields you want as part of your representation.
|
92
97
|
|
93
98
|
```ruby
|
94
99
|
recipe.represent([:title, :preparation_time])
|
95
|
-
=> { title: 'Beef
|
100
|
+
=> { title: 'Beef Stew', preparation_time: 60 }
|
96
101
|
```
|
97
|
-
#### Calculated
|
102
|
+
#### Calculated Fields
|
98
103
|
You can represent any instance variable or method (basically anything that returns `true` for `respond_to?`).
|
99
104
|
``` ruby
|
100
105
|
class Recipe < OpenStruct
|
@@ -112,11 +117,11 @@ end
|
|
112
117
|
```
|
113
118
|
|
114
119
|
```ruby
|
115
|
-
Recipe.new(title: 'Beef
|
116
|
-
=> { title: 'Beef
|
120
|
+
Recipe.new(title: 'Beef Stew', steps: ['chop', 'cook']).represent([:title, :num_steps])
|
121
|
+
=> { title: 'Beef Stew', num_steps: 2 }
|
117
122
|
```
|
118
123
|
|
119
|
-
### Nested
|
124
|
+
### Nested Representations
|
120
125
|
You can nest Kashmir objects to represent complex relationships between your objects.
|
121
126
|
```ruby
|
122
127
|
class Recipe < OpenStruct
|
@@ -137,8 +142,7 @@ class Chef < OpenStruct
|
|
137
142
|
end
|
138
143
|
```
|
139
144
|
|
140
|
-
|
141
|
-
You can then pass in another array with all the properties you want to present in the nested object.
|
145
|
+
When you create a representation, nest hashes to create nested representations.
|
142
146
|
```ruby
|
143
147
|
netto = Chef.new(name: 'Netto Farah')
|
144
148
|
beef_stew = Recipe.new(title: 'Beef Stew', chef: netto)
|
@@ -153,8 +157,8 @@ beef_stew.represent([:title, { :chef => [ :name ] }])
|
|
153
157
|
```
|
154
158
|
Not happy with this syntax? Check out `Kashmir::DSL` or `Kashmir::InlineDSL` for prettier code.
|
155
159
|
|
156
|
-
#### Base
|
157
|
-
|
160
|
+
#### Base Representations
|
161
|
+
Are you tired of repeating the same fields over and over?
|
158
162
|
You can create a base representation of your objects, so Kashmir returns basic fields automatically.
|
159
163
|
```ruby
|
160
164
|
class Recipe
|
@@ -207,7 +211,7 @@ end
|
|
207
211
|
```
|
208
212
|
|
209
213
|
```ruby
|
210
|
-
bbq_joint = Restaurant.new(name: "Netto's BBQ Joint", rating: '5
|
214
|
+
bbq_joint = Restaurant.new(name: "Netto's BBQ Joint", rating: '5 Stars')
|
211
215
|
netto = Chef.new(name: 'Netto', restaurant: bbq_joint)
|
212
216
|
brisket = Recipe.new(title: 'BBQ Brisket', chef: netto)
|
213
217
|
|
@@ -223,7 +227,7 @@ brisket.represent([
|
|
223
227
|
name: 'Netto',
|
224
228
|
restaurant: {
|
225
229
|
name: "Netto's BBQ Joint",
|
226
|
-
rating: '5
|
230
|
+
rating: '5 Stars'
|
227
231
|
}
|
228
232
|
}
|
229
233
|
}
|
@@ -310,7 +314,7 @@ brisket.represent(RecipePresenter)
|
|
310
314
|
|
311
315
|
=> { title: 'BBQ Brisket', num_steps: 2 }
|
312
316
|
```
|
313
|
-
####
|
317
|
+
#### Embedded Representers
|
314
318
|
It is also possible to define nested representers with `embed(:property_name, RepresenterClass)`.
|
315
319
|
|
316
320
|
```ruby
|
@@ -388,7 +392,7 @@ end
|
|
388
392
|
=> { title: 'BBQ Brisket', num_steps: 2 }
|
389
393
|
```
|
390
394
|
|
391
|
-
#### Nested
|
395
|
+
#### Nested Inline Representations
|
392
396
|
You can nest inline representations using `inline(:field, &block)` the same way we did with `Kashmir::Dsl`.
|
393
397
|
|
394
398
|
```ruby
|
@@ -512,7 +516,7 @@ For more examples, check out: https://github.com/IFTTT/kashmir/blob/master/test/
|
|
512
516
|
Caching is the best feature in Kashmir.
|
513
517
|
The `Kashmir::Caching` module will cache every level of the dependency tree Kashmir generates when representing an object.
|
514
518
|
|
515
|
-
![Dependency Tree](
|
519
|
+
![Dependency Tree](https://raw.githubusercontent.com/IFTTT/kashmir/images/images/kashmir.png?token=AAIf57rtAVfFPENYmWfBJ9nhZOmbFs1qks5VuVFOwA%3D%3D "Dependency Tree")
|
516
520
|
|
517
521
|
As you can see in the image above, Kashmir will build a dependency tree of the representation.
|
518
522
|
If you have Caching on, Kashmir will:
|
@@ -527,7 +531,7 @@ Kashmir will also be able to fill in blanks in the dependency tree and fetch mis
|
|
527
531
|
Caching is turned off by default, but you can use one of the two available implementations.
|
528
532
|
|
529
533
|
- [In Memory Caching] https://github.com/IFTTT/kashmir/blob/master/lib/kashmir/plugins/memory_caching.rb
|
530
|
-
- [Memcached] https://github.com/IFTTT/kashmir/blob/master/lib/kashmir/plugins/memcached_caching.rb
|
534
|
+
- [Memcached] https://github.com/IFTTT/kashmir/blob/master/lib/kashmir/plugins/memcached_caching.rb
|
531
535
|
|
532
536
|
You can also build your own custom caching engine by following the `NullCaching` protocol available at:
|
533
537
|
https://github.com/IFTTT/kashmir/blob/master/lib/kashmir/plugins/null_caching.rb
|
@@ -542,6 +546,8 @@ Kashmir.init(
|
|
542
546
|
|
543
547
|
##### With Memcached
|
544
548
|
```ruby
|
549
|
+
require 'kashmir/plugins/memcached_caching'
|
550
|
+
|
545
551
|
client = Dalli::Client.new(url, namespace: 'kashmir', compress: true)
|
546
552
|
default_ttl = 5.minutes
|
547
553
|
|
data/kashmir.gemspec
CHANGED
@@ -21,8 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_runtime_dependency "colorize", "~> 0.7"
|
25
|
-
|
26
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
27
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
26
|
spec.add_development_dependency "minitest", "~> 5.0"
|
@@ -30,6 +28,5 @@ Gem::Specification.new do |spec|
|
|
30
28
|
spec.add_development_dependency "mocha", "~> 1.1"
|
31
29
|
spec.add_development_dependency "sqlite3", "1.3.10"
|
32
30
|
|
33
|
-
spec.add_development_dependency "
|
34
|
-
spec.add_development_dependency "activerecord", "3.2.8"
|
31
|
+
spec.add_development_dependency "activerecord", "~> 4.2"
|
35
32
|
end
|
data/lib/kashmir.rb
CHANGED
data/lib/kashmir/caching.rb
CHANGED
@@ -1,39 +1,37 @@
|
|
1
1
|
require 'kashmir/plugins/memory_caching'
|
2
2
|
require 'kashmir/plugins/null_caching'
|
3
|
-
require 'kashmir/plugins/memcached_caching'
|
4
|
-
require 'colorize'
|
5
3
|
|
6
4
|
module Kashmir
|
7
5
|
module Caching
|
8
6
|
|
9
7
|
def from_cache(representation_definition, object)
|
10
|
-
log("
|
8
|
+
log("read: #{log_key(object, representation_definition)}", :debug)
|
11
9
|
|
12
10
|
cached_representation = Kashmir.caching.from_cache(representation_definition, object)
|
13
11
|
|
14
12
|
if cached_representation
|
15
|
-
log("
|
13
|
+
log("hit: #{log_key(object, representation_definition)}")
|
16
14
|
else
|
17
|
-
log("
|
15
|
+
log("miss: #{log_key(object, representation_definition)}")
|
18
16
|
end
|
19
17
|
|
20
18
|
cached_representation
|
21
19
|
end
|
22
20
|
|
23
21
|
def bulk_from_cache(representation_definition, objects)
|
24
|
-
class_name = objects.
|
25
|
-
log("
|
22
|
+
class_name = objects.length > 0 ? objects.first.class.to_s : ''
|
23
|
+
log("read_multi: [#{objects.length}]#{class_name} : #{representation_definition}", :debug)
|
26
24
|
Kashmir.caching.bulk_from_cache(representation_definition, objects)
|
27
25
|
end
|
28
26
|
|
29
27
|
def store_presenter(representation_definition, representation, object, ttl)
|
30
|
-
log("
|
28
|
+
log("write TTL: #{ttl}: #{log_key(object, representation_definition)}", :debug)
|
31
29
|
Kashmir.caching.store_presenter(representation_definition, representation, object, ttl)
|
32
30
|
end
|
33
31
|
|
34
32
|
def bulk_write(representation_definition, representations, objects, ttl)
|
35
|
-
class_name = objects.
|
36
|
-
log("
|
33
|
+
class_name = objects.length > 0 ? objects.first.class.to_s : ''
|
34
|
+
log("write_multi: TTL: #{ttl}: [#{objects.length}]#{class_name} : #{representation_definition}", :debug)
|
37
35
|
Kashmir.caching.bulk_write(representation_definition, representations, objects, ttl)
|
38
36
|
end
|
39
37
|
|
@@ -42,7 +40,7 @@ module Kashmir
|
|
42
40
|
end
|
43
41
|
|
44
42
|
def log(message, level=:info)
|
45
|
-
Kashmir.logger.send(level, ("\
|
43
|
+
Kashmir.logger.send(level, ("\nKashmir::Caching #{message}\n"))
|
46
44
|
end
|
47
45
|
|
48
46
|
module_function :from_cache, :bulk_from_cache, :bulk_write, :store_presenter, :log_key, :log
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# We have to reopen Preloader to allow for it
|
2
2
|
# to accept any random attribute name as a preloadable association.
|
3
3
|
#
|
4
|
-
# This allows us to send any
|
5
|
-
#
|
4
|
+
# This allows us to send any arbitrary Hash to Preloader, without
|
5
|
+
# requiring it to be an ActiveRecord relation in advance.
|
6
6
|
#
|
7
7
|
|
8
8
|
module ArV4Patch
|
@@ -26,6 +26,8 @@ module ArV4Patch
|
|
26
26
|
klasses = h[assoc.reflection] ||= {}
|
27
27
|
(klasses[assoc.klass] ||= []) << record
|
28
28
|
end
|
29
|
+
|
30
|
+
h.delete(nil)
|
29
31
|
h
|
30
32
|
end
|
31
33
|
end
|
@@ -12,7 +12,11 @@ module Kashmir
|
|
12
12
|
end
|
13
13
|
|
14
14
|
if to_load.any?
|
15
|
-
ActiveRecord::
|
15
|
+
if ActiveRecord::VERSION::STRING >= "4.0.2"
|
16
|
+
ActiveRecord::Associations::Preloader.new.preload(to_load, representation_definition)
|
17
|
+
else
|
18
|
+
ActiveRecord::Associations::Preloader.new(to_load, representation_definition).run
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
to_load_representations = to_load.map do |subject|
|
data/lib/kashmir/version.rb
CHANGED
data/test/caching_test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kashmir
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- IFTTT
|
@@ -9,22 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: colorize
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - "~>"
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '0.7'
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - "~>"
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0.7'
|
28
14
|
- !ruby/object:Gem::Dependency
|
29
15
|
name: bundler
|
30
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -110,33 +96,19 @@ dependencies:
|
|
110
96
|
- !ruby/object:Gem::Version
|
111
97
|
version: 1.3.10
|
112
98
|
- !ruby/object:Gem::Dependency
|
113
|
-
name:
|
99
|
+
name: activerecord
|
114
100
|
requirement: !ruby/object:Gem::Requirement
|
115
101
|
requirements:
|
116
102
|
- - "~>"
|
117
103
|
- !ruby/object:Gem::Version
|
118
|
-
version: '2
|
104
|
+
version: '4.2'
|
119
105
|
type: :development
|
120
106
|
prerelease: false
|
121
107
|
version_requirements: !ruby/object:Gem::Requirement
|
122
108
|
requirements:
|
123
109
|
- - "~>"
|
124
110
|
- !ruby/object:Gem::Version
|
125
|
-
version: '2
|
126
|
-
- !ruby/object:Gem::Dependency
|
127
|
-
name: activerecord
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
-
requirements:
|
130
|
-
- - '='
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: 3.2.8
|
133
|
-
type: :development
|
134
|
-
prerelease: false
|
135
|
-
version_requirements: !ruby/object:Gem::Requirement
|
136
|
-
requirements:
|
137
|
-
- - '='
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
version: 3.2.8
|
111
|
+
version: '4.2'
|
140
112
|
description: "\n Kashmir helps you easily define decorators/representers/presenters
|
141
113
|
for ruby objects.\n Optionally, Kashmir will also cache these views for faster
|
142
114
|
lookups.\n "
|
@@ -148,8 +120,7 @@ extensions: []
|
|
148
120
|
extra_rdoc_files: []
|
149
121
|
files:
|
150
122
|
- Gemfile
|
151
|
-
-
|
152
|
-
- LICENSE.txt
|
123
|
+
- LICENSE
|
153
124
|
- README.md
|
154
125
|
- Rakefile
|
155
126
|
- kashmir.gemspec
|
data/Gemfile.lock
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
kashmir (0.0.1)
|
5
|
-
colorize (~> 0.7)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (3.2.22)
|
11
|
-
activesupport (= 3.2.22)
|
12
|
-
builder (~> 3.0.0)
|
13
|
-
activerecord (3.2.22)
|
14
|
-
activemodel (= 3.2.22)
|
15
|
-
activesupport (= 3.2.22)
|
16
|
-
arel (~> 3.0.2)
|
17
|
-
tzinfo (~> 0.3.29)
|
18
|
-
activesupport (3.2.22)
|
19
|
-
i18n (~> 0.6, >= 0.6.4)
|
20
|
-
multi_json (~> 1.0)
|
21
|
-
arel (3.0.3)
|
22
|
-
builder (3.0.4)
|
23
|
-
colorize (0.7.7)
|
24
|
-
dalli (2.7.4)
|
25
|
-
i18n (0.7.0)
|
26
|
-
metaclass (0.0.4)
|
27
|
-
minitest (5.5.1)
|
28
|
-
minitest-around (0.3.2)
|
29
|
-
minitest (~> 5.0)
|
30
|
-
mocha (1.1.0)
|
31
|
-
metaclass (~> 0.0.1)
|
32
|
-
multi_json (1.11.1)
|
33
|
-
rake (10.4.2)
|
34
|
-
sqlite3 (1.3.10)
|
35
|
-
tzinfo (0.3.44)
|
36
|
-
|
37
|
-
PLATFORMS
|
38
|
-
ruby
|
39
|
-
|
40
|
-
DEPENDENCIES
|
41
|
-
activerecord (~> 3.2.8)
|
42
|
-
bundler (~> 1.7)
|
43
|
-
dalli (~> 2.7)
|
44
|
-
kashmir!
|
45
|
-
minitest (~> 5.0)
|
46
|
-
minitest-around (~> 0.3)
|
47
|
-
mocha (~> 1.1.0)
|
48
|
-
rake (~> 10.0)
|
49
|
-
sqlite3 (= 1.3.10)
|