snaps 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +157 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/snaps/application.js +13 -0
  6. data/app/assets/stylesheets/snaps/application.css +15 -0
  7. data/app/controllers/snaps/application_controller.rb +4 -0
  8. data/app/helpers/snaps/application_helper.rb +4 -0
  9. data/app/models/concerns/snaps/suppressor.rb +55 -0
  10. data/app/models/snaps/tag.rb +45 -0
  11. data/app/views/layouts/snaps/application.html.erb +14 -0
  12. data/config/routes.rb +2 -0
  13. data/db/migrate/20150321131740_create_snaps_tags.rb +12 -0
  14. data/lib/snaps.rb +61 -0
  15. data/lib/snaps/engine.rb +13 -0
  16. data/lib/snaps/snapshot.rb +42 -0
  17. data/lib/snaps/version.rb +3 -0
  18. data/lib/tasks/snaps_tasks.rake +4 -0
  19. data/spec/dummy/README.rdoc +28 -0
  20. data/spec/dummy/Rakefile +6 -0
  21. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  22. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/models/post.rb +3 -0
  26. data/spec/dummy/app/models/post_with_sections.rb +7 -0
  27. data/spec/dummy/app/models/section.rb +5 -0
  28. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  29. data/spec/dummy/bin/bundle +3 -0
  30. data/spec/dummy/bin/rails +4 -0
  31. data/spec/dummy/bin/rake +4 -0
  32. data/spec/dummy/bin/setup +29 -0
  33. data/spec/dummy/config.ru +4 -0
  34. data/spec/dummy/config/application.rb +26 -0
  35. data/spec/dummy/config/boot.rb +5 -0
  36. data/spec/dummy/config/database.yml +25 -0
  37. data/spec/dummy/config/environment.rb +5 -0
  38. data/spec/dummy/config/environments/development.rb +41 -0
  39. data/spec/dummy/config/environments/production.rb +79 -0
  40. data/spec/dummy/config/environments/test.rb +42 -0
  41. data/spec/dummy/config/initializers/assets.rb +11 -0
  42. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  43. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  44. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  45. data/spec/dummy/config/initializers/inflections.rb +16 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  47. data/spec/dummy/config/initializers/session_store.rb +3 -0
  48. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  49. data/spec/dummy/config/locales/en.yml +23 -0
  50. data/spec/dummy/config/routes.rb +4 -0
  51. data/spec/dummy/config/secrets.yml +22 -0
  52. data/spec/dummy/db/development.sqlite3 +0 -0
  53. data/spec/dummy/db/migrate/20150321145225_create_posts.rb +11 -0
  54. data/spec/dummy/db/migrate/20150323173024_create_sections.rb +11 -0
  55. data/spec/dummy/db/migrate/20150324074047_create_snaps_tags.snaps.rb +13 -0
  56. data/spec/dummy/db/schema.rb +41 -0
  57. data/spec/dummy/db/test.sqlite3 +0 -0
  58. data/spec/dummy/log/development.log +36 -0
  59. data/spec/dummy/log/test.log +472 -0
  60. data/spec/dummy/public/404.html +67 -0
  61. data/spec/dummy/public/422.html +67 -0
  62. data/spec/dummy/public/500.html +66 -0
  63. data/spec/dummy/public/favicon.ico +0 -0
  64. data/spec/dummy/spec/factories/posts.rb +11 -0
  65. data/spec/dummy/spec/factories/sections.rb +8 -0
  66. data/spec/integration/revision_spec.rb +214 -0
  67. data/spec/integration/revision_spec.rb! +8 -0
  68. data/spec/spec_helper.rb +26 -0
  69. data/spec/support/config/timecop.rb +11 -0
  70. metadata +247 -0
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,11 @@
1
+ FactoryGirl.define do
2
+ factory :post do
3
+ title "MyString"
4
+ body "MyText"
5
+ end
6
+
7
+ factory :post_with_sections do
8
+ title "MyString"
9
+ body "MyText"
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :section do
3
+ post_id 1
4
+ perma_id 1
5
+ title "MyString"
6
+ end
7
+
8
+ end
@@ -0,0 +1,214 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Snaps.revision" do
4
+ it "ensures a perma_id" do
5
+ post = create(:post)
6
+ expect(post.perma_id).to be_present
7
+
8
+ post2 = create(:post)
9
+ expect(post2.perma_id).to(be > post.perma_id)
10
+ end
11
+
12
+ it "creates a default tag when a new revisioned record is created" do
13
+ post = create(:post)
14
+
15
+ tag = Snaps::Tag.find_by(record: post)
16
+
17
+ expect(tag.tag).to eq("draft")
18
+ end
19
+
20
+ describe '#snapshot!' do
21
+ it "creates new record" do
22
+ post = create(:post, title: 'ein titel')
23
+ post.snapshot!
24
+ expect(Post.all.length).to eq(2)
25
+
26
+ new_post = Post.last
27
+
28
+ expect(post.perma_id).to eq(new_post.perma_id)
29
+ expect(post.id).not_to eq(new_post.id)
30
+ expect(new_post.title).to eq(post.title)
31
+ end
32
+
33
+ it 'yields unsaved record' do
34
+ post = create(:post, title: 'ein titel')
35
+
36
+ is_new_record = :not_called
37
+
38
+ snapshot = post.snapshot! do |s|
39
+ is_new_record = s.new_record?
40
+ s.title = 'zwei titel'
41
+ end
42
+
43
+ expect(is_new_record).to be(true)
44
+ expect(snapshot.reload.title).to eq('zwei titel')
45
+ end
46
+
47
+ it 'does not create a default tag' do
48
+ post = create(:post, title: 'ein titel')
49
+
50
+ expect {
51
+ post.snapshot!
52
+ }.not_to change { Snaps::Tag.count }
53
+ end
54
+
55
+ it 'can tag the new record' do
56
+ post = create(:post, title: 'ein titel')
57
+ post_snapshot = post.snapshot!(tag: :published)
58
+
59
+ expect(Post.with_snaps_tag(:published).first).to eq(post_snapshot)
60
+ end
61
+
62
+ it 'snapshots the components of the record' do
63
+ post = create(:post_with_sections, title: 'ein titel')
64
+ section = post.sections.create!(title: 'section')
65
+
66
+ post_snapshot = post.snapshot!
67
+
68
+ expect(post_snapshot.sections.length).to eq(1)
69
+ expect(post_snapshot.sections.first.title).to eq(section.title)
70
+ expect(post_snapshot.sections.first.id).not_to eq(section.id)
71
+ end
72
+
73
+ end
74
+
75
+ describe '#snaps_tag!' do
76
+ it "creates new record in snaps_tags" do
77
+ post = create(:post)
78
+ tag = post.snaps_tag!(:published)
79
+
80
+ expect(tag.record).to eq(post)
81
+ expect(tag.tag).to eq("published")
82
+ end
83
+
84
+ it "supersedes record tagged previousy with the same tag" do
85
+ post = create(:post)
86
+ new_post = post.snapshot!
87
+ tag = post.snaps_tag!(:published)
88
+ new_post.snaps_tag!(:published)
89
+
90
+ tag.reload
91
+
92
+ expect(tag.superseded_at).to be_present
93
+ end
94
+
95
+ it 'does not supersede tags with different tagnames' do
96
+ post = create(:post)
97
+ new_post = post.snapshot!
98
+ tag = post.snaps_tag!(:published)
99
+ new_post.snaps_tag!(:foo)
100
+
101
+ tag.reload
102
+
103
+ expect(tag.superseded_at).to be_blank
104
+ end
105
+
106
+ it 'does not change already superseded tags' do
107
+ post = create(:post)
108
+ superseded_tag = post.snaps_tag!(:published)
109
+
110
+ post_snapshot = post.snapshot!
111
+ post_snapshot.snaps_tag!(:published)
112
+
113
+ Timecop.freeze 2.weeks.from_now do
114
+ post_snapshot = post.snapshot!
115
+ post_snapshot.snaps_tag!(:published)
116
+
117
+ superseded_tag.reload
118
+
119
+ expect(superseded_tag.superseded_at).to eq(2.weeks.ago)
120
+ end
121
+ end
122
+ end
123
+
124
+ describe '#snaps_untag!' do
125
+ def tag_count_for(record, tag)
126
+ Snaps::Tag.for_all_revisions_of(record).with_name(tag).current.count
127
+ end
128
+
129
+ it "supersedes tag on a record" do
130
+ post = create(:post, title: 'ein titel')
131
+
132
+ expect {
133
+ post.snaps_untag!(:draft)
134
+ }.to change { tag_count_for(post, :draft) }.by(-1)
135
+ end
136
+
137
+ it "does not supersede other tags" do
138
+ post = create(:post, title: 'ein titel')
139
+ post.snapshot!(tag: :published)
140
+
141
+ expect {
142
+ post.snaps_untag!(:draft)
143
+ }.not_to change { tag_count_for(post, :published) }
144
+ end
145
+
146
+ it "does not supersede other records" do
147
+ post1 = create(:post, title: 'ein titel')
148
+ post2 = create(:post, title: 'ein titel')
149
+
150
+ expect {
151
+ post1.snaps_untag!(:draft)
152
+ }.not_to change { tag_count_for(post2, :draft) }
153
+ end
154
+ end
155
+
156
+ describe '#snaps_revisions' do
157
+ it 'returns all revisions of record' do
158
+ post = create(:post)
159
+ post_snapshot = post.snapshot!
160
+
161
+ result = post.snaps_revisions
162
+
163
+ expect(result.length).to eq(2)
164
+ expect(result).to include(post_snapshot)
165
+ expect(result).to include(post)
166
+ end
167
+ end
168
+
169
+ describe '.with_snaps_tag' do
170
+ it "returns list of records joined with snaps_tags" do
171
+ post = create(:post)
172
+ post.snaps_tag!(:published)
173
+
174
+ expect(Post.with_snaps_tag(:published).length).to eq(1)
175
+ end
176
+
177
+ it "does not return records with differnt tags" do
178
+ post = create(:post)
179
+ post.snaps_tag!(:published)
180
+
181
+ expect(Post.with_snaps_tag(:other).length).to eq(0)
182
+ end
183
+
184
+ it 'does not return untagged records' do
185
+ create(:post)
186
+ expect(Post.with_snaps_tag(:published).length).to eq(0)
187
+ end
188
+
189
+ it "only returns current revision of record" do
190
+ post = create(:post)
191
+ post_snapshot = post.snapshot!
192
+ post.snaps_tag!(:published)
193
+ post_snapshot.snaps_tag!(:published)
194
+
195
+ expect(Post.with_snaps_tag(:published).length).to eq(1)
196
+ expect(Post.with_snaps_tag(:published).first).to eq(post_snapshot)
197
+ end
198
+
199
+ context 'with :all_revisions option' do
200
+ it "returns all revisions of record" do
201
+ post = create(:post)
202
+ post_snapshot = post.snapshot!
203
+ post.snaps_tag!(:published)
204
+ post_snapshot.snaps_tag!(:published)
205
+
206
+ result = Post.with_snaps_tag(:published, all_revisions: true)
207
+
208
+ expect(result.length).to eq(2)
209
+ expect(result).to include(post_snapshot)
210
+ expect(result).to include(post)
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Snaps.revision" do
4
+ it "provides #with_snaps_tags method" do
5
+ post = xcreate(:post)
6
+ expect(post).not_to be_new_record
7
+ end
8
+ end