pedanco-diffr 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 +7 -0
- data/.gitignore +34 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +22 -0
- data/README.md +184 -0
- data/Rakefile +1 -0
- data/lib/pedanco/diffr.rb +33 -0
- data/lib/pedanco/diffr/change.rb +50 -0
- data/lib/pedanco/diffr/change_set.rb +209 -0
- data/lib/pedanco/diffr/version.rb +5 -0
- data/pedanco-diffr.gemspec +33 -0
- data/spec/lib/pedanco/diffr/change_set_spec.rb +184 -0
- data/spec/lib/pedanco/diffr/change_spec.rb +45 -0
- data/spec/spec_helper.rb +14 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ab7434a821dc08fd4cbaf49a4b1802182b755bed
|
4
|
+
data.tar.gz: 06219e44cfbf45f8ff8a867a2058ff8db8dc5e78
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c54abecceac0488bc88d4f1069bab65a62d4b20ecd55594e33ec0fbd8bc299941aac5a252c5ad22fc1ae4f9a4cc00f27846b1ae2eea8d9ff328c1b749af26087
|
7
|
+
data.tar.gz: 22b9b82fb4856c4957437ce5666494bec1f6f51de3e62b8f75ef21999e1e1343be8208fa0d3900ec5b221474c2ef668a51d56bf33a4cea8b3b0b521b062363d1
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pedanco-diffr (1.0.0)
|
5
|
+
activesupport (>= 3.2, <= 4.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (4.2.0)
|
11
|
+
i18n (~> 0.7)
|
12
|
+
json (~> 1.7, >= 1.7.7)
|
13
|
+
minitest (~> 5.1)
|
14
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
15
|
+
tzinfo (~> 1.1)
|
16
|
+
codeclimate-test-reporter (0.4.7)
|
17
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
18
|
+
coveralls (0.7.11)
|
19
|
+
multi_json (~> 1.10)
|
20
|
+
rest-client (>= 1.6.8, < 2)
|
21
|
+
simplecov (~> 0.9.1)
|
22
|
+
term-ansicolor (~> 1.3)
|
23
|
+
thor (~> 0.19.1)
|
24
|
+
diff-lcs (1.2.5)
|
25
|
+
docile (1.1.5)
|
26
|
+
i18n (0.7.0)
|
27
|
+
json (1.8.2)
|
28
|
+
mime-types (2.4.3)
|
29
|
+
minitest (5.5.1)
|
30
|
+
multi_json (1.11.0)
|
31
|
+
netrc (0.10.3)
|
32
|
+
rake (10.4.2)
|
33
|
+
rest-client (1.7.3)
|
34
|
+
mime-types (>= 1.16, < 3.0)
|
35
|
+
netrc (~> 0.7)
|
36
|
+
rspec (3.2.0)
|
37
|
+
rspec-core (~> 3.2.0)
|
38
|
+
rspec-expectations (~> 3.2.0)
|
39
|
+
rspec-mocks (~> 3.2.0)
|
40
|
+
rspec-core (3.2.1)
|
41
|
+
rspec-support (~> 3.2.0)
|
42
|
+
rspec-expectations (3.2.0)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.2.0)
|
45
|
+
rspec-mocks (3.2.1)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.2.0)
|
48
|
+
rspec-support (3.2.2)
|
49
|
+
simplecov (0.9.2)
|
50
|
+
docile (~> 1.1.0)
|
51
|
+
multi_json (~> 1.0)
|
52
|
+
simplecov-html (~> 0.9.0)
|
53
|
+
simplecov-html (0.9.0)
|
54
|
+
term-ansicolor (1.3.0)
|
55
|
+
tins (~> 1.0)
|
56
|
+
thor (0.19.1)
|
57
|
+
thread_safe (0.3.4)
|
58
|
+
tins (1.3.5)
|
59
|
+
tzinfo (1.2.2)
|
60
|
+
thread_safe (~> 0.1)
|
61
|
+
|
62
|
+
PLATFORMS
|
63
|
+
ruby
|
64
|
+
|
65
|
+
DEPENDENCIES
|
66
|
+
bundler (~> 1.7)
|
67
|
+
codeclimate-test-reporter
|
68
|
+
coveralls
|
69
|
+
pedanco-diffr!
|
70
|
+
rake (~> 10.0)
|
71
|
+
rspec (~> 3.2)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 DevelopmentArc
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# pedanco-diffr
|
2
|
+
|
3
|
+
[](https://codeship.com/projects/67769)
|
4
|
+
[](https://codeclimate.com/github/DevelopmentArc/pedanco-diffr)
|
5
|
+
[](https://coveralls.io/r/DevelopmentArc/pedanco-diffr?branch=master)
|
6
|
+
|
7
|
+
A change set library for managing how data is evolved overtime. `Pedanco::Diffr` allows you to track changes in a `ChangeSet` and then query the `ChangeSet` to determine if the data has changed, what the current value is and what the previous value was. This was inspired by the [ActiveModel::Dirty](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html) class, but has been isolated so that it can be used to track any kind of data change without the ActiveModel/ActiveRecord inclusions.
|
8
|
+
|
9
|
+
# Installation
|
10
|
+
```bash
|
11
|
+
gem install pedanco-diffr
|
12
|
+
```
|
13
|
+
|
14
|
+
Or add it to your Gemfile:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
gem 'pedanco-diffr'
|
18
|
+
```
|
19
|
+
|
20
|
+
# Usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
# Create a new ChangeSet
|
24
|
+
change_set = Pedanco::Diffr::ChangeSet.new(name: ['Tim', 'Tom'])
|
25
|
+
|
26
|
+
# Add another change and query the ChangeSet
|
27
|
+
change_set.add_change(:age, 21, 23)
|
28
|
+
change_set.name_changed? # => true
|
29
|
+
change_set.changed(:age) # => true
|
30
|
+
|
31
|
+
# Remove a change and query the ChangeSet
|
32
|
+
change_set.remove_change(:age) # => true
|
33
|
+
change_set.age_changed? # => false
|
34
|
+
change_set.changed?(:age) # => false
|
35
|
+
|
36
|
+
# Extract a change
|
37
|
+
name_change = change_set.get_change(:name) # => Pedanco::Diffr::Change
|
38
|
+
name_change.current # => 'Tim'
|
39
|
+
name_change.previous # => 'Tom'
|
40
|
+
```
|
41
|
+
|
42
|
+
# Creating a ChangeSet
|
43
|
+
When creating a new ChangeSet you can pass in a `Hash` of named `Arrays`. The key of the hash will be used to generate the change name, and the `Array` defines the current and previous values.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
data_set = { city: ['San Diego', 'Denver'], state: ['CA', nil] }
|
47
|
+
change_set = Pedanco::Diffr::ChangeSet.new(data_set)
|
48
|
+
change_set.state_changed? #=> true
|
49
|
+
```
|
50
|
+
|
51
|
+
You can also directly add changes using the `add_change` method.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
55
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
56
|
+
change_set.add_change(:state, 'CA')
|
57
|
+
|
58
|
+
change_set.city_changed? #=> true
|
59
|
+
change_set.state_changed? #=> true
|
60
|
+
```
|
61
|
+
|
62
|
+
If you want to import `ActiveModel::Dirty` changes into a `ChangeSet` you can call the `parse_changes()` method passing in the data.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# We have updated an Address Model (ActiveRecord::Base) and want to store the changes
|
66
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
67
|
+
change_set.parse_changes(address.changes)
|
68
|
+
```
|
69
|
+
|
70
|
+
# Quering the ChangeSet
|
71
|
+
|
72
|
+
Once a change has been added you can query the ChangeSet to see if the set contains a change you are looking for. You can do this by using the `_changed?` convinence method, which prepends the name of the change, such as `city_changed?`. You can can also call the `changed?` method passing in the name of the change you want to verify.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
76
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
77
|
+
|
78
|
+
# Using _changed?
|
79
|
+
change_set.city_changed? #=> true
|
80
|
+
change_set.address_changed? #=> false
|
81
|
+
|
82
|
+
# Using changed?()
|
83
|
+
change_set.changed?(:city) # => true
|
84
|
+
change_set.changed?(:address) # => false
|
85
|
+
```
|
86
|
+
|
87
|
+
The `changed?` method also allows you to pass in an array of names to query by. By default, if the `ChangeSet` has any of the changes requested, it will return `true`.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
91
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
92
|
+
|
93
|
+
# Pass an Array of names to query
|
94
|
+
change_set.changed?([:city, :address]) # => true
|
95
|
+
```
|
96
|
+
|
97
|
+
If you need to make sure that all the key names passed have changed, then you can pass the `:all` flag to `changed?` method.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
101
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
102
|
+
change_set.changed?([:city, :address], :all) # => false
|
103
|
+
```
|
104
|
+
|
105
|
+
# Using Changes
|
106
|
+
Once you have created a change set you can access the current and previous value of the change by calling `get_change`
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
110
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
111
|
+
change_set.add_change(:state, 'CA')
|
112
|
+
|
113
|
+
change_set.get_change(:city).current # => 'San Diego'
|
114
|
+
change_set.get_change(:city).previous # => 'Denver'
|
115
|
+
```
|
116
|
+
|
117
|
+
When calling `get_change` the ChangeSet will return a `Pedanco::Diffr::Change` instance. This instance allows you to get the name, current and previous value. If the change request can not be found then an empty `Change` will be returned.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
121
|
+
change = change_set.get_change(:age)
|
122
|
+
change.name # => :age
|
123
|
+
change.current # => nil
|
124
|
+
change.previous # => nil
|
125
|
+
```
|
126
|
+
|
127
|
+
# Change values
|
128
|
+
A `Pedanco::Diffr::Change` instance's current or previous values can be any kind of data. By default both the current and previous are `nil`. This allows for empty changes, a change from empty to something or something to empty. Only the name of the change is required. The name can be a `String` or `Symbol`. By defailt we convert the name to a `Symbol` for lookup purposes.
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
132
|
+
change_set.add_change('name', nil, nil) # => Empty change
|
133
|
+
change_set.add_change('field', 'Bar') # => Empty to something change
|
134
|
+
change_set.add_change(:money, nil, 'Something') # => Soemthing to empty change
|
135
|
+
```
|
136
|
+
|
137
|
+
When calling `add_change` only a current value must be passed to the method (including `nil`). By default the previous value will be set to `nil`.
|
138
|
+
|
139
|
+
# Updating changes
|
140
|
+
When working with a ChangeSet you can remove an existing change or overwrite it. To remove a change you can call the `remove_change` method. To override a change, just call `add_change` with the same name.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
change_set = Pedanco::Diffr::ChangeSet.new
|
144
|
+
change_set.add_change(:city, 'San Diego', 'Denver')
|
145
|
+
change_set.add_change(:state, 'CA')
|
146
|
+
|
147
|
+
# overriding a change
|
148
|
+
change_set.add_change(:state, 'CA', 'CO')
|
149
|
+
change_set.get_change(:state).previous # => 'CO'
|
150
|
+
|
151
|
+
# removing a change
|
152
|
+
change_set.remove_change(:city)
|
153
|
+
change_set.city_changed? # => false
|
154
|
+
```
|
155
|
+
|
156
|
+
# Why Pedanco::Diffr?
|
157
|
+
At [Pedanco](https://pedanco.com) we use [Wisper](https://github.com/krisleech/wisper) to handle global events. When these events are dispatched, they trigger cache invalidation, system wide updates, and other complex tasks that usually run in an async worker. The challenge we had was that some changes in the system trigger different actions.
|
158
|
+
|
159
|
+
For example, when a user changes their Role or name, we need to run a lot of cache updates to change access rights and views. But, if they just change their signature then we don't need to trigger these system updates.
|
160
|
+
|
161
|
+
`Pedanco::Diffr` allows us to build a `ChangeSet` that is then passed along with the Wisper event. Our global subscribers can then process the changes and trigger different actions based on what changed. We use a combination of custom change tracking in [Mutations](https://github.com/cypriss/mutations) and `ActiveRecord` changes to build out our `ChangeSet` and then dispatch them once the process of creation/updating is complete.
|
162
|
+
|
163
|
+
# Liscence
|
164
|
+
The MIT License (MIT)
|
165
|
+
|
166
|
+
Copyright (c) 2015 DevelopmentArc
|
167
|
+
|
168
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
169
|
+
of this software and associated documentation files (the "Software"), to deal
|
170
|
+
in the Software without restriction, including without limitation the rights
|
171
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
172
|
+
copies of the Software, and to permit persons to whom the Software is
|
173
|
+
furnished to do so, subject to the following conditions:
|
174
|
+
|
175
|
+
The above copyright notice and this permission notice shall be included in all
|
176
|
+
copies or substantial portions of the Software.
|
177
|
+
|
178
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
179
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
180
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
181
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
182
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
183
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
184
|
+
SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#--
|
2
|
+
# The MIT License (MIT)
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015 DevelopmentArc
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
14
|
+
# copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
# SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
# 3rd Party Gem Dependencies
|
26
|
+
require 'active_support'
|
27
|
+
require 'active_support/core_ext/object/blank'
|
28
|
+
require 'active_support/core_ext/array/wrap'
|
29
|
+
|
30
|
+
# Gem Dependencies
|
31
|
+
require 'pedanco/diffr/version'
|
32
|
+
require 'pedanco/diffr/change'
|
33
|
+
require 'pedanco/diffr/change_set'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Pedanco
|
2
|
+
module Diffr
|
3
|
+
#
|
4
|
+
# A Change represents a current and previous state for specifc piece of
|
5
|
+
# data. These states can represent any kind of information and are linked
|
6
|
+
# by the name of the data they represent.
|
7
|
+
#
|
8
|
+
# Pedanco::Diffr::Change.new(:first_name, 'Jim', 'James')
|
9
|
+
#
|
10
|
+
# @!attribute [rw] name
|
11
|
+
# @return [String,Symbol] the name identifier for the change set
|
12
|
+
# @!attribute [rw] current
|
13
|
+
# @return [Any] the current state of the change
|
14
|
+
# @!attribute [rw] previous
|
15
|
+
# @return [Any] the previous state of the change
|
16
|
+
#
|
17
|
+
# @author [jpolanco]
|
18
|
+
#
|
19
|
+
class Change
|
20
|
+
attr_accessor :name, :current, :previous
|
21
|
+
|
22
|
+
#
|
23
|
+
# Initializes the data when passed via new().
|
24
|
+
#
|
25
|
+
# @param name [String/Symbol] (required) The name of the data
|
26
|
+
# @param current [Any] (optional) The current value for the
|
27
|
+
# change, defaults to nil.
|
28
|
+
# @param previous [Any] (optional) The previous value for the
|
29
|
+
# change, defaults to nil.
|
30
|
+
#
|
31
|
+
# @raise [RuntimeError] if name is nil or ''
|
32
|
+
def initialize(name, current = nil, previous = nil)
|
33
|
+
fail 'Name is required for a Change.' if name.blank?
|
34
|
+
@name = name
|
35
|
+
@current = current
|
36
|
+
@previous = previous
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Converts the Change into an array, following the ActiveRecord
|
41
|
+
# changes syntax. The first position is the previous value, the
|
42
|
+
# second is the current value.
|
43
|
+
#
|
44
|
+
# @return [Array] The array version of the Change
|
45
|
+
def to_a
|
46
|
+
[@previous, @current]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module Pedanco
|
2
|
+
module Diffr
|
3
|
+
#
|
4
|
+
# A ChangeSet contains a one or more changes. Change data can be
|
5
|
+
# passed during `new()` or can be added/removed directly from the
|
6
|
+
# change instance.
|
7
|
+
#
|
8
|
+
# When a change is added, the ChangeSet provides both a direct method
|
9
|
+
# to look up a change or a convience method with a `_changed?` prefix.
|
10
|
+
#
|
11
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
12
|
+
# inst.add_change(:age, 40, 39)
|
13
|
+
#
|
14
|
+
# inst.age_changed? # true
|
15
|
+
# inst.changed?(:age) # true
|
16
|
+
#
|
17
|
+
# When creating a ChangeSet you can also pass in ActiveModel::Dirty syntax
|
18
|
+
# to the `parse_changes()` method to convert the data into a ChangeSet.
|
19
|
+
#
|
20
|
+
# @author [jpolanco]
|
21
|
+
#
|
22
|
+
class ChangeSet
|
23
|
+
#
|
24
|
+
# Initializes the Change instance. Allows for a hash of array pairs, ex:
|
25
|
+
#
|
26
|
+
# { name: ['Bob', 'Tom'], age: [33, 32] }
|
27
|
+
#
|
28
|
+
# which represents a change set to be parsed in on creation.
|
29
|
+
#
|
30
|
+
# @param change_hash [Hash]
|
31
|
+
# (optional) hash to be converted into a ChangeSet.
|
32
|
+
#
|
33
|
+
def initialize(change_hash = {})
|
34
|
+
@changes = parse_change_hash(change_hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Override to catch `_changed?` postfix convenience calls to
|
39
|
+
# determine if a property has changed.
|
40
|
+
#
|
41
|
+
def method_missing(name, *args, &block)
|
42
|
+
if name.to_s =~ /_changed\?/
|
43
|
+
@changes.key?(name.to_s.gsub(/_changed\?/, '').to_sym)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Implementation to support respond_to? lookup for
|
51
|
+
# `_changed?` postfix convenience calls.
|
52
|
+
#
|
53
|
+
# @return [Boolean] true if class responds to the requested
|
54
|
+
# method name
|
55
|
+
def respond_to?(name, include_private = false)
|
56
|
+
if name.to_s =~ /_changed\?/
|
57
|
+
true
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Adds a change to the change set. This method converts the arguments
|
65
|
+
# into a Change object internally.
|
66
|
+
#
|
67
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
68
|
+
# inst.add_change(:age, 40, 39)
|
69
|
+
#
|
70
|
+
# inst.age_changed? # true
|
71
|
+
#
|
72
|
+
# @param name [String,Symbol] The name of the value that has changed
|
73
|
+
# @param current_value [anything] The current value
|
74
|
+
# @param previous_value [anything] (optional) The previous value,
|
75
|
+
# the default is nil
|
76
|
+
#
|
77
|
+
# @return [Change] Returns the generated change
|
78
|
+
def add_change(name, current_value, previous_value = nil)
|
79
|
+
sym = name.to_sym
|
80
|
+
@changes[sym] =
|
81
|
+
Change.new(sym, current_value, previous_value)
|
82
|
+
@changes[sym]
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Removes an existing change by the name of the change. This method
|
87
|
+
# is safe to call if a change is not found.
|
88
|
+
#
|
89
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
90
|
+
# inst.add_change(:age, 40, 39)
|
91
|
+
# inst.age_changed? # true
|
92
|
+
#
|
93
|
+
# inst.remove_change(:age) # true
|
94
|
+
#
|
95
|
+
# @param name [String,Symbol] The name of the change to remove.
|
96
|
+
#
|
97
|
+
# @return [Change] The removed change,
|
98
|
+
# nil if no change by that name was found
|
99
|
+
def remove_change(name)
|
100
|
+
@changes.delete(name.to_sym)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Determines if the provided change(s) exist in the change set.
|
105
|
+
# When the match type is set to `:any`, if any key matches at least
|
106
|
+
# one change the method returns false.
|
107
|
+
#
|
108
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
109
|
+
# inst.add_change(:age, 40, 39)
|
110
|
+
# inst.add_change(:name, 'George', 'Frank')
|
111
|
+
#
|
112
|
+
# inst.changed?([:foo, :age]) # true
|
113
|
+
#
|
114
|
+
# If the match type is :all, all matches must exist.
|
115
|
+
#
|
116
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
117
|
+
# inst.add_change(:age, 40, 39)
|
118
|
+
# inst.add_change(:name, 'George', 'Frank')
|
119
|
+
#
|
120
|
+
# inst.changed?([:foo, :age], :all) # false
|
121
|
+
#
|
122
|
+
# @param keys [String,Symbol,Array] Defines a single or list
|
123
|
+
# of names to validate existence of.
|
124
|
+
# @param match_type [Symbol] (optional) Defines how a check is
|
125
|
+
# made, default is :any also accepts :all
|
126
|
+
#
|
127
|
+
# @return [Boolean] True if matches found, false if not.
|
128
|
+
def changed?(keys, match_type = :any)
|
129
|
+
keys = Array.wrap(keys).map(&:to_sym)
|
130
|
+
if match_type == :all
|
131
|
+
(keys & @changes.keys).length == keys.length
|
132
|
+
else
|
133
|
+
(keys & @changes.keys).present?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Looks up a change by name and returns it if found. If the
|
139
|
+
# change is not found an empty Change instance is returned. This
|
140
|
+
# prevents having to check for nil.
|
141
|
+
#
|
142
|
+
# inst = Pedanco::Diffr::ChangeSet.new
|
143
|
+
# inst.add_change(:age, 40, 39)
|
144
|
+
#
|
145
|
+
# # returns Pedanco::Diffr::Change(:age, current: 40, previous: 39)
|
146
|
+
# inst.get_change(:age)
|
147
|
+
#
|
148
|
+
# @param name [String,Symbol] The name of the change to lookup.
|
149
|
+
#
|
150
|
+
# @return [Change] The Change object by name,
|
151
|
+
# an empty change is returned if not found.
|
152
|
+
def get_change(name)
|
153
|
+
@changes.fetch(name.to_sym, Change.new(name.to_sym))
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Finds and returns a ChangeSet as an array, used for legacy rendering
|
158
|
+
# that expects changes to be in [current, previous] format.
|
159
|
+
#
|
160
|
+
# @param name [String,Symbol] (optional) The name of the change
|
161
|
+
#
|
162
|
+
# @return [Array] The Change as an array or empty if no change is found.
|
163
|
+
def to_a(name = nil)
|
164
|
+
if name.present?
|
165
|
+
change = @changes[name.to_sym]
|
166
|
+
change.present? ? change.to_a : []
|
167
|
+
else
|
168
|
+
@changes.map { |k, v| [k, v.to_a] }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Converts the Change Set into an Hash.
|
174
|
+
#
|
175
|
+
# {
|
176
|
+
# name: [current, previous]
|
177
|
+
# }
|
178
|
+
#
|
179
|
+
#
|
180
|
+
# @return [Hash] The Change Set as an hash.
|
181
|
+
def to_hash
|
182
|
+
Hash[@changes.map { |k, v| [k, v.to_a] }]
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Parses the ActiveModel::Dirty syntax to build a changeset.
|
187
|
+
# The Dirty syntax is:
|
188
|
+
#
|
189
|
+
# { 'property_name' => [old_value, new_value] }
|
190
|
+
#
|
191
|
+
# @param changes [Hash] An ActiveModel::Dirty changes hash
|
192
|
+
#
|
193
|
+
def parse_changes(changes)
|
194
|
+
changes.each { |name, change| add_change(name, change[1], change[0]) }
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def parse_change_hash(hash)
|
200
|
+
hash_array = hash.map do |k, v|
|
201
|
+
name = k.to_sym
|
202
|
+
value = Array.wrap(v)
|
203
|
+
[name, Change.new(name, value[0], value[1])]
|
204
|
+
end
|
205
|
+
Hash[hash_array]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -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 'pedanco/diffr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'pedanco-diffr'
|
8
|
+
spec.version = Pedanco::Diffr::VERSION
|
9
|
+
spec.authors = ['James Polanco']
|
10
|
+
spec.email = ['james@developmentarc.com']
|
11
|
+
spec.summary = 'Provides a change library for managing system changes.'
|
12
|
+
spec.description = <<-EOM
|
13
|
+
Pedanco::Diffr provides a change set management system for tracking changes
|
14
|
+
anywhere in the system. Diffr works with both ActiveRecord style changes and
|
15
|
+
provides a custom system so that it kind be used in a wider syntax without
|
16
|
+
requiring ActiveRecord or ActiveModel.
|
17
|
+
EOM
|
18
|
+
|
19
|
+
spec.homepage = 'https://github.com/DevelopmentArc/pedanco-diffr'
|
20
|
+
spec.license = 'MIT'
|
21
|
+
|
22
|
+
spec.files = `git ls-files -z`.split("\x0")
|
23
|
+
spec.test_files = `git ls-files test`.split("\x0")
|
24
|
+
spec.require_path = 'lib'
|
25
|
+
|
26
|
+
### Gem Dependencies
|
27
|
+
spec.add_runtime_dependency 'activesupport', '<= 4.2', '>= 3.2'
|
28
|
+
|
29
|
+
### Development Dependencies
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
31
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
33
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Pedanco::Diffr::ChangeSet do
|
4
|
+
|
5
|
+
let(:change_set) { Pedanco::Diffr::ChangeSet.new }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
it 'parses change hash' do
|
9
|
+
set = described_class.new(foo: %w(bar baz), biff: [nil, 'hello'])
|
10
|
+
expect(set.changed?([:foo, 'biff'])).to be_truthy
|
11
|
+
expect(set.get_change(:foo).current).to eq 'bar'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#method_missing' do
|
16
|
+
it 'returns true for _changed? postfix' do
|
17
|
+
change_set.add_change(:name, nil, 'bar')
|
18
|
+
expect(change_set.name_changed?).to eq true
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'calls super for non-matched calls' do
|
22
|
+
expect { change_set.foobar }.to raise_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#respond_to?' do
|
27
|
+
it 'returns true for _changed? postfix' do
|
28
|
+
expect(change_set.respond_to?(:name_changed?)).to eq true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'calls super for non-matched calls' do
|
32
|
+
expect(change_set.respond_to?(:foobar)).to eq false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#add_change' do
|
37
|
+
it 'adds a new change' do
|
38
|
+
change_set.add_change(:name, 'foo', 'bar')
|
39
|
+
expect(change_set.get_change(:name)).to_not be_nil
|
40
|
+
expect(change_set.get_change(:name).current).to eq 'foo'
|
41
|
+
expect(change_set.get_change(:name).previous).to eq 'bar'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'allows nil for current' do
|
45
|
+
change_set.add_change(:name, nil, 'bar')
|
46
|
+
expect(change_set.get_change(:name)).to_not be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'allows nil for previous' do
|
50
|
+
change_set.add_change(:name, 'foo', nil)
|
51
|
+
expect(change_set.get_change(:name)).to_not be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'defaults previous to nil' do
|
55
|
+
change_set.add_change(:name, 'foo')
|
56
|
+
expect(change_set.get_change(:name)).to_not be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'requires a name' do
|
60
|
+
expect do
|
61
|
+
change_set.add_change(nil, 'foo', 'bar')
|
62
|
+
end.to raise_error
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'requires current as an argument' do
|
66
|
+
expect do
|
67
|
+
change_set.add_change(:name)
|
68
|
+
end.to raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'overrides a change' do
|
72
|
+
change_set.add_change(:name, 'foo', 'bar')
|
73
|
+
change_set.add_change(:name, 'bar', 'baz')
|
74
|
+
expect(change_set.get_change(:name).current).to eq 'bar'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#remove_change' do
|
79
|
+
before(:each) { change_set.add_change(:name, 'foo') }
|
80
|
+
|
81
|
+
it 'deletes an existing change' do
|
82
|
+
change_set.remove_change(:name)
|
83
|
+
expect(change_set.changed?(:name)).to be_falsey
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'returns truthy when removing an existing change' do
|
87
|
+
expect(change_set.remove_change(:name)).to be_truthy
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'returns falsey when removing an non-existing change' do
|
91
|
+
expect(change_set.remove_change(:foo)).to be_falsey
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#changed?' do
|
96
|
+
before(:each) do
|
97
|
+
change_set.add_change(:name, 'foo')
|
98
|
+
change_set.add_change(:age, 12)
|
99
|
+
change_set.add_change(:email, 'bar@biz.com')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'finds a single change' do
|
103
|
+
expect(change_set.changed?(:name)).to eq true
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'matches mutliple changes' do
|
107
|
+
expect(change_set.changed?([:name, :age])).to eq true
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'requires one change to match' do
|
111
|
+
expect(change_set.changed?([:age, :phone])).to eq true
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'requires all changes to match' do
|
115
|
+
expect(change_set.changed?([:name, :age, :phone], :all)).to eq false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#get_change' do
|
120
|
+
before(:each) { change_set.add_change(:name, 'foo') }
|
121
|
+
|
122
|
+
it 'returns the change when found' do
|
123
|
+
expect(change_set.get_change(:name).current).to eq 'foo'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'returns the Diffr::Change object' do
|
127
|
+
expect(change_set.get_change(:name)).to be_instance_of(Pedanco::Diffr::Change)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'returns empty Diffr::Change if not found' do
|
131
|
+
expect(change_set.get_change(:foo).current).to be_nil
|
132
|
+
expect(change_set.get_change(:foo).previous).to be_nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '_changed? matchers' do
|
137
|
+
before(:each) { change_set.add_change(:name, 'foo') }
|
138
|
+
|
139
|
+
it 'returns true for changes that match' do
|
140
|
+
expect(change_set.name_changed?).to be_truthy
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns false for changes that do not exists' do
|
144
|
+
expect(change_set.foo_changed?).to be_falsey
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#to_a' do
|
149
|
+
before(:each) do
|
150
|
+
change_set.add_change(:name, 'foo')
|
151
|
+
change_set.add_change(:age, 33, 32)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns the change when found' do
|
155
|
+
expect(change_set.to_a(:name)).to eq [nil, 'foo']
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns nil if not found' do
|
159
|
+
expect(change_set.to_a(:foo)).to eq []
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'maps out all the changes with no arguments' do
|
163
|
+
expect(change_set.to_a).to eq [[:name, [nil, "foo"]], [:age, [32, 33]]]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#to_hash' do
|
168
|
+
before(:each) { change_set.add_change(:name, 'foo') }
|
169
|
+
|
170
|
+
it 'returns the change when found' do
|
171
|
+
expect(change_set.to_hash).to include(name: [nil, 'foo'])
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
describe '#parse_changes' do
|
177
|
+
before(:each) { change_set.parse_changes('foo' => %w(baz bar), 'biff' => [10, 1]) }
|
178
|
+
|
179
|
+
it 'extracts the changes' do
|
180
|
+
expect(change_set.get_change(:foo).current).to eq 'bar'
|
181
|
+
expect(change_set.get_change('biff').previous).to eq 10
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Pedanco::Diffr::Change do
|
4
|
+
context '#initialize' do
|
5
|
+
it 'requires a name' do
|
6
|
+
expect { Pedanco::Diffr::Change.new }.to raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'default values' do
|
10
|
+
subject { Pedanco::Diffr::Change.new(:foo) }
|
11
|
+
|
12
|
+
it 'defaults current to nil' do
|
13
|
+
expect(subject.current).to be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'defaults previous to nil' do
|
17
|
+
expect(subject.previous).to be_nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'passing values' do
|
22
|
+
subject { Pedanco::Diffr::Change.new(:bar, 'baz', 'biff') }
|
23
|
+
|
24
|
+
it 'sets the name' do
|
25
|
+
expect(subject.name).to eq :bar
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sets the current' do
|
29
|
+
expect(subject.current).to eq 'baz'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the previous' do
|
33
|
+
expect(subject.previous).to eq 'biff'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context '#to_a' do
|
39
|
+
subject { Pedanco::Diffr::Change.new(:bar, 'baz', 'biff') }
|
40
|
+
|
41
|
+
it 'orders the array correctly' do
|
42
|
+
expect(subject.to_a).to eq %w(biff baz)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
require 'coveralls'
|
5
|
+
Coveralls.wear!
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
Bundler.setup
|
9
|
+
|
10
|
+
require 'pedanco/diffr' # and any other gems you need
|
11
|
+
|
12
|
+
RSpec.configure do |_config|
|
13
|
+
# some (optional) config here
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pedanco-diffr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Polanco
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "<="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "<="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.2'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.7'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rspec
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3.2'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.2'
|
75
|
+
description: |2
|
76
|
+
Pedanco::Diffr provides a change set management system for tracking changes
|
77
|
+
anywhere in the system. Diffr works with both ActiveRecord style changes and
|
78
|
+
provides a custom system so that it kind be used in a wider syntax without
|
79
|
+
requiring ActiveRecord or ActiveModel.
|
80
|
+
email:
|
81
|
+
- james@developmentarc.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- ".gitignore"
|
87
|
+
- ".rspec"
|
88
|
+
- ".rubocop.yml"
|
89
|
+
- CHANGELOG.md
|
90
|
+
- Gemfile
|
91
|
+
- Gemfile.lock
|
92
|
+
- LICENSE
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/pedanco/diffr.rb
|
96
|
+
- lib/pedanco/diffr/change.rb
|
97
|
+
- lib/pedanco/diffr/change_set.rb
|
98
|
+
- lib/pedanco/diffr/version.rb
|
99
|
+
- pedanco-diffr.gemspec
|
100
|
+
- spec/lib/pedanco/diffr/change_set_spec.rb
|
101
|
+
- spec/lib/pedanco/diffr/change_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
homepage: https://github.com/DevelopmentArc/pedanco-diffr
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.4.2
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Provides a change library for managing system changes.
|
127
|
+
test_files: []
|