dm-is-temporal 0.0.1 → 0.2.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.
- data/README.md +109 -32
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/dm-is-temporal/is/temporal.rb +38 -9
- data/spec/temporal_spec.rb +244 -4
- metadata +3 -4
- data/Gemfile +0 -0
data/README.md
CHANGED
@@ -6,12 +6,13 @@ DataMapper plugin implementing temporal patterns on DataMapper models.
|
|
6
6
|
These patterns are based on research by Martin Fowler, Richard Snodgrass and others. For more information follow these links:
|
7
7
|
|
8
8
|
+ [Temporal Patterns](http://martinfowler.com/eaaDev/timeNarrative.html)
|
9
|
-
|
10
9
|
+ [Developing Time-Oriented Database Applications in SQL](http://www.cs.arizona.edu/people/rts/publications.html)
|
11
10
|
|
12
11
|
Examples
|
13
12
|
---------
|
14
13
|
|
14
|
+
So lets assume you have a simple class. The plugin will automatically create some auxillary tables when you auto_migrate.
|
15
|
+
|
15
16
|
require 'rubygems'
|
16
17
|
require 'dm-core'
|
17
18
|
require 'dm-migrations'
|
@@ -32,38 +33,114 @@ Examples
|
|
32
33
|
end
|
33
34
|
|
34
35
|
DataMapper.auto_migrate!
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
m.foo
|
39
|
-
|
40
|
-
m.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
m.name
|
47
|
-
#=> 'hello'
|
48
|
-
|
49
|
-
m.foo
|
50
|
-
#= 2
|
51
|
-
|
52
|
-
m.foo = 42
|
53
|
-
|
36
|
+
|
37
|
+
You can create, modify and access it as normal:
|
38
|
+
|
39
|
+
m = MyModel.create(:name => 'start', :foo => 42)
|
40
|
+
|
41
|
+
m.bar = 'hello'
|
42
|
+
m.foo #= 42
|
43
|
+
m.name #=> 'start'
|
44
|
+
|
45
|
+
Or you can access it at different times (future or past):
|
46
|
+
|
54
47
|
old = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
55
|
-
|
56
|
-
|
57
|
-
m.foo
|
58
|
-
#=> 42
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
48
|
+
now = DateTime.now
|
49
|
+
|
50
|
+
m.at(old).foo #=> nil
|
51
|
+
m.at(now).foo #=> 42
|
52
|
+
|
53
|
+
But it really gets interesting when you modify it `at` different `DateTime`s
|
54
|
+
|
55
|
+
oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
56
|
+
nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
|
57
|
+
future = DateTime.parse('4712-01-01T00:00:00+00:00')
|
58
|
+
|
59
|
+
m.at(oldish).foo = 1
|
60
|
+
|
61
|
+
m.at(oldish).foo #=> 1
|
62
|
+
m.at(nowish).foo #=> 42
|
63
|
+
m.at(future).foo #=> 42
|
64
|
+
|
65
|
+
m.at(nowish).foo = 1024
|
66
|
+
|
67
|
+
m.at(oldish).foo #=> 1
|
68
|
+
m.at(nowish).foo #=> 1024
|
69
|
+
m.at(future).foo #=> 1024
|
70
|
+
|
71
|
+
m.at(future).foo = 3
|
72
|
+
|
73
|
+
m.at(oldish).foo #=> 1
|
74
|
+
m.at(nowish).foo #=> 1024
|
75
|
+
m.at(future).foo #=> 3
|
76
|
+
|
77
|
+
Remember that properties outside of the `is_temporal` block are not versioned. But you can read and write them though the `at(time)` method if you want:
|
78
|
+
|
79
|
+
m.at(now).name = "finished"
|
80
|
+
|
81
|
+
m.at(old).name #=> 'finished'
|
82
|
+
m.at(now).name #=> 'finished'
|
83
|
+
|
84
|
+
If you try to set a value at the same time as one you already set, it will overwrite the previous value (like non-temporal models). In future versions of dm-is-temporal you will be able to configure if this works or causes an error.
|
85
|
+
|
86
|
+
m.at(nowish).foo = 11
|
87
|
+
m.at(nowish).foo #=> 11
|
88
|
+
|
89
|
+
m.at(nowish).foo = 22
|
90
|
+
m.at(nowish).foo #=> 22
|
91
|
+
|
92
|
+
You can also update several properties with the same time in a block (I use `v` for "version" here)
|
93
|
+
|
94
|
+
|
95
|
+
m.at(nowish) do |v|
|
96
|
+
v.foo = 42
|
97
|
+
v.bar = "cat"
|
98
|
+
end
|
99
|
+
|
100
|
+
m.at(nowish).foo #=> 42
|
101
|
+
m.at(nowish).bar #=> 'cat'
|
102
|
+
|
103
|
+
|
104
|
+
How it works
|
105
|
+
-------------
|
106
|
+
Temporal patterns differ from versioning patterns (such as [dm-is-versioned](https://github.com/datamapper/dm-is-versioned))
|
107
|
+
in that every version of the temporal properties is a peer (even the most recent). Accessing temporal properties without the `at(time)` method
|
108
|
+
is just a convinience for `at(DateTime.now)`.
|
109
|
+
|
110
|
+
In addition, you have the ability inject versions at previous time-states (modifying history).
|
111
|
+
|
112
|
+
When you use the `is_temporal` form, the plugin will dynamically create a temporal version table. In the example above,
|
113
|
+
these two tables would be created:
|
114
|
+
|
115
|
+
# db.my_models table
|
116
|
+
---------------------------------------------
|
117
|
+
| id | name |
|
118
|
+
---------------------------------------------
|
119
|
+
| 1 | 'start' |
|
120
|
+
|
121
|
+
|
122
|
+
# db.my_model_temporal_versions table
|
123
|
+
-----------------------------------------------------------------------
|
124
|
+
| id | foo | bar | updated_at | my_model_id |
|
125
|
+
-----------------------------------------------------------------------
|
126
|
+
| 1 | '42' | null | DateTime | 1 |
|
127
|
+
| 2 | '1024' | null | DateTime | 1 |
|
128
|
+
| 3 | '1024' | 'hello' | DateTime | 1 |
|
129
|
+
|
130
|
+
Thanks
|
131
|
+
------
|
132
|
+
Thanks to the [dm-is-versioned](https://github.com/datamapper/dm-is-versioned) folks! I based a lot of my infrastructure
|
133
|
+
on that project.
|
134
|
+
|
135
|
+
TODO
|
136
|
+
------
|
137
|
+
|
138
|
+
+ MyClass.update (update all records for a model) doesn't work
|
139
|
+
+ Temporal Associations
|
140
|
+
+ Temporal Property pattern (i.e. multiple independent temporal properties per class)
|
141
|
+
+ Bi-temporality
|
142
|
+
+ Add a config flag that enables an error to be raised when attempting to rewrite existing versions
|
143
|
+
|
67
144
|
|
68
145
|
Copyright
|
69
146
|
----------
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.2.0
|
@@ -41,7 +41,7 @@ module DataMapper
|
|
41
41
|
|
42
42
|
version_model.property(:id, DataMapper::Property::Serial)
|
43
43
|
version_model.property(:updated_at, DataMapper::Property::DateTime)
|
44
|
-
version_model.before(:save) { self.updated_at
|
44
|
+
version_model.before(:save) { self.updated_at ||= DateTime.now }
|
45
45
|
version_model.instance_eval(&block)
|
46
46
|
|
47
47
|
const_set(:TemporalVersion, version_model)
|
@@ -70,11 +70,16 @@ module DataMapper
|
|
70
70
|
class_eval <<-RUBY
|
71
71
|
|
72
72
|
def self.update(options={})
|
73
|
-
raise '
|
73
|
+
raise "Updating all doesn't work yet"
|
74
|
+
# t_opts = __select_temporal_options__(options)
|
75
|
+
# raise "Can't update at temporal properties from class level yet." if !t_opts.empty?
|
76
|
+
# super.update(options)
|
74
77
|
end
|
75
78
|
|
76
|
-
def self.all(
|
77
|
-
|
79
|
+
def self.all(options={})
|
80
|
+
t_opts = __select_temporal_options__(options)
|
81
|
+
raise "Can't select all by temporal properties from class level yet." if !t_opts.empty?
|
82
|
+
super.all(options)
|
78
83
|
end
|
79
84
|
|
80
85
|
def self.create(options={})
|
@@ -87,8 +92,13 @@ module DataMapper
|
|
87
92
|
base
|
88
93
|
end
|
89
94
|
|
90
|
-
def at(context)
|
91
|
-
|
95
|
+
def at(context=DateTime.now, &block)
|
96
|
+
if block_given?
|
97
|
+
yield TemporalProxy.new(self, context)
|
98
|
+
else
|
99
|
+
# this is hokie. need to do better
|
100
|
+
@__at__ = context
|
101
|
+
end
|
92
102
|
self
|
93
103
|
end
|
94
104
|
|
@@ -141,10 +151,15 @@ module DataMapper
|
|
141
151
|
def create_temporal_writer(name)
|
142
152
|
class_eval <<-RUBY
|
143
153
|
def #{name}=(x)
|
154
|
+
at = @__at__
|
144
155
|
t = __version_for_context__
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
if t.nil?
|
157
|
+
t = TemporalVersion.create(:updated_at => at)
|
158
|
+
temporal_versions << t
|
159
|
+
elsif t.updated_at != at
|
160
|
+
t = TemporalVersion.create(t.attributes.merge(:id => nil, :updated_at => at))
|
161
|
+
temporal_versions << t
|
162
|
+
end
|
148
163
|
t.#{name} = x
|
149
164
|
self.save
|
150
165
|
#{name}
|
@@ -152,6 +167,20 @@ module DataMapper
|
|
152
167
|
RUBY
|
153
168
|
end
|
154
169
|
|
170
|
+
class TemporalProxy
|
171
|
+
# make this a blank slate
|
172
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
173
|
+
|
174
|
+
def initialize(proxied_object, context)
|
175
|
+
@proxied_object = proxied_object
|
176
|
+
@context = context
|
177
|
+
end
|
178
|
+
|
179
|
+
def method_missing(sym, *args, &block)
|
180
|
+
@proxied_object.at(@context).__send__(sym, *args, &block)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
155
184
|
module Migration
|
156
185
|
|
157
186
|
def auto_migrate!(repository_name = self.repository_name)
|
data/spec/temporal_spec.rb
CHANGED
@@ -4,10 +4,6 @@ require 'spec_helper'
|
|
4
4
|
class MyModel
|
5
5
|
include DataMapper::Resource
|
6
6
|
|
7
|
-
# def self.default_repository_name
|
8
|
-
# :test
|
9
|
-
# end
|
10
|
-
|
11
7
|
property :id, Serial
|
12
8
|
property :name, String
|
13
9
|
|
@@ -47,6 +43,98 @@ describe DataMapper::Is::Temporal do
|
|
47
43
|
MyModel.create
|
48
44
|
end
|
49
45
|
|
46
|
+
it "version has the right parent" do
|
47
|
+
subject.foo = 42
|
48
|
+
subject.instance_eval { puts self.temporal_versions[0].my_model_id.should == self.id}
|
49
|
+
end
|
50
|
+
|
51
|
+
it "update all still works for non-temporal properties" do
|
52
|
+
pending
|
53
|
+
MyModel.update(:name => 'all the same')
|
54
|
+
|
55
|
+
subject.name.should == 'all the same'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "select all still works for non-temporal properties" do
|
59
|
+
subject.name = 'looking for me!'
|
60
|
+
subject.save
|
61
|
+
all = MyModel.all(:name => 'looking for me!')
|
62
|
+
all.size.should == 1
|
63
|
+
all[0].name.should == 'looking for me!'
|
64
|
+
end
|
65
|
+
|
66
|
+
context "non-temporal properties" do
|
67
|
+
it "should work as normal" do
|
68
|
+
subject.name = 'foo'
|
69
|
+
subject.name.should == 'foo'
|
70
|
+
|
71
|
+
subject.name = 'bar'
|
72
|
+
subject.name.should == 'bar'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should work when accessed via at(time)" do
|
76
|
+
oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
77
|
+
nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
|
78
|
+
future = DateTime.parse('4712-01-01T00:00:00+00:00')
|
79
|
+
|
80
|
+
subject.at(oldish).name = 'foo'
|
81
|
+
|
82
|
+
subject.at(oldish).name.should == 'foo'
|
83
|
+
subject.at(nowish).name.should == 'foo'
|
84
|
+
subject.at(future).name.should == 'foo'
|
85
|
+
|
86
|
+
subject.at(nowish).name = 'bar'
|
87
|
+
|
88
|
+
subject.at(oldish).name.should == 'bar'
|
89
|
+
subject.at(nowish).name.should == 'bar'
|
90
|
+
subject.at(future).name.should == 'bar'
|
91
|
+
|
92
|
+
subject.name = 'rat'
|
93
|
+
|
94
|
+
subject.at(oldish).name.should == 'rat'
|
95
|
+
subject.at(nowish).name.should == 'rat'
|
96
|
+
subject.at(future).name.should == 'rat'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "setting temporal properties" do
|
101
|
+
it "works" do
|
102
|
+
oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
103
|
+
nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
|
104
|
+
future = DateTime.parse('4712-01-01T00:00:00+00:00')
|
105
|
+
|
106
|
+
subject.at(oldish).foo = 42
|
107
|
+
|
108
|
+
subject.at(oldish).foo.should == 42
|
109
|
+
subject.at(nowish).foo.should == 42
|
110
|
+
subject.at(future).foo.should == 42
|
111
|
+
|
112
|
+
subject.at(nowish).foo = 1024
|
113
|
+
|
114
|
+
subject.at(oldish).foo.should == 42
|
115
|
+
subject.at(nowish).foo.should == 1024
|
116
|
+
subject.at(future).foo.should == 1024
|
117
|
+
|
118
|
+
subject.at(future).foo = 3
|
119
|
+
|
120
|
+
subject.at(oldish).foo.should == 42
|
121
|
+
subject.at(nowish).foo.should == 1024
|
122
|
+
subject.at(future).foo.should == 3
|
123
|
+
|
124
|
+
subject.instance_eval { puts self.temporal_versions.size.should == 3}
|
125
|
+
end
|
126
|
+
|
127
|
+
it "and rewriting works" do
|
128
|
+
now = DateTime.parse('2011-03-01T00:00:00+00:00')
|
129
|
+
|
130
|
+
subject.at(now).foo = 42
|
131
|
+
subject.at(now).foo.should == 42
|
132
|
+
|
133
|
+
subject.at(now).foo = 1
|
134
|
+
subject.at(now).foo.should == 1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
50
138
|
context "when at context" do
|
51
139
|
it "returns old values" do
|
52
140
|
subject.foo.should == nil
|
@@ -58,6 +146,12 @@ describe DataMapper::Is::Temporal do
|
|
58
146
|
|
59
147
|
subject.at(old).foo.should == nil
|
60
148
|
subject.at(now).foo.should == 42
|
149
|
+
|
150
|
+
subject.foo = 1024
|
151
|
+
subject.foo.should == 1024
|
152
|
+
|
153
|
+
subject.at(old).foo.should == nil
|
154
|
+
subject.at(now).foo.should == 42
|
61
155
|
end
|
62
156
|
end
|
63
157
|
|
@@ -67,6 +161,15 @@ describe DataMapper::Is::Temporal do
|
|
67
161
|
subject.foo = 42
|
68
162
|
subject.foo.should == 42
|
69
163
|
end
|
164
|
+
|
165
|
+
it "returns 'same' for bar" do
|
166
|
+
subject.bar = 'same'
|
167
|
+
subject.bar.should == 'same'
|
168
|
+
subject.foo.should == nil
|
169
|
+
subject.foo = 42
|
170
|
+
subject.foo.should == 42
|
171
|
+
subject.bar.should == 'same'
|
172
|
+
end
|
70
173
|
end
|
71
174
|
|
72
175
|
context "when bar is 'hello'" do
|
@@ -166,5 +269,142 @@ describe DataMapper::Is::Temporal do
|
|
166
269
|
end
|
167
270
|
end
|
168
271
|
end
|
272
|
+
|
273
|
+
context "when multi setting" do
|
274
|
+
it "works with different times" do
|
275
|
+
oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
276
|
+
nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
|
277
|
+
future = DateTime.parse('4712-01-01T00:00:00+00:00')
|
278
|
+
|
279
|
+
subject.at(oldish) do |s|
|
280
|
+
s.foo = 42
|
281
|
+
s.bar = "cat"
|
282
|
+
end
|
283
|
+
|
284
|
+
subject.at(oldish).foo.should == 42
|
285
|
+
subject.at(nowish).foo.should == 42
|
286
|
+
subject.at(future).foo.should == 42
|
287
|
+
|
288
|
+
subject.at(oldish).bar.should == "cat"
|
289
|
+
subject.at(nowish).bar.should == "cat"
|
290
|
+
subject.at(future).bar.should == "cat"
|
291
|
+
|
292
|
+
subject.at(nowish) do |s|
|
293
|
+
s.foo = 1024
|
294
|
+
s.bar = "dog"
|
295
|
+
end
|
296
|
+
|
297
|
+
subject.at(oldish).foo.should == 42
|
298
|
+
subject.at(nowish).foo.should == 1024
|
299
|
+
subject.at(future).foo.should == 1024
|
300
|
+
|
301
|
+
subject.at(oldish).bar.should == "cat"
|
302
|
+
subject.at(nowish).bar.should == "dog"
|
303
|
+
subject.at(future).bar.should == "dog"
|
304
|
+
|
305
|
+
subject.at(future) do |s|
|
306
|
+
s.foo = 3
|
307
|
+
s.bar = "rat"
|
308
|
+
end
|
309
|
+
|
310
|
+
subject.at(oldish).foo.should == 42
|
311
|
+
subject.at(nowish).foo.should == 1024
|
312
|
+
subject.at(future).foo.should == 3
|
313
|
+
|
314
|
+
subject.at(oldish).bar.should == "cat"
|
315
|
+
subject.at(nowish).bar.should == "dog"
|
316
|
+
subject.at(future).bar.should == "rat"
|
317
|
+
|
318
|
+
subject.instance_eval { puts self.temporal_versions.size.should == 3}
|
319
|
+
end
|
320
|
+
|
321
|
+
it "works with different times and non-temporal properties" do
|
322
|
+
oldish = DateTime.parse('-4712-01-01T00:00:00+00:00')
|
323
|
+
nowish = DateTime.parse('2011-03-01T00:00:00+00:00')
|
324
|
+
future = DateTime.parse('4712-01-01T00:00:00+00:00')
|
325
|
+
|
326
|
+
subject.at(oldish) do |s|
|
327
|
+
s.name = "same"
|
328
|
+
s.foo = 42
|
329
|
+
s.bar = "cat"
|
330
|
+
end
|
331
|
+
|
332
|
+
subject.at(oldish).foo.should == 42
|
333
|
+
subject.at(nowish).foo.should == 42
|
334
|
+
subject.at(future).foo.should == 42
|
335
|
+
|
336
|
+
subject.at(oldish).bar.should == "cat"
|
337
|
+
subject.at(nowish).bar.should == "cat"
|
338
|
+
subject.at(future).bar.should == "cat"
|
339
|
+
|
340
|
+
subject.at(oldish).name.should == "same"
|
341
|
+
subject.at(nowish).name.should == "same"
|
342
|
+
subject.at(future).name.should == "same"
|
343
|
+
|
344
|
+
subject.at(nowish) do |s|
|
345
|
+
s.name = "every"
|
346
|
+
s.foo = 1024
|
347
|
+
s.bar = "dog"
|
348
|
+
end
|
349
|
+
|
350
|
+
subject.at(oldish).foo.should == 42
|
351
|
+
subject.at(nowish).foo.should == 1024
|
352
|
+
subject.at(future).foo.should == 1024
|
353
|
+
|
354
|
+
subject.at(oldish).bar.should == "cat"
|
355
|
+
subject.at(nowish).bar.should == "dog"
|
356
|
+
subject.at(future).bar.should == "dog"
|
357
|
+
|
358
|
+
subject.at(oldish).name.should == "every"
|
359
|
+
subject.at(nowish).name.should == "every"
|
360
|
+
subject.at(future).name.should == "every"
|
361
|
+
|
362
|
+
subject.at(future) do |s|
|
363
|
+
s.name = "time"
|
364
|
+
s.foo = 3
|
365
|
+
s.bar = "rat"
|
366
|
+
end
|
367
|
+
|
368
|
+
subject.at(oldish).foo.should == 42
|
369
|
+
subject.at(nowish).foo.should == 1024
|
370
|
+
subject.at(future).foo.should == 3
|
371
|
+
|
372
|
+
subject.at(oldish).bar.should == "cat"
|
373
|
+
subject.at(nowish).bar.should == "dog"
|
374
|
+
subject.at(future).bar.should == "rat"
|
375
|
+
|
376
|
+
subject.at(oldish).name.should == "time"
|
377
|
+
subject.at(nowish).name.should == "time"
|
378
|
+
subject.at(future).name.should == "time"
|
379
|
+
|
380
|
+
subject.instance_eval { puts self.temporal_versions.size.should == 3}
|
381
|
+
end
|
382
|
+
|
383
|
+
it "works with no time (i.e. now)" do
|
384
|
+
subject.at do |s|
|
385
|
+
s.foo = 42
|
386
|
+
s.bar = "cat"
|
387
|
+
end
|
388
|
+
|
389
|
+
subject.foo.should == 42
|
390
|
+
subject.bar.should == "cat"
|
391
|
+
|
392
|
+
subject.instance_eval { puts self.temporal_versions.size.should == 1}
|
393
|
+
end
|
394
|
+
|
395
|
+
it "works with no time (i.e. now)" do
|
396
|
+
subject.at do |s|
|
397
|
+
s.name = "foobar"
|
398
|
+
s.foo = 42
|
399
|
+
s.bar = "cat"
|
400
|
+
end
|
401
|
+
|
402
|
+
subject.foo.should == 42
|
403
|
+
subject.bar.should == "cat"
|
404
|
+
subject.name.should == "foobar"
|
405
|
+
|
406
|
+
subject.instance_eval { puts self.temporal_versions.size.should == 1}
|
407
|
+
end
|
408
|
+
end
|
169
409
|
end
|
170
410
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 2
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.1
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Joe Kutner
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-03-
|
17
|
+
date: 2011-03-22 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -28,7 +28,6 @@ extra_rdoc_files:
|
|
28
28
|
- LICENSE.txt
|
29
29
|
- README.md
|
30
30
|
files:
|
31
|
-
- Gemfile
|
32
31
|
- LICENSE.txt
|
33
32
|
- README.md
|
34
33
|
- Rakefile
|
data/Gemfile
DELETED
File without changes
|