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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4898ed46bb0f2c3a761f82478c73b9d78c6e1a7d
4
- data.tar.gz: 6a3b917da4a4ebd61757e227d1e54e090dd23c05
3
+ metadata.gz: 6bbec1f206620c902a60106546a48d9b126a31d8
4
+ data.tar.gz: 1e0f6f864cc9bc39bbf80950e6b9071d4b436814
5
5
  SHA512:
6
- metadata.gz: 5675e00c800a9e4e6a590e112fe331b211e6f57f97d1b851a335343c721e89c06c4d0325617a8696da4a8fb8e25f1cf7b38bee0c2d3e85d9de42428abfa23e86
7
- data.tar.gz: 955338eac937b4825f4604c1601d43b0032d6bdf70b685ea1469d3ed3aef8329be1bf085284c3fd765ba03eb5338d8b967e08c33afa63d5b42755cd87f082095
6
+ metadata.gz: 8e08e8aa4f48fe64d67651e07a6f305ef50f7192fbf8f75b06d26bbaf321fbc53d77b7130523719c7885efe9514627eee6691e82b1cbc7385ba4b2210ffa8f49
7
+ data.tar.gz: 3dbb5154581a0e24a443e9002d037b43648a542a73f92096018073af42114f16caa80083edb26519106877c51cf35f8c019e27eef55fff182295e2d515a2c0e3
@@ -1 +1 @@
1
- 2.1.0-p0
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 reverts operation made upon object.
9
- It stores the object state before the mutator operation and allows to
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
- Undo uses adapters for storage (Redis, ActiveRecord, etc) and custom
13
- serializers (ActiveRecord, Virtus, etc). It is very lightweight
14
- solution that can be used as well with heavy ActiveRecord as with
15
- simple Hash or Virtus objects. No database required: store data as it
16
- suites you.
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
- ``` ruby
57
- uuid = Undo.store object
58
- Undo.restore uuid
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
- Additionally possible to wrap object in Undo decorator:
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
- ### UUID
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
- By default uuids are generated by `SecureRandom.uuid`. The generator is managed by `uuid_generator`
88
- setting (see below in configuration chapter).
96
+ Undo.delete uuid
89
97
 
90
- UUID can be provided to both #wrap and #store methods and it will be used instead of generated one:
98
+ to manually delete related data in storage. It accepts the same options as store/restore.
91
99
 
92
- ``` ruby
93
- uuid = "uniq identifier"
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
- Undo.store object, uuid: uuid
96
- Undo.restore uuid
97
- ```
106
+ # use generated uuid
107
+ uuid = Undo.store object
108
+ Undo.restore uuid
98
109
 
99
- If object respond to #uuid method then it value will be used instead:
110
+ # manually specify uuid
111
+ uuid = "uniq identifier"
112
+ Undo.store object, uuid: uuid
113
+ Undo.restore uuid
100
114
 
101
- ``` ruby
102
- object = OpenStruct.new uuid: "uniq identifier"
103
- Undo.store object # => uniq identifier
104
- Undo.restore "uniq identifier"
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
- `storage` option responsible for putting and fetching object state to or from some storage.
110
- Implement `put(uuid, object)` and `fetch(uuid)` methods.
111
- Currently available storages:
112
- * `Undo::Storage::Memory` simple runtime storage (Hash)
113
- * `gem "undo-storage-rails_cache"` designed for Rails, but can be used with any ducktype cache store
114
- * `gem "undo-storage-redis"` designed to be used with `gem "redis"` from v0.1 to current
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
- See also [documentation](http://github.com/AlexParamonov/undo)
117
- on project repository for full list of currently available storage adapters.
143
+ #### Serializer
118
144
 
119
- To convert objects to data that can be processed by storage adapters and backward, use `serializers`:
145
+ To convert objects to hashes that can be processed by storage adapters
146
+ and backward, `serializer` is used:
120
147
 
121
- ``` ruby
122
- Undo.configure do |config|
123
- config.serializer = CustomSerializer.new
124
- end
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
- Check [documentation](http://github.com/AlexParamonov/undo) on project
132
- repository for currently available serializers.
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
- `uuid_generator` option allows to setup custom uuid generator.
161
+ Check the [documentation on Github](http://github.com/AlexParamonov/undo/README.md)
162
+ for current list of available serializers.
135
163
 
136
- By default it is using `SecureRandom.uuid`.
137
- To define custom uuid generator use `uuid_generator` option:
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
- ``` ruby
140
- Undo.configure do |config|
141
- config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
142
- end
143
- ```
167
+ #### UUID generator
144
168
 
145
- `mutator methods` option defines a list of methods that will trigger storage#put
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
- ``` ruby
150
- Undo.configure do |config|
151
- config.mutator_methods += [:put, :push, :pop]
152
- end
153
- ```
171
+ Undo.configure do |config|
172
+ config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
173
+ end
154
174
 
155
- ### In place configuration
175
+ By default it is using `SecureRandom.uuid`.
156
176
 
157
- Any configuration option from previous chapter can be applied in
158
- place for given operation. To restore object from another storage use
159
- `storage` option:
177
+ ### In place configuration
160
178
 
161
- ``` ruby
162
- Undo.restore uuid, storage: AnotherStorage.new
163
- ```
179
+ Any configuration option from previous chapter can be applied in place
180
+ for a given operation.
164
181
 
165
- To wrap an object using custom mutator_methods use `mutator_methods` option:
182
+ For example to restore object from another storage, the `storage`
183
+ option may be used in place:
166
184
 
167
- ``` ruby
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
- ``` ruby
174
- Undo.wrap post, serializer: PostSerializer.new(post)
175
- post.destroy
176
- Undo.restore uuid, serializer: PostDeserializer.new(options)
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
- Additionally when given serializer accepts additional options to
180
- `#serialize` or `#deserialize`, those options can be set in place as well:
201
+ Same applies for `#restore` and `#delete` methods.
181
202
 
182
- ``` ruby
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 ( http://github.com/AlexParamonov/undo/fork )
211
- 2. Create your feature branch (`git checkout -b my-new-feature`)
212
- 3. Commit your changes (`git commit -am 'Add some feature'`)
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
@@ -1,44 +1,32 @@
1
1
  require "undo/version"
2
2
  require "undo/config"
3
- require "undo/wrapper"
3
+ require "undo/keeper"
4
4
 
5
5
  module Undo
6
- def self.configure(&block)
7
- block_given? ? block.call(config) : config
6
+ def self.configure
7
+ yield config
8
8
  end
9
9
 
10
10
  def self.store(object, options = {})
11
- config.with(options) do |config|
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
- config.with(options) do |config|
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.wrap(object, options = {})
28
- options[:uuid] ||= uuid object, 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.uuid(object, options = {})
36
- options[:uuid] || config.uuid_generator.call(object)
23
+ def self.config
24
+ @config ||= Config.new
37
25
  end
38
26
 
39
- def self.config
40
- @config ||= Undo::Config.new
27
+ def self.keeper(options)
28
+ Keeper.new(config, options)
41
29
  end
42
30
 
43
- private_class_method :uuid, :config
31
+ private_class_method :config, :keeper
44
32
  end
@@ -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 :mutator_methods, Array[Symbol], default: [:update, :delete, :destroy]
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
- attribute :uuid_generator, Proc, default: ->(config, _) {
10
- require "securerandom"
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
- options.delete_if do |key, _|
31
- attributes.keys.include? key
32
- end
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,15 @@
1
+ require 'json'
2
+
3
+ module Undo
4
+ module Container
5
+ class Json
6
+ def pack(object)
7
+ object.to_json
8
+ end
9
+
10
+ def unpack(json)
11
+ JSON.load json
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -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
@@ -4,17 +4,19 @@ module Undo
4
4
  def initialize(options = {})
5
5
  end
6
6
 
7
- def put(uuid, object)
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
- private
16
- attr_writer :storage
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
@@ -1,3 +1,3 @@
1
1
  module Undo
2
- VERSION = "0.1.1"
2
+ VERSION = IO.read("VERSION")
3
3
  end
@@ -1,12 +1,5 @@
1
- if !!ENV['CI']
2
- require 'coveralls'
3
- Coveralls.wear!
4
- else
5
- require 'pry'
6
- end
7
-
8
- ENV['RAILS_ENV'] ||= 'test'
9
1
  require 'rspec'
2
+ require_relative "support/ci_helper"
10
3
  require 'undo'
11
4
 
12
5
  $: << File.expand_path('../lib', File.dirname(__FILE__))
@@ -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,10 @@
1
+ require "spec_helper_lite"
2
+
3
+ describe Undo::Config do
4
+ describe "#filter" do
5
+ it "removes known attributes" do
6
+ subject.class.attribute :known
7
+ expect(subject.filter known: true, unknown: true).to eq unknown: true
8
+ end
9
+ end
10
+ 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
- subject { described_class }
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(serializer.serialize object).to eq object
9
+ expect(subject.serialize object).to eq object
13
10
  end
14
11
 
15
12
  specify "#deserialize" do
16
- expect(serializer.deserialize object).to eq object
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
- adapter.put 123, object
11
- expect(adapter.fetch 123).to eq object
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
@@ -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
- it "stores and restores object" do
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 "serializing" do
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(:put).ordered
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
- uuid = subject.store object
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
- it "pass through options from store to serialize" do
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
- it "pass through options from wrap to serialize" do
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 "#wrap" do
74
- before do
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
- decorator = subject.wrap object
84
- expect(object).to receive(:some_method)
85
- decorator.some_method
64
+ specify "from #store" do
65
+ expect(storage).to receive(:store).with(anything, object, foo: :bar)
86
66
 
87
- expect(decorator.class).to eq Array
88
- expect(decorator).to be_a Array
67
+ subject.store object,
68
+ storage: storage,
69
+ foo: :bar
89
70
  end
90
71
 
91
- describe "restores" do
92
- specify "using provided uuid" do
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
- specify "using gerenated uuid" do
101
- model = subject.wrap object
102
- model.change
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
@@ -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 = Undo::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 reverts operation made upon object.
14
- It stores the object state before the mutator operation and allows to restore this state later.
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.add_development_dependency "bundler", "~> 1.5"
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.1.1
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-03-16 00:00:00.000000000 Z
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.5'
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.5'
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 reverts operation made upon object.
72
- It stores the object state before the mutator operation and allows to restore this state later.
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:
@@ -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
@@ -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