undo 0.0.1

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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +18 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +19 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +191 -0
  11. data/Rakefile +10 -0
  12. data/lib/undo.rb +23 -0
  13. data/lib/undo/config.rb +27 -0
  14. data/lib/undo/environment.rb +3 -0
  15. data/lib/undo/model.rb +33 -0
  16. data/lib/undo/serializer/simple.rb +23 -0
  17. data/lib/undo/storage/memory_adapter.rb +23 -0
  18. data/lib/undo/version.rb +3 -0
  19. data/spec/acceptance/it_works_spec.rb +7 -0
  20. data/spec/acceptance/undo_deletion_spec.rb +14 -0
  21. data/spec/factories.rb +6 -0
  22. data/spec/it_works_spec.rb +8 -0
  23. data/spec/rails_app/.gitignore +4 -0
  24. data/spec/rails_app/Rakefile +7 -0
  25. data/spec/rails_app/app/models/.gitkeep +0 -0
  26. data/spec/rails_app/app/models/user.rb +2 -0
  27. data/spec/rails_app/config.ru +4 -0
  28. data/spec/rails_app/config/application.rb +13 -0
  29. data/spec/rails_app/config/boot.rb +6 -0
  30. data/spec/rails_app/config/database.yml +22 -0
  31. data/spec/rails_app/config/environment.rb +5 -0
  32. data/spec/rails_app/config/environments/development.rb +23 -0
  33. data/spec/rails_app/config/environments/production.rb +50 -0
  34. data/spec/rails_app/config/environments/test.rb +34 -0
  35. data/spec/rails_app/config/routes.rb +2 -0
  36. data/spec/rails_app/db/migrate/20140214213610_create_users.rb +10 -0
  37. data/spec/rails_app/db/schema.rb +23 -0
  38. data/spec/rails_app/db/seeds.rb +7 -0
  39. data/spec/rails_app/script/rails +6 -0
  40. data/spec/spec_helper_lite.rb +12 -0
  41. data/spec/spec_helper_rails.rb +22 -0
  42. data/spec/undo/model_spec.rb +36 -0
  43. data/spec/undo/serializer/simple_spec.rb +19 -0
  44. data/spec/undo_spec.rb +46 -0
  45. data/undo.gemspec +33 -0
  46. metadata +199 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be7c69cbf8e0489d7d3ee42a63d3791ace4a85ee
