undo 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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