undo 0.1.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +131 -109
- data/VERSION +1 -0
- data/lib/undo.rb +12 -24
- data/lib/undo/config.rb +15 -22
- data/lib/undo/container/json.rb +15 -0
- data/lib/undo/integration/shared_undo_integration_examples.rb +31 -0
- data/lib/undo/keeper.rb +36 -0
- data/lib/undo/storage/adapter.rb +31 -0
- data/lib/undo/storage/memory.rb +6 -4
- data/lib/undo/version.rb +1 -1
- data/spec/spec_helper_lite.rb +1 -8
- data/spec/support/ci_helper.rb +16 -0
- data/spec/undo/config_spec.rb +10 -0
- data/spec/undo/container/json_spec.rb +16 -0
- data/spec/undo/serializer/null_spec.rb +11 -6
- data/spec/undo/storage/memory_spec.rb +17 -4
- data/spec/undo_spec.rb +26 -54
- data/undo.gemspec +6 -10
- metadata +32 -13
- data/lib/undo/wrapper.rb +0 -34
- data/spec/undo/wrapper_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bbec1f206620c902a60106546a48d9b126a31d8
|
4
|
+
data.tar.gz: 1e0f6f864cc9bc39bbf80950e6b9071d4b436814
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e08e8aa4f48fe64d67651e07a6f305ef50f7192fbf8f75b06d26bbaf321fbc53d77b7130523719c7885efe9514627eee6691e82b1cbc7385ba4b2210ffa8f49
|
7
|
+
data.tar.gz: 3dbb5154581a0e24a443e9002d037b43648a542a73f92096018073af42114f16caa80083edb26519106877c51cf35f8c019e27eef55fff182295e2d515a2c0e3
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.1
|
data/README.md
CHANGED
@@ -1,19 +1,25 @@
|
|
1
1
|
Undo
|
2
2
|
==========
|
3
3
|
[![Build Status](https://travis-ci.org/AlexParamonov/undo.png?branch=master)](https://travis-ci.org/AlexParamonov/undo)
|
4
|
-
[![Gemnasium Build Status](https://gemnasium.com/AlexParamonov/undo.png)](http://gemnasium.com/AlexParamonov/undo)
|
5
4
|
[![Coverage Status](https://coveralls.io/repos/AlexParamonov/undo/badge.png?branch=master)](https://coveralls.io/r/AlexParamonov/undo?branch=master)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/AlexParamonov/undo.png)](https://codeclimate.com/github/AlexParamonov/undo)
|
6
|
+
[![Gemnasium Build Status](https://gemnasium.com/AlexParamonov/undo.png)](http://gemnasium.com/AlexParamonov/undo)
|
6
7
|
[![Gem Version](https://badge.fury.io/rb/undo.png)](http://badge.fury.io/rb/undo)
|
7
8
|
|
8
|
-
Undo
|
9
|
-
It stores the object state before the
|
9
|
+
The Undo restores previous object state.
|
10
|
+
It stores the object state before the mutation and allows to
|
10
11
|
restore this state later.
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
Lightweight, modular and very flexible library that works as with
|
14
|
+
Rails, as with plain Ruby. No specific persistence required: store
|
15
|
+
data as it suites you.
|
16
|
+
|
17
|
+
The Undo uses plugable adapters for storage such as Redis,
|
18
|
+
RailsCache. And serializers such as ActiveModel for example. Simple
|
19
|
+
interface makes it easy to implement new adapters or tweak existing.
|
20
|
+
|
21
|
+
Most of adapters and serializers have no external dependencies and can
|
22
|
+
be used with similary quaking objects.
|
17
23
|
|
18
24
|
Contents
|
19
25
|
---------
|
@@ -23,11 +29,14 @@ Contents
|
|
23
29
|
1. Undo operation
|
24
30
|
1. Configuration options
|
25
31
|
1. In place configuration
|
32
|
+
1. Pass through options
|
33
|
+
1. Utils
|
26
34
|
1. Contacts
|
27
35
|
1. Compatibility
|
28
36
|
1. Contributing
|
29
37
|
1. Copyright
|
30
38
|
|
39
|
+
|
31
40
|
Installation
|
32
41
|
------------
|
33
42
|
|
@@ -43,146 +52,159 @@ Or install it yourself as:
|
|
43
52
|
|
44
53
|
$ gem install undo
|
45
54
|
|
55
|
+
|
46
56
|
Requirements
|
47
57
|
------------
|
48
58
|
1. Ruby 1.9 or above
|
49
|
-
1. gem virtus ~> 1.0
|
59
|
+
1. gem [virtus](https://github.com/solnic/virtus) ~> 1.0
|
60
|
+
|
50
61
|
|
51
62
|
Usage
|
52
63
|
-----
|
53
64
|
|
54
65
|
### Undo operation
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
uuid = Undo.store object
|
68
|
+
|
69
|
+
# ...
|
70
|
+
# do something destructive
|
71
|
+
# ...
|
72
|
+
|
73
|
+
Undo.restore uuid
|
74
|
+
|
60
75
|
That is basically it :)
|
61
76
|
|
62
|
-
|
63
|
-
|
64
|
-
``` ruby
|
65
|
-
decorated_object = Undo.wrap object
|
66
|
-
Undo.restore decorated_object.uuid
|
67
|
-
```
|
68
|
-
Use decorated_object as usual afterwards. Undo gem will store object
|
69
|
-
state on each hit to `mutator methods`. By default mutator_methods are
|
70
|
-
`update`, `delete`, `destroy`. Those methods can be changed either in
|
71
|
-
place or using global configuration (see below).
|
72
|
-
|
73
|
-
To use something more advanced rather plain memory storage and
|
74
|
-
pass through serializer, configure the Undo:
|
75
|
-
|
76
|
-
``` ruby
|
77
|
-
Undo.configure do |config|
|
78
|
-
config.storage = Undo::Storage::RailsCache.new
|
79
|
-
config.serializer = Undo::Serializer::ActiveModel.new
|
80
|
-
end
|
81
|
-
```
|
82
|
-
gem `undo-storage-rails_cache` and gem `undo-serializer-active_model` are required for this.
|
83
|
-
There are a bunch of other Rails free adapters. Read about them in configuration chapter below.
|
77
|
+
To use something more advanced than plain memory storage and
|
78
|
+
pass through serializer, configure the Undo:
|
84
79
|
|
85
|
-
|
80
|
+
Undo.configure do |config|
|
81
|
+
config.storage = Undo::Storage::RailsCache.new
|
82
|
+
config.serializer = Undo::Serializer::ActiveModel.new
|
83
|
+
end
|
84
|
+
|
85
|
+
It allows to serialize and deserialize ActiveRecord models (and
|
86
|
+
POROs after implementing the #attributes method) and store it to Rails
|
87
|
+
cache. See those gems documentation for more details:
|
88
|
+
|
89
|
+
* [gem `undo-storage-rails_cache`](https://github.com/AlexParamonov/undo-storage-rails_cache)
|
90
|
+
* [gem `undo-serializer-active_model`](https://github.com/AlexParamonov/undo-serializer-active_model)
|
91
|
+
|
92
|
+
There are more adapters. Read about them in configuration chapter below.
|
93
|
+
|
94
|
+
When storage does not garbage collect old records, call
|
86
95
|
|
87
|
-
|
88
|
-
setting (see below in configuration chapter).
|
96
|
+
Undo.delete uuid
|
89
97
|
|
90
|
-
|
98
|
+
to manually delete related data in storage. It accepts the same options as store/restore.
|
91
99
|
|
92
|
-
|
93
|
-
|
100
|
+
### UUID
|
101
|
+
|
102
|
+
UUID is a uniq key used by the Undo to store and retrieve data. It is
|
103
|
+
generated automatically, but it is possible to either provide custom
|
104
|
+
uuid or custom uuid generator.
|
94
105
|
|
95
|
-
|
96
|
-
Undo.
|
97
|
-
|
106
|
+
# use generated uuid
|
107
|
+
uuid = Undo.store object
|
108
|
+
Undo.restore uuid
|
98
109
|
|
99
|
-
|
110
|
+
# manually specify uuid
|
111
|
+
uuid = "uniq identifier"
|
112
|
+
Undo.store object, uuid: uuid
|
113
|
+
Undo.restore uuid
|
100
114
|
|
101
|
-
|
102
|
-
|
103
|
-
Undo.
|
104
|
-
|
105
|
-
|
115
|
+
# specify uuid generator in place
|
116
|
+
uuid = Undo.store object, uuid_generator: ->(object) { "#{object.class.name}_#{object.id}" }
|
117
|
+
Undo.restore uuid
|
118
|
+
|
119
|
+
By default uuids are generated by `SecureRandom.uuid`. The generator
|
120
|
+
set by `uuid_generator` option which may be set in place as shown
|
121
|
+
above or in global configuration (see below).
|
106
122
|
|
107
123
|
### Configuration options
|
108
124
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
125
|
+
#### Storage
|
126
|
+
|
127
|
+
The `storage` option is responsible of defining a storage adapter that
|
128
|
+
can store, fetch and delete object state from storage.
|
129
|
+
|
130
|
+
Adapter must implement `store(uuid, object, options)`, `fetch(uuid, options)` and
|
131
|
+
optionally `delete(uuid, options)` methods. Naming follows ruby `Hash`
|
132
|
+
interface.
|
133
|
+
|
134
|
+
Currently available storage adapters:
|
135
|
+
|
136
|
+
* [`Undo::Storage::Memory`](https://github.com/AlexParamonov/undo/blob/master/lib/undo/storage/memory.rb) is a simple runtime storage (ruby Hash object).
|
137
|
+
* [`gem "undo-storage-rails_cache"`](https://github.com/AlexParamonov/undo-storage-rails_cache) designed for Rails cache, but can be used with any similarly quaking cache store. Rails constant may not even be defined.
|
138
|
+
* [`gem "undo-storage-redis"`](https://github.com/AlexParamonov/undo-storage-redis) designed to be used with `gem "redis"`
|
139
|
+
|
140
|
+
Check the [documentation on Github](http://github.com/AlexParamonov/undo/README.md)
|
141
|
+
for current list of available storage adapters.
|
115
142
|
|
116
|
-
|
117
|
-
on project repository for full list of currently available storage adapters.
|
143
|
+
#### Serializer
|
118
144
|
|
119
|
-
To convert objects to
|
145
|
+
To convert objects to hashes that can be processed by storage adapters
|
146
|
+
and backward, `serializer` is used:
|
120
147
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
148
|
+
Undo.configure do |config|
|
149
|
+
config.serializer = CustomSerializer.new
|
150
|
+
end
|
151
|
+
|
152
|
+
Serializer must implement `serialize(object, options)` and
|
153
|
+
`deserialize(object, options)` methods.
|
126
154
|
|
127
155
|
Currently available serializers:
|
128
|
-
* `Undo::Serializer::Null` pass though serializer. It do nothing and returns whatever passed to it.
|
129
|
-
* `gem undo-serializer-active_model` depends on #attributes method so can be used with Virtus and PORO objects as well as with ActiveRecord objects.
|
130
156
|
|
131
|
-
|
132
|
-
|
157
|
+
* [`Undo::Serializer::Null`](https://github.com/AlexParamonov/undo/blob/master/lib/undo/serializer/null.rb) passes through object without any convertions. It do nothing and returns whatever passed to it.
|
158
|
+
* [`gem undo-serializer-active_model`](https://github.com/AlexParamonov/undo-serializer-active_model) allows to serialize the ActiveRecord models as well as POROs (with implemented #attributes method).
|
159
|
+
* [`gem undo-serializer-primitive`](https://github.com/AlexParamonov/undo-serializer-primitive) serializes primitive values. Is used by active_model serializer
|
133
160
|
|
134
|
-
|
161
|
+
Check the [documentation on Github](http://github.com/AlexParamonov/undo/README.md)
|
162
|
+
for current list of available serializers.
|
135
163
|
|
136
|
-
|
137
|
-
|
164
|
+
Serializer could return any type of object, Hash is not forced. But in
|
165
|
+
this case compatible storage adapter should be used.
|
138
166
|
|
139
|
-
|
140
|
-
Undo.configure do |config|
|
141
|
-
config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
|
142
|
-
end
|
143
|
-
```
|
167
|
+
#### UUID generator
|
144
168
|
|
145
|
-
`
|
146
|
-
By default mutator_methods are `update`, `delete`, `destroy`.
|
147
|
-
To append custom mutator_methods use
|
169
|
+
`uuid_generator` option allows to setup custom uuid generator:
|
148
170
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
```
|
171
|
+
Undo.configure do |config|
|
172
|
+
config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
|
173
|
+
end
|
154
174
|
|
155
|
-
|
175
|
+
By default it is using `SecureRandom.uuid`.
|
156
176
|
|
157
|
-
|
158
|
-
place for given operation. To restore object from another storage use
|
159
|
-
`storage` option:
|
177
|
+
### In place configuration
|
160
178
|
|
161
|
-
|
162
|
-
|
163
|
-
```
|
179
|
+
Any configuration option from previous chapter can be applied in place
|
180
|
+
for a given operation.
|
164
181
|
|
165
|
-
|
182
|
+
For example to restore object from another storage, the `storage`
|
183
|
+
option may be used in place:
|
166
184
|
|
167
|
-
|
168
|
-
Undo.wrap object, mutator_methods: [:save]
|
169
|
-
```
|
185
|
+
Undo.restore uuid, storage: AnotherStorage.new
|
170
186
|
|
171
187
|
To use custom serializer or deserializer use `serializer` option:
|
172
188
|
|
173
|
-
|
174
|
-
Undo.
|
175
|
-
|
176
|
-
|
177
|
-
|
189
|
+
Undo.store post, serializer: PostSerializer.new(post)
|
190
|
+
Undo.restore uuid, serializer: PostDeserializer.new(options)
|
191
|
+
|
192
|
+
and so on.
|
193
|
+
|
194
|
+
### Pass through options
|
195
|
+
|
196
|
+
Any option, that is not recognized by the Undo as configuration
|
197
|
+
option, will be bypass to the serializer and storage adapter:
|
198
|
+
|
199
|
+
Undo.store post, include: :comments, expires_in: 1.hour
|
178
200
|
|
179
|
-
|
180
|
-
`#serialize` or `#deserialize`, those options can be set in place as well:
|
201
|
+
Same applies for `#restore` and `#delete` methods.
|
181
202
|
|
182
|
-
|
183
|
-
Undo.store post, include: :comments
|
184
|
-
```
|
203
|
+
### Utils
|
185
204
|
|
205
|
+
* [`Undo::Container::Json`](https://github.com/AlexParamonov/undo/blob/master/lib/undo/container/json.rb) used to pack and unpack hashes in storages
|
206
|
+
* [`Undo::Storage::Adapter`](https://github.com/AlexParamonov/undo/blob/master/lib/undo/storage/adapter.rb) abstract storage adapter that provides pack and unpack methods
|
207
|
+
* [Shared integration test](https://github.com/AlexParamonov/undo/blob/master/lib/undo/integration/shared_undo_integration_examples.rb) can be used in storage or serializers to verify integrity with Undo gem versions
|
186
208
|
|
187
209
|
Contacts
|
188
210
|
-------------
|
@@ -207,11 +229,11 @@ See [build history](http://travis-ci.org/#!/AlexParamonov/undo/builds)
|
|
207
229
|
|
208
230
|
## Contributing
|
209
231
|
|
210
|
-
1. Fork repository
|
211
|
-
2. Create
|
212
|
-
3. Commit
|
232
|
+
1. [Fork repository](http://github.com/AlexParamonov/undo/fork)
|
233
|
+
2. Create feature branch (`git checkout -b my-new-feature`)
|
234
|
+
3. Commit changes (`git commit -am 'Add some feature'`)
|
213
235
|
4. Push to the branch (`git push origin my-new-feature`)
|
214
|
-
5. Create new Pull Request
|
236
|
+
5. Create new Pull Request on Github
|
215
237
|
|
216
238
|
Copyright
|
217
239
|
---------
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/undo.rb
CHANGED
@@ -1,44 +1,32 @@
|
|
1
1
|
require "undo/version"
|
2
2
|
require "undo/config"
|
3
|
-
require "undo/
|
3
|
+
require "undo/keeper"
|
4
4
|
|
5
5
|
module Undo
|
6
|
-
def self.configure
|
7
|
-
|
6
|
+
def self.configure
|
7
|
+
yield config
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.store(object, options = {})
|
11
|
-
|
12
|
-
uuid(object, options).tap do |uuid|
|
13
|
-
config.storage.put uuid,
|
14
|
-
config.serializer.serialize(object, config.filter(options))
|
15
|
-
end
|
16
|
-
end
|
11
|
+
keeper(options).store object
|
17
12
|
end
|
18
13
|
|
19
14
|
def self.restore(uuid, options = {})
|
20
|
-
|
21
|
-
config.serializer.deserialize config.storage.fetch(uuid),
|
22
|
-
config.filter(options)
|
23
|
-
|
24
|
-
end
|
15
|
+
keeper(options).restore uuid
|
25
16
|
end
|
26
17
|
|
27
|
-
def self.
|
28
|
-
options
|
29
|
-
config.with(options) do |config|
|
30
|
-
Wrapper.new object, options.merge(mutator_methods: config.mutator_methods)
|
31
|
-
end
|
18
|
+
def self.delete(uuid, options = {})
|
19
|
+
keeper(options).delete uuid
|
32
20
|
end
|
33
21
|
|
34
22
|
private
|
35
|
-
def self.
|
36
|
-
|
23
|
+
def self.config
|
24
|
+
@config ||= Config.new
|
37
25
|
end
|
38
26
|
|
39
|
-
def self.
|
40
|
-
|
27
|
+
def self.keeper(options)
|
28
|
+
Keeper.new(config, options)
|
41
29
|
end
|
42
30
|
|
43
|
-
private_class_method :
|
31
|
+
private_class_method :config, :keeper
|
44
32
|
end
|
data/lib/undo/config.rb
CHANGED
@@ -1,35 +1,28 @@
|
|
1
1
|
require "virtus"
|
2
|
+
require "undo/serializer/null"
|
3
|
+
require "undo/storage/memory"
|
4
|
+
require "securerandom"
|
2
5
|
|
3
6
|
module Undo
|
4
7
|
class Config
|
5
8
|
include Virtus.model
|
6
9
|
|
7
|
-
attribute :
|
10
|
+
attribute :serializer, Object, default: Undo::Serializer::Null.new
|
11
|
+
attribute :storage, Object, default: Undo::Storage::Memory.new
|
12
|
+
attribute :uuid_generator, Proc, default: :default_uuid_generator
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
->(object) { SecureRandom.uuid }
|
12
|
-
}
|
13
|
-
attribute :serializer, Object, default: ->(config, _) {
|
14
|
-
require "undo/serializer/null"
|
15
|
-
Undo::Serializer::Null.new
|
16
|
-
}
|
17
|
-
attribute :storage, Object, default: ->(config, _) {
|
18
|
-
require "undo/storage/memory"
|
19
|
-
Undo::Storage::Memory.new serializer: config.serializer
|
20
|
-
}, lazy: true
|
21
|
-
|
22
|
-
def with(attribute_updates = {}, &block)
|
23
|
-
config = attribute_updates.empty? ? self
|
24
|
-
: self.class.new(attribute_set.get(self).merge attribute_updates)
|
25
|
-
|
26
|
-
block_given? ? block.call(config) : config
|
14
|
+
def with(attribute_updates = {})
|
15
|
+
self.class.new attributes.merge attribute_updates
|
27
16
|
end
|
28
17
|
|
29
18
|
def filter(options)
|
30
|
-
|
31
|
-
|
32
|
-
|
19
|
+
attribute_names = attribute_set.map(&:name)
|
20
|
+
options.reject { |key, _| attribute_names.include? key.to_sym }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def default_uuid_generator
|
25
|
+
-> object { SecureRandom.uuid }
|
33
26
|
end
|
34
27
|
end
|
35
28
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
shared_examples "undo integration" do
|
2
|
+
subject { Undo }
|
3
|
+
let(:object) { Hash.new hello: :world }
|
4
|
+
|
5
|
+
it "stores and restores object" do
|
6
|
+
uuid = subject.store object
|
7
|
+
expect(subject.restore uuid).to eq object
|
8
|
+
end
|
9
|
+
|
10
|
+
it "deletes stored object" do
|
11
|
+
uuid = subject.store object
|
12
|
+
subject.delete uuid
|
13
|
+
expect { subject.restore uuid }.to raise_error(KeyError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "delete an unexisted key does not raise an error" do
|
17
|
+
expect { subject.delete "does not exist" }.not_to raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "special cases" do
|
21
|
+
it "stores and restores nil" do
|
22
|
+
uuid = subject.store nil
|
23
|
+
expect(subject.restore uuid).to eq nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "stores and restores array" do
|
27
|
+
uuid = subject.store [1,2,3]
|
28
|
+
expect(subject.restore uuid).to eq [1,2,3]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/undo/keeper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Undo
|
4
|
+
class Keeper
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def initialize(config, options)
|
8
|
+
@config = config.with options
|
9
|
+
@options = config.filter options
|
10
|
+
end
|
11
|
+
|
12
|
+
def store(object)
|
13
|
+
build_uuid(object).tap do |uuid|
|
14
|
+
reflection = serializer.serialize object, options
|
15
|
+
storage.store uuid, reflection, options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def restore(uuid)
|
20
|
+
reflection = storage.fetch uuid, options
|
21
|
+
serializer.deserialize reflection, options
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(uuid)
|
25
|
+
storage.delete uuid, options
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
attr_reader :config, :options
|
30
|
+
def_delegators :config, :storage, :serializer, :uuid_generator
|
31
|
+
|
32
|
+
def build_uuid(object)
|
33
|
+
options[:uuid] || uuid_generator.call(object)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "undo/container/json"
|
2
|
+
|
3
|
+
module Undo
|
4
|
+
module Storage
|
5
|
+
class Adapter
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
@container = options.fetch(:container) { Undo::Container::Json.new }
|
9
|
+
end
|
10
|
+
|
11
|
+
def store(uuid, object, options = {}) end
|
12
|
+
def fetch(uuid, options = {}) end
|
13
|
+
def delete(uuid, options = {}) end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :options, :container
|
17
|
+
|
18
|
+
def pack(object)
|
19
|
+
container.pack object
|
20
|
+
end
|
21
|
+
|
22
|
+
def unpack(data)
|
23
|
+
container.unpack data
|
24
|
+
end
|
25
|
+
|
26
|
+
def adapter_options(local_options)
|
27
|
+
options.merge local_options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/undo/storage/memory.rb
CHANGED
@@ -4,17 +4,19 @@ module Undo
|
|
4
4
|
def initialize(options = {})
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
7
|
+
def store(uuid, object, options = {})
|
8
8
|
storage[uuid] = object
|
9
9
|
end
|
10
10
|
|
11
|
-
def fetch(uuid)
|
11
|
+
def fetch(uuid, options = {})
|
12
12
|
storage.fetch(uuid)
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
def delete(uuid, options = {})
|
16
|
+
storage.delete(uuid)
|
17
|
+
end
|
17
18
|
|
19
|
+
private
|
18
20
|
def storage
|
19
21
|
@storage ||= {}
|
20
22
|
end
|
data/lib/undo/version.rb
CHANGED
data/spec/spec_helper_lite.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
if !!ENV["CI"]
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls'
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
add_filter '/vendor/'
|
12
|
+
minimum_coverage(90)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
require "pry"
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper_lite"
|
2
|
+
require "undo/container/json"
|
3
|
+
|
4
|
+
describe Undo::Container::Json do
|
5
|
+
let(:container) { described_class.new }
|
6
|
+
|
7
|
+
it "packs to json" do
|
8
|
+
json = container.pack "hello" => "world"
|
9
|
+
expect(json).to eq '{"hello":"world"}'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "unpaks from json" do
|
13
|
+
hash = container.unpack '{"hello":"world"}'
|
14
|
+
expect(hash).to eq "hello" => "world"
|
15
|
+
end
|
16
|
+
end
|
@@ -2,18 +2,23 @@ require "spec_helper_lite"
|
|
2
2
|
require "undo/serializer/null"
|
3
3
|
|
4
4
|
describe Undo::Serializer::Null do
|
5
|
-
|
6
|
-
let(:serializer) { described_class.new }
|
5
|
+
let(:object) { double :object }
|
7
6
|
|
8
7
|
describe "returns passed argument" do
|
9
|
-
let(:object) { double :object }
|
10
|
-
|
11
8
|
specify "#serialize" do
|
12
|
-
expect(
|
9
|
+
expect(subject.serialize object).to eq object
|
13
10
|
end
|
14
11
|
|
15
12
|
specify "#deserialize" do
|
16
|
-
expect(
|
13
|
+
expect(subject.deserialize object).to eq object
|
17
14
|
end
|
18
15
|
end
|
16
|
+
|
17
|
+
it "accepts options" do
|
18
|
+
options = { foo: :bar }
|
19
|
+
expect do
|
20
|
+
subject.serialize object, options
|
21
|
+
subject.deserialize object, options
|
22
|
+
end.not_to raise_error
|
23
|
+
end
|
19
24
|
end
|
@@ -2,12 +2,25 @@ require "spec_helper_lite"
|
|
2
2
|
require 'undo/storage/memory'
|
3
3
|
|
4
4
|
describe Undo::Storage::Memory do
|
5
|
-
subject { described_class }
|
6
|
-
let(:adapter) { subject.new }
|
7
5
|
let(:object) { double :object }
|
8
6
|
|
9
7
|
it "stores any object" do
|
10
|
-
|
11
|
-
expect(
|
8
|
+
subject.store 123, object
|
9
|
+
expect(subject.fetch 123).to eq object
|
10
|
+
end
|
11
|
+
|
12
|
+
it "deletes stored object" do
|
13
|
+
subject.store 123, object
|
14
|
+
subject.delete 123
|
15
|
+
expect { subject.fetch 123 }.to raise_error(KeyError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "accepts options" do
|
19
|
+
options = { foo: :bar }
|
20
|
+
expect do
|
21
|
+
subject.store 123, object, options
|
22
|
+
subject.fetch 123, options
|
23
|
+
subject.delete 123, options
|
24
|
+
end.not_to raise_error
|
12
25
|
end
|
13
26
|
end
|
data/spec/undo_spec.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
require "spec_helper_lite"
|
2
|
+
require "undo/integration/shared_undo_integration_examples"
|
2
3
|
|
3
4
|
describe Undo do
|
4
5
|
let(:object) { double :object, change: true }
|
5
6
|
subject { described_class }
|
6
7
|
|
7
|
-
|
8
|
-
uuid = subject.store object
|
9
|
-
expect(subject.restore uuid).to eq object
|
10
|
-
end
|
8
|
+
include_examples "undo integration"
|
11
9
|
|
12
10
|
it "stores and restores object using provided uuid" do
|
13
11
|
uuid = "uniq_identifier"
|
@@ -16,13 +14,13 @@ describe Undo do
|
|
16
14
|
expect(subject.restore uuid).to eq object
|
17
15
|
end
|
18
16
|
|
19
|
-
describe "
|
20
|
-
let(:storage) { double :storage }
|
17
|
+
describe "serialization" do
|
21
18
|
let(:serializer) { double :serializer }
|
19
|
+
let(:storage) { double :storage }
|
22
20
|
|
23
21
|
it "serializes data before storing" do
|
24
22
|
expect(serializer).to receive(:serialize).with(object, anything).ordered
|
25
|
-
expect(storage).to receive(:
|
23
|
+
expect(storage).to receive(:store).ordered
|
26
24
|
|
27
25
|
subject.store object,
|
28
26
|
storage: storage,
|
@@ -30,17 +28,19 @@ describe Undo do
|
|
30
28
|
end
|
31
29
|
|
32
30
|
it "deserializes data before restoring" do
|
33
|
-
|
34
|
-
|
35
|
-
expect(storage).to receive(:fetch).and_return(foo: :bar).ordered
|
36
|
-
expect(serializer).to receive(:deserialize).with({ foo: :bar }, anything).ordered
|
31
|
+
expect(storage).to receive(:fetch).and_return(object).ordered
|
32
|
+
expect(serializer).to receive(:deserialize).with(object, anything).ordered
|
37
33
|
|
38
|
-
subject.restore uuid,
|
34
|
+
subject.restore "uuid",
|
39
35
|
storage: storage,
|
40
36
|
serializer: serializer
|
41
37
|
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "pass through options to serializer" do
|
41
|
+
let(:serializer) { double :serializer }
|
42
42
|
|
43
|
-
|
43
|
+
specify "from #store" do
|
44
44
|
expect(serializer).to receive(:serialize).with(object, foo: :bar)
|
45
45
|
|
46
46
|
subject.store object,
|
@@ -48,19 +48,7 @@ describe Undo do
|
|
48
48
|
foo: :bar
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
expect(serializer).to receive(:serialize)
|
53
|
-
|
54
|
-
wrapper = subject.wrap object,
|
55
|
-
serializer: serializer,
|
56
|
-
mutator_methods: :change,
|
57
|
-
foo: :bar
|
58
|
-
|
59
|
-
wrapper.change
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
it "pass through options from restore to deserialize" do
|
51
|
+
specify "from #restore" do
|
64
52
|
uuid = subject.store object
|
65
53
|
expect(serializer).to receive(:deserialize).with(object, foo: :bar)
|
66
54
|
|
@@ -70,39 +58,23 @@ describe Undo do
|
|
70
58
|
end
|
71
59
|
end
|
72
60
|
|
73
|
-
describe "
|
74
|
-
|
75
|
-
subject.configure do |config|
|
76
|
-
config.mutator_methods = [:change]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
it "is a decorator" do
|
81
|
-
object = %w[hello world]
|
61
|
+
describe "pass through options to storage" do
|
62
|
+
let(:storage) { double :storage }
|
82
63
|
|
83
|
-
|
84
|
-
expect(
|
85
|
-
decorator.some_method
|
64
|
+
specify "from #store" do
|
65
|
+
expect(storage).to receive(:store).with(anything, object, foo: :bar)
|
86
66
|
|
87
|
-
|
88
|
-
|
67
|
+
subject.store object,
|
68
|
+
storage: storage,
|
69
|
+
foo: :bar
|
89
70
|
end
|
90
71
|
|
91
|
-
|
92
|
-
|
93
|
-
uuid = "uniq_identifier"
|
94
|
-
model = subject.wrap object, uuid: uuid
|
95
|
-
model.change
|
96
|
-
|
97
|
-
expect(subject.restore uuid).to eq object
|
98
|
-
end
|
72
|
+
specify "from #restore" do
|
73
|
+
expect(storage).to receive(:fetch).with("uuid", foo: :bar)
|
99
74
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
expect(subject.restore model.uuid).to eq object
|
105
|
-
end
|
75
|
+
subject.restore "uuid",
|
76
|
+
storage: storage,
|
77
|
+
foo: :bar
|
106
78
|
end
|
107
79
|
end
|
108
80
|
end
|
data/undo.gemspec
CHANGED
@@ -1,21 +1,17 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'undo/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = "undo"
|
8
|
-
spec.version =
|
7
|
+
spec.version = IO.read("VERSION")
|
9
8
|
spec.authors = ["Alexander Paramonov"]
|
10
9
|
spec.email = ["alexander.n.paramonov@gmail.com"]
|
11
10
|
spec.summary = %q{Reverts operation made upon object}
|
12
11
|
spec.description = %q{
|
13
|
-
Undo
|
14
|
-
|
15
|
-
|
16
|
-
Undo uses adapters for storage (Redis, ActiveRecord, etc) and custom serializers (ActiveRecord, Virtus, etc).
|
17
|
-
It is very lightweight solution that can be used as well with heavy ActiveRecord as with simple Hash or Virtus objects.
|
18
|
-
No database required: store data as it suites you.
|
12
|
+
The Undo restores previous object state.
|
13
|
+
Lightweight, modular and very flexible library that works as with
|
14
|
+
Rails, as with plain Ruby.
|
19
15
|
}
|
20
16
|
spec.homepage = "http://github.com/AlexParamonov/undo"
|
21
17
|
spec.license = "MIT"
|
@@ -26,8 +22,8 @@ No database required: store data as it suites you.
|
|
26
22
|
spec.require_paths = ["lib"]
|
27
23
|
|
28
24
|
spec.add_dependency "virtus", "~> 1.0"
|
29
|
-
spec.
|
25
|
+
spec.add_dependency "json", ">= 1.7"
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.0'
|
30
27
|
spec.add_development_dependency "rake"
|
31
28
|
spec.add_development_dependency "rspec", ">= 3.0.0.beta1"
|
32
|
-
|
33
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: undo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Paramonov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: virtus
|
@@ -24,20 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
47
|
+
version: '1.0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
54
|
+
version: '1.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,12 +82,9 @@ dependencies:
|
|
68
82
|
version: 3.0.0.beta1
|
69
83
|
description: |2
|
70
84
|
|
71
|
-
Undo
|
72
|
-
|
73
|
-
|
74
|
-
Undo uses adapters for storage (Redis, ActiveRecord, etc) and custom serializers (ActiveRecord, Virtus, etc).
|
75
|
-
It is very lightweight solution that can be used as well with heavy ActiveRecord as with simple Hash or Virtus objects.
|
76
|
-
No database required: store data as it suites you.
|
85
|
+
The Undo restores previous object state.
|
86
|
+
Lightweight, modular and very flexible library that works as with
|
87
|
+
Rails, as with plain Ruby.
|
77
88
|
email:
|
78
89
|
- alexander.n.paramonov@gmail.com
|
79
90
|
executables: []
|
@@ -90,16 +101,22 @@ files:
|
|
90
101
|
- LICENSE.txt
|
91
102
|
- README.md
|
92
103
|
- Rakefile
|
104
|
+
- VERSION
|
93
105
|
- lib/undo.rb
|
94
106
|
- lib/undo/config.rb
|
107
|
+
- lib/undo/container/json.rb
|
108
|
+
- lib/undo/integration/shared_undo_integration_examples.rb
|
109
|
+
- lib/undo/keeper.rb
|
95
110
|
- lib/undo/serializer/null.rb
|
111
|
+
- lib/undo/storage/adapter.rb
|
96
112
|
- lib/undo/storage/memory.rb
|
97
113
|
- lib/undo/version.rb
|
98
|
-
- lib/undo/wrapper.rb
|
99
114
|
- spec/spec_helper_lite.rb
|
115
|
+
- spec/support/ci_helper.rb
|
116
|
+
- spec/undo/config_spec.rb
|
117
|
+
- spec/undo/container/json_spec.rb
|
100
118
|
- spec/undo/serializer/null_spec.rb
|
101
119
|
- spec/undo/storage/memory_spec.rb
|
102
|
-
- spec/undo/wrapper_spec.rb
|
103
120
|
- spec/undo_spec.rb
|
104
121
|
- undo.gemspec
|
105
122
|
homepage: http://github.com/AlexParamonov/undo
|
@@ -128,8 +145,10 @@ specification_version: 4
|
|
128
145
|
summary: Reverts operation made upon object
|
129
146
|
test_files:
|
130
147
|
- spec/spec_helper_lite.rb
|
148
|
+
- spec/support/ci_helper.rb
|
149
|
+
- spec/undo/config_spec.rb
|
150
|
+
- spec/undo/container/json_spec.rb
|
131
151
|
- spec/undo/serializer/null_spec.rb
|
132
152
|
- spec/undo/storage/memory_spec.rb
|
133
|
-
- spec/undo/wrapper_spec.rb
|
134
153
|
- spec/undo_spec.rb
|
135
154
|
has_rdoc:
|
data/lib/undo/wrapper.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require "forwardable"
|
2
|
-
|
3
|
-
module Undo
|
4
|
-
class Wrapper < SimpleDelegator
|
5
|
-
extend Forwardable
|
6
|
-
def_delegators :object, :class, :kind_of?
|
7
|
-
attr_reader :uuid
|
8
|
-
|
9
|
-
def initialize(object, options = {})
|
10
|
-
@object = object
|
11
|
-
@mutator_methods = options.delete(:mutator_methods) || []
|
12
|
-
@uuid = object.respond_to?(:uuid) ? object.uuid : options.fetch(:uuid)
|
13
|
-
@options = options
|
14
|
-
|
15
|
-
super object
|
16
|
-
end
|
17
|
-
|
18
|
-
def method_missing(method, *args, &block)
|
19
|
-
store if mutator_methods.include? method
|
20
|
-
super method, *args, &block
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
attr_reader :object, :options
|
25
|
-
|
26
|
-
def mutator_methods
|
27
|
-
Kernel.Array(@mutator_methods)
|
28
|
-
end
|
29
|
-
|
30
|
-
def store
|
31
|
-
Undo.store object, options.merge(uuid: uuid)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/spec/undo/wrapper_spec.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require "spec_helper_lite"
|
2
|
-
|
3
|
-
describe Undo::Wrapper do
|
4
|
-
subject { described_class }
|
5
|
-
let(:model) { subject.new object, uuid: uuid, mutator_methods: mutator_methods }
|
6
|
-
let(:mutator_methods) { [:change] }
|
7
|
-
let(:object) { double :object, change: true }
|
8
|
-
let(:uuid) { double :uuid }
|
9
|
-
|
10
|
-
describe "storage" do
|
11
|
-
it "stores object when mutator method is called" do
|
12
|
-
expect(model).to receive(:store)
|
13
|
-
model.change
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#uuid" do
|
18
|
-
it "uses provided uuid" do
|
19
|
-
expect(model.uuid).to eq uuid
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "when object respond_to uuid" do
|
23
|
-
it "uses object#uuid instead" do
|
24
|
-
expect(object).to receive(:uuid) { "123" }
|
25
|
-
expect(model.uuid).to eq "123"
|
26
|
-
expect(Undo).to receive(:store).with(object, hash_including(uuid: "123"))
|
27
|
-
model.change
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|