4
+ data.tar.gz: 8652a478948f1e5cd1ea38e1c7943d025f755b4d
5
+ SHA512:
6
+ metadata.gz: aa050823366a0684e076aa024f2b5a90078f9c6e7df8a6e3cfaf1a08134025b193bdd95383b2bdcb705d8fc376951d73eda5b367efdc4ab35b6caaafdc3fd347
7
+ data.tar.gz: 96d0ab84f7c9269d9d032b0ec55f0f6d02b0d6efaafba220696f2b3eb9730f368b974d41ef07a1f4311ef0baa0424339d383baa737fa7f0dd0aeabca73c1c03d
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /tags
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ undo
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0-p0
data/.travis.yml ADDED
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.0
5
+ - 1.9.3
6
+ - ruby-head
7
+ - rbx-19mode
8
+ - jruby-19mode
9
+ - jruby-head
10
+
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
14
+ - rvm: rbx-19mode
15
+ - rvm: jruby-19mode
16
+ - rvm: jruby-head
17
+
18
+ script:
19
+ - "bundle exec rake ci:all"
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in undo.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rails', '4.0.2'
8
+ gem 'sqlite3'
9
+ gem 'factory_girl'
10
+ gem 'faker'
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Alexander Paramonov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,191 @@
1
+ Undo
2
+ ==========
3
+ [![Build Status](https://travis-ci.org/AlexParamonov/undo.png?branch=master)](http://travis-ci.org/AlexParamonov/undo)
4
+ [![Gemnasium Build Status](https://gemnasium.com/AlexParamonov/undo.png)](http://gemnasium.com/AlexParamonov/undo)
5
+ [![Coverage Status](https://coveralls.io/repos/AlexParamonov/undo/badge.png?branch=master)](https://coveralls.io/r/AlexParamonov/undo?branch=master)
6
+
7
+ Undo last operation on object.
8
+ Undo gem allows to use custom adapters for storage (Redis,
9
+ ActiveRecord, etc). And custom serializers when need (ActiveRecord, Virtus, etc)
10
+
11
+ Contents
12
+ ---------
13
+ 1. Installation
14
+ 1. Requirements
15
+ 1. Usage
16
+ 1. Undo operation
17
+ 1. Configuration options
18
+ 1. In place configuration
19
+ 1. Contacts
20
+ 1. Compatibility
21
+ 1. Contributing
22
+ 1. Copyright
23
+
24
+ Installation
25
+ ------------
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ gem 'undo'
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Or install it yourself as:
36
+
37
+ $ gem install undo
38
+
39
+ Requirements
40
+ ------------
41
+ 1. Ruby 1.9 or above
42
+ 1. gem virtus ~> 1.0
43
+
44
+ Usage
45
+ -----
46
+
47
+ ### Undo operation
48
+
49
+ Wrap object in Undo decorator:
50
+
51
+ ``` ruby
52
+ Undo.wrap object
53
+ ```
54
+
55
+ And use it as usual afterwards. Undo gem will store object state on each hit to `mutator methods`.
56
+ By default mutator_methods are `update`, `delete`, `destroy`.
57
+ To append custom mutator_methods use
58
+
59
+ ``` ruby
60
+ Undo.configure do |config|
61
+ config.mutator_methods += [:put, :push, :pop]
62
+ end
63
+ ```
64
+
65
+ To restore previous version
66
+
67
+ ```
68
+ Undo.restore uuid
69
+ ```
70
+
71
+ `uuid` is provided by wrapped object:
72
+
73
+ ```
74
+ Undo.wrap(object).uuid
75
+ ```
76
+
77
+ By default it is using `SecureRandom.uuid`.
78
+ To define custom uuid generator use `uuid_generator` option:
79
+
80
+ ``` ruby
81
+ Undo.configure do |config|
82
+ config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
83
+ end
84
+ ```
85
+
86
+ ### Configuration options
87
+ `storage` option responsible for putting and fetching object state to or from some storage.
88
+ Implement `put(uuid, object)` and `fetch(uuid)` methods. See MemoryAdapter for example.
89
+ See also [documentation](http://github.com/AlexParamonov/undo)
90
+ on project repository for currently available storage adapters.
91
+ If provided storage cant handle objects (most of the storage works with own formats as json for example),
92
+ pass `serializer` to it:
93
+
94
+ ``` ruby
95
+ Undo.configure do |config|
96
+ config.storage = AnotherStorage.new(serializer: config.serializer)
97
+ end
98
+ ```
99
+
100
+ By default here is very basic `Serializer::Simple`.
101
+ There are no more serializers available now, but check
102
+ [documentation](http://github.com/AlexParamonov/undo) on project
103
+ repository for currently available serializers.
104
+
105
+ Serializer is not used by `Undo` gem directly. It mean to be used in
106
+ storage adopters to serialize and deserialize data to required format.
107
+ Storage adapter may use serializer this way:
108
+
109
+ ``` ruby
110
+ json = serializer.to_json object # in put method
111
+ return serializer.load_from_json json # in fetch method
112
+
113
+ xml = serializer.to_xml object
114
+ object = serializer.load_from_xml xml
115
+ ```
116
+
117
+ Usage of serializer is optional, some storages does not need them.
118
+ Serializer has the responsibility to write object to persistence
119
+ (load_from\_\* methods) This seems a good candinate to extraction to own
120
+ class (Deserializer or Loader) but due to to\_\* and load_from\_\*
121
+ method are tightly coupled with data format it is in one class now.
122
+ Let me know using the Github feedback (create an issue) if you have
123
+ any idea on this topic.
124
+
125
+ `uuid_generator` option allows to setup custom uuid generator.
126
+
127
+ By default it is using `SecureRandom.uuid`.
128
+ To define custom uuid generator use `uuid_generator` option:
129
+
130
+ ``` ruby
131
+ Undo.configure do |config|
132
+ config.uuid_generator = ->(object) { "#{object.class.name}_#{object.id}" }
133
+ end
134
+ ```
135
+
136
+ `mutator methods` option defines a list of methods that will trigger storage#put
137
+ By default mutator_methods are `update`, `delete`, `destroy`.
138
+ To set custom mutator_methods use
139
+
140
+ ``` ruby
141
+ Undo.configure do |config|
142
+ config.mutator_methods = [:put, :push, :pop]
143
+ end
144
+ ```
145
+
146
+ ### In place configuration
147
+ Any configuration option from previous chapter can be applied in place
148
+ only for given operation.
149
+ To restore object from another storage use `storage` option:
150
+
151
+ ``` ruby
152
+ Undo.restore uuid, storage: AnotherStorage.new
153
+ ```
154
+
155
+ To wrap an object using custom serializer use `serializer` option:
156
+
157
+ ``` ruby
158
+ Undo.wrap object, serializer: AnotherSerializer.new
159
+ ```
160
+
161
+ Contacts
162
+ -------------
163
+ Have questions or recommendations? Contact me via `alexander.n.paramonov@gmail.com`
164
+ Found a bug or have enhancement request? You are welcome at [Github bugtracker](https://github.com/AlexParamonov/undo/issues)
165
+
166
+
167
+ Compatibility
168
+ -------------
169
+ tested with Ruby
170
+
171
+ * 2.1
172
+ * 2.0
173
+ * 1.9.3
174
+ * rbx-19mode
175
+ * ruby-head
176
+
177
+ See [build history](http://travis-ci.org/#!/AlexParamonov/undo/builds)
178
+
179
+
180
+ ## Contributing
181
+
182
+ 1. Fork repository ( http://github.com/AlexParamonov/undo/fork )
183
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
184
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
185
+ 4. Push to the branch (`git push origin my-new-feature`)
186
+ 5. Create new Pull Request
187
+
188
+ Copyright
189
+ ---------
190
+ Copyright © 2014 Alexander Paramonov.
191
+ Released under the MIT License. See the LICENSE file for further details.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ namespace :ci do
5
+ desc "Run tests on CI"
6
+ RSpec::Core::RakeTask.new('all') do |t|
7
+ t.rspec_opts = '-fprogress'
8
+ t.verbose = true
9
+ end
10
+ end
data/lib/undo.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "undo/version"
2
+ require "undo/config"
3
+
4
+ module Undo
5
+ require "undo/model"
6
+
7
+ def self.configure(&block)
8
+ yield(config) if block_given?
9
+ config
10
+ end
11
+
12
+ def self.config
13
+ @config ||= Undo::Config.new
14
+ end
15
+
16
+ def self.wrap(object, *args)
17
+ Model.new object, *args
18
+ end
19
+
20
+ def self.restore(uuid, options = {})
21
+ config.with(options).storage.fetch uuid
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ require "virtus"
2
+
3
+ module Undo
4
+ class Config
5
+ include Virtus.model
6
+
7
+ attribute :mutator_methods, Array[Symbol], default: [:update, :delete, :destroy]
8
+
9
+ attribute :uuid_generator, Proc, default: ->(config, _) {
10
+ require "securerandom"
11
+ ->(object) { SecureRandom.uuid }
12
+ }
13
+ attribute :storage, Object, default: ->(config, _) {
14
+ require "undo/storage/memory_adapter"
15
+ Undo::Storage::MemoryAdapter.new
16
+ }
17
+ attribute :serializer, Object, default: ->(config, _) {
18
+ require "undo/serializer/simple"
19
+ Undo::Serializer::Simple.new
20
+ }
21
+
22
+ def with(attribute_updates = {}, &block)
23
+ return self if attribute_updates.empty?
24
+ self.class.new attribute_set.get(self).merge(attribute_updates)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Undo
2
+ RUNNING_ON_CI = !!ENV['CI']
3
+ end
data/lib/undo/model.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Undo
2
+ class Model < SimpleDelegator
3
+ def initialize(object, options = {})
4
+ @object = object
5
+ @config = config.with options
6
+ super object
7
+ end
8
+
9
+ def uuid
10
+ @uuid ||= object.respond_to?(:uuid) ? object.uuid : generate_uuid
11
+ end
12
+
13
+ def method_missing(method, *args, &block)
14
+ store if config.mutator_methods.include? method
15
+ super method, *args, &block
16
+ end
17
+
18
+ private
19
+ attr_reader :object
20
+
21
+ def generate_uuid
22
+ config.uuid_generator.call object
23
+ end
24
+
25
+ def store
26
+ config.storage.put uuid, object
27
+ end
28
+
29
+ def config
30
+ @config ||= Undo.config
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ require "json"
2
+
3
+ module Undo
4
+ module Serializer
5
+ class Simple
6
+ def to_json(object)
7
+ object.to_json
8
+ end
9
+
10
+ def load_from_json(json)
11
+ JSON.parse json
12
+ end
13
+
14
+ def to_xml(object)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def load_from_xml(xml)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Undo
2
+ module Storage
3
+ class MemoryAdapter
4
+ def initialize(options = {})
5
+ end
6
+
7
+ def put(uuid, object)
8
+ storage[uuid] = object
9
+ end
10
+
11
+ def fetch(uuid)
12
+ storage.fetch uuid
13
+ end
14
+
15
+ private
16
+ attr_writer :storage
17
+
18
+ def storage
19
+ @storage ||= {}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Undo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper_rails"
2
+
3
+ describe "Initial rails setup" do
4
+ it "works" do
5
+ expect(true).to eq true
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper_rails"
2
+
3
+ describe "Undo deletion" do
4
+ subject { Undo }
5
+
6
+ it "reverts user deletion" do
7
+ user = create :user
8
+ undoable_model = subject.wrap user
9
+ undoable_model.destroy
10
+ restored_user = subject.restore undoable_model.uuid
11
+
12
+ expect(restored_user).to eq user
13
+ end
14
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ name { Faker::Name.name }
4
+ email { Faker::Internet.email }
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require "spec_helper_lite"
2
+
3
+ describe "Initial setup" do
4
+ it "works" do
5
+ expect(true).to eq true
6
+ end
7
+
8
+ end
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ db/*.sqlite3
3
+ log/*.log
4
+ tmp/
@@ -0,0 +1,7 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+ require 'rake'
6
+
7
+ RailsApp::Application.load_tasks
File without changes
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run RailsApp::Application
@@ -0,0 +1,13 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "rails/all"
4
+
5
+ # If you have a Gemfile, require the gems listed there, including any gems
6
+ # you've limited to :test, :development, or :production.
7
+ Bundler.require(:default, Rails.env) if defined?(Bundler)
8
+
9
+ module RailsApp
10
+ class Application < Rails::Application
11
+ I18n.enforce_available_locales = false
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+
3
+ # Set up gems listed in the Gemfile.
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
+
6
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
@@ -0,0 +1,22 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3
3
+ development:
4
+ adapter: sqlite3
5
+ database: db/development.sqlite3
6
+ pool: 5
7
+ timeout: 5000
8
+
9
+ # Warning: The database defined as "test" will be erased and
10
+ # re-generated from your development database when you run "rake".
11
+ # Do not set this db to the same as development or production.
12
+ test:
13
+ adapter: sqlite3
14
+ database: db/test.sqlite3
15
+ pool: 5
16
+ timeout: 5000
17
+
18
+ production:
19
+ adapter: sqlite3
20
+ database: db/production.sqlite3
21
+ pool: 5
22
+ timeout: 5000
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ RailsApp::Application.initialize!
@@ -0,0 +1,23 @@
1
+ RailsApp::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # In the development environment your application's code is reloaded on
5
+ # every request. This slows down response time but is perfect for development
6
+ # since you don't have to restart the webserver when you make code changes.
7
+ config.cache_classes = false
8
+ config.eager_load = false
9
+
10
+ # Show full error reports and disable caching
11
+ config.consider_all_requests_local = true
12
+ config.action_controller.perform_caching = false
13
+
14
+ # Don't care if the mailer can't send
15
+ config.action_mailer.raise_delivery_errors = false
16
+
17
+ # Print deprecation notices to the Rails logger
18
+ config.active_support.deprecation = :log
19
+
20
+ # Only use best-standards-support built into browsers
21
+ config.action_dispatch.best_standards_support = :builtin
22
+ end
23
+
@@ -0,0 +1,50 @@
1
+ RailsApp::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # The production environment is meant for finished, "live" apps.
5
+ # Code is not reloaded between requests
6
+ config.cache_classes = true
7
+ config.eager_load = true
8
+
9
+ # Full error reports are disabled and caching is turned on
10
+ config.consider_all_requests_local = false
11
+ config.action_controller.perform_caching = true
12
+
13
+ # Specifies the header that your server uses for sending files
14
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
15
+
16
+ # For nginx:
17
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
18
+
19
+ # If you have no front-end server that supports something like X-Sendfile,
20
+ # just comment this out and Rails will serve the files
21
+
22
+ # See everything in the log (default is :info)
23
+ # config.log_level = :debug
24
+
25
+ # Use a different logger for distributed setups
26
+ # config.logger = SyslogLogger.new
27
+
28
+ # Use a different cache store in production
29
+ # config.cache_store = :mem_cache_store
30
+
31
+ # Disable Rails's static asset server
32
+ # In production, Apache or nginx will already do this
33
+ config.serve_static_assets = false
34
+
35
+ # Enable serving of images, stylesheets, and javascripts from an asset server
36
+ # config.action_controller.asset_host = "http://assets.example.com"
37
+
38
+ # Disable delivery errors, bad email addresses will be ignored
39
+ # config.action_mailer.raise_delivery_errors = false
40
+
41
+ # Enable threaded mode
42
+ # config.threadsafe!
43
+
44
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
45
+ # the I18n.default_locale when a translation can not be found)
46
+ config.i18n.fallbacks = true
47
+
48
+ # Send deprecation notices to registered listeners
49
+ config.active_support.deprecation = :notify
50
+ end
@@ -0,0 +1,34 @@
1
+ RailsApp::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # The test environment is used exclusively to run your application's
5
+ # test suite. You never need to work with it otherwise. Remember that
6
+ # your test database is "scratch space" for the test suite and is wiped
7
+ # and recreated between test runs. Don't rely on the data there!
8
+ config.cache_classes = true
9
+ config.eager_load = false
10
+
11
+ # Show full error reports and disable caching
12
+ config.consider_all_requests_local = true
13
+ config.action_controller.perform_caching = false
14
+
15
+ # Raise exceptions instead of rendering exception templates
16
+ config.action_dispatch.show_exceptions = false
17
+
18
+ # Disable request forgery protection in test environment
19
+ config.action_controller.allow_forgery_protection = false
20
+
21
+ # Tell Action Mailer not to deliver emails to the real world.
22
+ # The :test delivery method accumulates sent emails in the
23
+ # ActionMailer::Base.deliveries array.
24
+ config.action_mailer.delivery_method = :test
25
+ config.action_mailer.default_url_options = { :host => 'www.example.com' }
26
+
27
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
28
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
29
+ # like if you have constraints or database-specific column types
30
+ # config.active_record.schema_format = :sql
31
+
32
+ # Print deprecation notices to the stderr
33
+ config.active_support.deprecation = :stderr
34
+ end
@@ -0,0 +1,2 @@
1
+ RailsApp::Application.routes.draw do
2
+ end
@@ -0,0 +1,10 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.string :email
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20140214213610) do
15
+
16
+ create_table "users", force: true do |t|
17
+ t.string "name"
18
+ t.string "email"
19
+ t.datetime "created_at"
20
+ t.datetime "updated_at"
21
+ end
22
+
23
+ end
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
+ # Mayor.create(:name => 'Daley', :city => cities.first)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,12 @@
1
+ if Undo::RUNNING_ON_CI
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ else
5
+ require 'pry'
6
+ end
7
+
8
+ ENV['RAILS_ENV'] ||= 'test'
9
+ require 'rspec'
10
+ require 'undo'
11
+
12
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
@@ -0,0 +1,22 @@
1
+ if Undo::RUNNING_ON_CI
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ else
5
+ require 'pry'
6
+ end
7
+
8
+ ENV['RAILS_ENV'] ||= 'test'
9
+
10
+ require File.expand_path('../rails_app/config/environment', __FILE__)
11
+ require 'rspec'
12
+ require_relative 'factories'
13
+
14
+ ActiveRecord::Migration.verbose = false
15
+ ActiveRecord::Migrator.migrate(Rails.root.join('db', 'migrate').to_s)
16
+
17
+ RSpec.configure do |config|
18
+ config.mock_with :rspec do |config|
19
+ config.syntax = [:expect, :should]
20
+ end
21
+ config.include FactoryGirl::Syntax::Methods
22
+ end
@@ -0,0 +1,36 @@
1
+ require "spec_helper_lite"
2
+
3
+ describe Undo::Model do
4
+ subject { described_class }
5
+ let(:model) { subject.new object }
6
+ let(:object) { double :object }
7
+
8
+ describe "#uuid" do
9
+ it "using object#uuid" do
10
+ expect(object).to receive(:uuid) { "123" }
11
+ expect(model.uuid).to eq "123"
12
+ end
13
+
14
+ context "object do not respond to #uuid" do
15
+ it "using configured uuid gerenator" do
16
+ model = subject.new object, uuid_generator: proc { "123" }
17
+ expect(model.uuid).to eq "123"
18
+ end
19
+
20
+ it "using SecureRandom uuid gerenator by default" do
21
+ expect(SecureRandom).to receive(:uuid) { "123" }
22
+ expect(model.uuid).to eq "123"
23
+ end
24
+
25
+ it "passes object to custom uuid gerenator" do
26
+ uuid_generator = double :uuid_generator
27
+ expect(uuid_generator).to receive(:call).with(object)
28
+
29
+ model = subject.new object, uuid_generator: uuid_generator
30
+ model.uuid
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper_lite"
2
+ require "undo/serializer/simple"
3
+
4
+ describe Undo::Serializer::Simple do
5
+ subject { described_class }
6
+ let(:serializer) { described_class.new }
7
+
8
+ it "serializes to json" do
9
+ object = { "hello" => "world" }
10
+ result = serializer.to_json object
11
+ expect(result).to eq '{"hello":"world"}'
12
+ end
13
+
14
+ it "deserializes from json" do
15
+ serialized_object = '{"hello":"world"}'
16
+ result = serializer.load_from_json serialized_object
17
+ expect(result).to eq "hello" => "world"
18
+ end
19
+ end
data/spec/undo_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "spec_helper_lite"
2
+
3
+ describe Undo do
4
+ subject { described_class }
5
+
6
+ describe "#wrap" do
7
+ let(:object) { double :object, change: true }
8
+
9
+ before do
10
+ subject.configure do |config|
11
+ config.mutator_methods = [:change]
12
+ end
13
+ end
14
+
15
+ it "is a decorator" do
16
+ object = [:a, :b]
17
+ decorator = subject.wrap object
18
+ expect(object).to receive(:some_method)
19
+
20
+ decorator.some_method
21
+ expect(object.class).to eq Array
22
+ end
23
+
24
+ it "restores object" do
25
+ undoable_model = subject.wrap object
26
+ undoable_model.change
27
+ restored_object = subject.restore undoable_model.uuid
28
+
29
+ expect(restored_object).to eq object
30
+ end
31
+
32
+ describe "with options" do
33
+ it "restores using provided options" do
34
+ storage = double
35
+ expect(storage).to receive(:put)
36
+ expect(storage).to receive(:fetch) { object }
37
+
38
+ undoable_model = subject.wrap object, storage: storage
39
+ undoable_model.change
40
+ restored_object = subject.restore undoable_model.uuid, storage: storage
41
+
42
+ expect(restored_object).to eq object
43
+ end
44
+ end
45
+ end
46
+ end
data/undo.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'undo/version'
5
+ require 'undo/environment'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "undo"
9
+ spec.version = Undo::VERSION
10
+ spec.authors = ["Alexander Paramonov"]
11
+ spec.email = ["alexander.n.paramonov@gmail.com"]
12
+ spec.summary = %q{Undo operations on object}
13
+ spec.description = %q{Undo allows to reverl last operation on object}
14
+ spec.homepage = "http://github.com/AlexParamonov/undo"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "virtus", "~> 1.0"
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", ">= 3.0.0.beta1"
26
+
27
+ if Undo::RUNNING_ON_CI
28
+ spec.add_development_dependency "coveralls"
29
+ else
30
+ spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "pry-plus" if "ruby" == RUBY_ENGINE
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: undo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Paramonov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: virtus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0.beta1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0.beta1
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-plus
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Undo allows to reverl last operation on object
98
+ email:
99
+ - alexander.n.paramonov@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".coveralls.yml"
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".ruby-gemset"
108
+ - ".ruby-version"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - lib/undo.rb
115
+ - lib/undo/config.rb
116
+ - lib/undo/environment.rb
117
+ - lib/undo/model.rb
118
+ - lib/undo/serializer/simple.rb
119
+ - lib/undo/storage/memory_adapter.rb
120
+ - lib/undo/version.rb
121
+ - spec/acceptance/it_works_spec.rb
122
+ - spec/acceptance/undo_deletion_spec.rb
123
+ - spec/factories.rb
124
+ - spec/it_works_spec.rb
125
+ - spec/rails_app/.gitignore
126
+ - spec/rails_app/Rakefile
127
+ - spec/rails_app/app/models/.gitkeep
128
+ - spec/rails_app/app/models/user.rb
129
+ - spec/rails_app/config.ru
130
+ - spec/rails_app/config/application.rb
131
+ - spec/rails_app/config/boot.rb
132
+ - spec/rails_app/config/database.yml
133
+ - spec/rails_app/config/environment.rb
134
+ - spec/rails_app/config/environments/development.rb
135
+ - spec/rails_app/config/environments/production.rb
136
+ - spec/rails_app/config/environments/test.rb
137
+ - spec/rails_app/config/routes.rb
138
+ - spec/rails_app/db/migrate/20140214213610_create_users.rb
139
+ - spec/rails_app/db/schema.rb
140
+ - spec/rails_app/db/seeds.rb
141
+ - spec/rails_app/script/rails
142
+ - spec/spec_helper_lite.rb
143
+ - spec/spec_helper_rails.rb
144
+ - spec/undo/model_spec.rb
145
+ - spec/undo/serializer/simple_spec.rb
146
+ - spec/undo_spec.rb
147
+ - undo.gemspec
148
+ homepage: http://github.com/AlexParamonov/undo
149
+ licenses:
150
+ - MIT
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.2.2
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Undo operations on object
172
+ test_files:
173
+ - spec/acceptance/it_works_spec.rb
174
+ - spec/acceptance/undo_deletion_spec.rb
175
+ - spec/factories.rb
176
+ - spec/it_works_spec.rb
177
+ - spec/rails_app/.gitignore
178
+ - spec/rails_app/Rakefile
179
+ - spec/rails_app/app/models/.gitkeep
180
+ - spec/rails_app/app/models/user.rb
181
+ - spec/rails_app/config.ru
182
+ - spec/rails_app/config/application.rb
183
+ - spec/rails_app/config/boot.rb
184
+ - spec/rails_app/config/database.yml
185
+ - spec/rails_app/config/environment.rb
186
+ - spec/rails_app/config/environments/development.rb
187
+ - spec/rails_app/config/environments/production.rb
188
+ - spec/rails_app/config/environments/test.rb
189
+ - spec/rails_app/config/routes.rb
190
+ - spec/rails_app/db/migrate/20140214213610_create_users.rb
191
+ - spec/rails_app/db/schema.rb
192
+ - spec/rails_app/db/seeds.rb
193
+ - spec/rails_app/script/rails
194
+ - spec/spec_helper_lite.rb
195
+ - spec/spec_helper_rails.rb
196
+ - spec/undo/model_spec.rb
197
+ - spec/undo/serializer/simple_spec.rb
198
+ - spec/undo_spec.rb
199
+ has_rdoc: