pigeons 0.0.1pre
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +676 -0
- data/README.md +29 -0
- data/Rakefile +10 -0
- data/lib/generators/pigeons/install_generator.rb +42 -0
- data/lib/generators/pigeons/templates/pigeon_letter.rb +30 -0
- data/lib/generators/pigeons/templates/pigeon_letter_migration.rb +16 -0
- data/lib/generators/pigeons/templates/pigeon_mailer.rb +8 -0
- data/lib/generators/pigeons/templates/pigeons.json +5 -0
- data/lib/pigeons/checks.rb +98 -0
- data/lib/pigeons/elements.rb +35 -0
- data/lib/pigeons/errors.rb +8 -0
- data/lib/pigeons/extensions.rb +34 -0
- data/lib/pigeons/logger.rb +15 -0
- data/lib/pigeons/pigeons.rb +156 -0
- data/lib/pigeons/pigeons_tasks.rb +8 -0
- data/lib/pigeons/scope.rb +205 -0
- data/lib/pigeons/version.rb +3 -0
- data/lib/pigeons.rb +10 -0
- data/pigeons.gemspec +29 -0
- data/tasks/pigeons.rake +142 -0
- data/test/test_helper.rb +143 -0
- data/test/test_pigeons.rb +429 -0
- metadata +200 -0
@@ -0,0 +1,429 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
# To test:
|
7
|
+
# 1) Bases (check)
|
8
|
+
# 2) Conditions
|
9
|
+
# 3) Flights (Cohorts)
|
10
|
+
# 4) Bit more complexity?
|
11
|
+
# 5) Send
|
12
|
+
# 6) "signup", aye!
|
13
|
+
|
14
|
+
class PigeonExtensions < Pigeons::Extension
|
15
|
+
|
16
|
+
# Match an ownership
|
17
|
+
condition /^own \s+ (?<property>\w+)/ix do |scope, match|
|
18
|
+
# p [ "Pigeon Extension::Condition", "#{item} matched conditional" ]
|
19
|
+
|
20
|
+
scope.where([ "property LIKE ?", "%#{match[:property].singularize}%" ])
|
21
|
+
end
|
22
|
+
|
23
|
+
condition /eaten/i do |scope, match|
|
24
|
+
scope.where(eaten: true)
|
25
|
+
end
|
26
|
+
|
27
|
+
condition /slept/i do |scope, match|
|
28
|
+
scope.where(slept: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
base /^(?<color>\w+) \s+ dragon[s]?$/ix do |match|
|
32
|
+
Dragon.scoped.where(color: match[:color])
|
33
|
+
end
|
34
|
+
|
35
|
+
base /^the pixies$/i do |match|
|
36
|
+
Pixie.scoped
|
37
|
+
end
|
38
|
+
|
39
|
+
event /^hatching$/ do |scope, time, match|
|
40
|
+
scope.where("hatched_at < ?", time)
|
41
|
+
end
|
42
|
+
|
43
|
+
event /^defeating (?<orc_name>.*)$/ do |scope, time, match|
|
44
|
+
|
45
|
+
source_arel = scope.arel_table
|
46
|
+
battle_arel = Battle.arel_table
|
47
|
+
orc_arel = Orc.arel_table
|
48
|
+
|
49
|
+
scope.where(
|
50
|
+
Battle.joins(:orc).where(
|
51
|
+
# match to dragon
|
52
|
+
battle_arel[:dragon_id].eq(source_arel[:id]).and(
|
53
|
+
orc_arel[:name].eq(match[:orc_name])
|
54
|
+
).and(
|
55
|
+
battle_arel[:created_at].lt(time)
|
56
|
+
).and(
|
57
|
+
battle_arel[:is_dragon_victor].eq(true)
|
58
|
+
)
|
59
|
+
).exists
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
event /^leveling up$/ do |scope, time, match|
|
64
|
+
|
65
|
+
source_arel = scope.arel_table
|
66
|
+
level_arel = Level.arel_table
|
67
|
+
|
68
|
+
scope.where(
|
69
|
+
Level.where(
|
70
|
+
level_arel[:pixie_id].eq(source_arel[:id]).and(
|
71
|
+
level_arel[:created_at].lt(time)
|
72
|
+
)
|
73
|
+
).exists
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class TestPigeons < Test::Unit::TestCase
|
80
|
+
include Mocha
|
81
|
+
|
82
|
+
context "when the config has issues" do
|
83
|
+
should "raise unknown letter type" do
|
84
|
+
Pigeons::Settings.pigeon_config_file = "ignored"
|
85
|
+
File.stubs(:exists? => true)
|
86
|
+
|
87
|
+
# Nonexistant
|
88
|
+
PigeonMailer.stubs(respond_to?: false)
|
89
|
+
File.stubs(:read => { flights: { aflight: [ "dragon gets a nonexistent letter" ] } }.to_json)
|
90
|
+
|
91
|
+
assert_raise Pigeons::PigeonError::PigeonFlightConfigError do
|
92
|
+
Pigeons.assemble
|
93
|
+
end
|
94
|
+
|
95
|
+
# No idea what class
|
96
|
+
PigeonMailer.stubs(respond_to?: true)
|
97
|
+
File.stubs(:read => { flights: { aflight: [ "rhinos gets a welcome letter" ] } }.to_json)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# These test flights will get run and we'll check the resultant SQL
|
102
|
+
# These are, obstensibly end-to-end tests
|
103
|
+
test_flights = [
|
104
|
+
# Test basic letter
|
105
|
+
{
|
106
|
+
name: "all dragons get welcome",
|
107
|
+
config: { flights: { aflight: [ "dragons gets a welcome letter" ] } },
|
108
|
+
# Simply a check to make sure we didn't send this letter type
|
109
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "welcome") ]
|
110
|
+
},
|
111
|
+
# Test a different base
|
112
|
+
{
|
113
|
+
name: "all orcs get goodbye",
|
114
|
+
config: { flights: { aflight: [ "orcs get a goodbye letter" ] } },
|
115
|
+
expected: [ letter_not_exists(simple_scope(Orc.scoped), "goodbye") ]
|
116
|
+
},
|
117
|
+
# Test relative time (after hours)
|
118
|
+
{
|
119
|
+
name: "all dragons get welcome after signup, hours",
|
120
|
+
config: { flights: { aflight: [ "dragons gets a welcome letter 24 hours after signup" ] } },
|
121
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "welcome").where("created_at < ?", 24.hours.ago) ]
|
122
|
+
},
|
123
|
+
# Test relative time (after days)
|
124
|
+
{
|
125
|
+
name: "all dragons get welcome after signup, days",
|
126
|
+
config: { flights: { aflight: [ "dragons gets a welcome letter 2 days after signup" ] } },
|
127
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "welcome").where("created_at < ?", 2.days.ago) ]
|
128
|
+
},
|
129
|
+
# Test recurring (every)
|
130
|
+
{
|
131
|
+
name: "all dragons get welcome after signup every weeks",
|
132
|
+
config: { flights: { aflight: [ "dragons get a welcome letter every 3 weeks after signup" ] } },
|
133
|
+
expected: [ simple_scope(Dragon.scoped).where(
|
134
|
+
PigeonLetter.where(
|
135
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
136
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
137
|
+
).and(
|
138
|
+
PigeonLetter.arel_table[:created_at].gt(3.weeks.ago)
|
139
|
+
).and(
|
140
|
+
PigeonLetter.arel_table[:letter_type].eq("welcome")
|
141
|
+
)
|
142
|
+
).exists.not
|
143
|
+
).where("created_at < ?", 3.weeks.ago) ]
|
144
|
+
},
|
145
|
+
# Now, let's try an after
|
146
|
+
{
|
147
|
+
name: "all dragons get welcomed then fired",
|
148
|
+
config: { flights: { aflight: [ "dragons get a welcome letter 2 seconds after signup",
|
149
|
+
"then get a fired letter 2 hours after that" ] } },
|
150
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "welcome").where("created_at < ?", 2.seconds.ago),
|
151
|
+
letter_not_exists(simple_scope(Dragon.scoped), "fired").where(
|
152
|
+
PigeonLetter.where(
|
153
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
154
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
155
|
+
).and(
|
156
|
+
PigeonLetter.arel_table[:letter_type].in(["welcome"])
|
157
|
+
)
|
158
|
+
).exists
|
159
|
+
).where(
|
160
|
+
PigeonLetter.where(
|
161
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
162
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
163
|
+
).and(
|
164
|
+
PigeonLetter.arel_table[:letter_type].in(["welcome"])
|
165
|
+
).and(
|
166
|
+
PigeonLetter.arel_table[:created_at].gt(2.hours.ago)
|
167
|
+
)
|
168
|
+
).exists.not
|
169
|
+
)
|
170
|
+
]
|
171
|
+
},
|
172
|
+
# Let's add a condition
|
173
|
+
{
|
174
|
+
name: "all dragons who own lairs get taxed",
|
175
|
+
config: { flights: { aflight: [ "dragons who own lairs get a tax letter every year" ] } },
|
176
|
+
expected: [ simple_scope(Dragon.scoped).where(
|
177
|
+
PigeonLetter.where(
|
178
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
179
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
180
|
+
).and(
|
181
|
+
PigeonLetter.arel_table[:created_at].gt(1.year.ago)
|
182
|
+
).and(
|
183
|
+
PigeonLetter.arel_table[:letter_type].eq("tax")
|
184
|
+
)
|
185
|
+
).exists.not
|
186
|
+
).where("property LIKE '%lair%'") ]
|
187
|
+
},
|
188
|
+
# Let's add a simple base
|
189
|
+
{
|
190
|
+
name: "all pixies",
|
191
|
+
config: { flights: { aflight: [ "the pixies get a punk rock letter" ] } },
|
192
|
+
expected: [ letter_not_exists(simple_scope(Pixie.scoped), "punk_rock") ]
|
193
|
+
},
|
194
|
+
# Let's add a complex base
|
195
|
+
{
|
196
|
+
name: "red dragon sadness",
|
197
|
+
config: { flights: { aflight: [ "red dragons get a hate letter every day after signup" ] } },
|
198
|
+
expected: [ simple_scope(Dragon.scoped.where(color: 'red')).where(
|
199
|
+
PigeonLetter.where(
|
200
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
201
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
202
|
+
).and(
|
203
|
+
PigeonLetter.arel_table[:created_at].gt(1.day.ago)
|
204
|
+
).and(
|
205
|
+
PigeonLetter.arel_table[:letter_type].eq("hate")
|
206
|
+
)
|
207
|
+
).exists.not
|
208
|
+
).where(["created_at < ?", 1.day.ago]) ]
|
209
|
+
},
|
210
|
+
# Now let's test complex running conditions
|
211
|
+
{
|
212
|
+
name: "running conditions",
|
213
|
+
config: { flights: { aflight: [ "dragons get a hello letter after signup",
|
214
|
+
"then who've eaten get a food letter 1 hour after that",
|
215
|
+
"then who've slept get a sleep letter after that",
|
216
|
+
"then get a goodnight letter 30 minutes after that",
|
217
|
+
"then gets a nightcap letter" ] } }, # Note, these running conditions are complicated
|
218
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "hello").where("created_at < ?", Time.now),
|
219
|
+
letter_not_exists(simple_scope(Dragon.scoped).where(eaten: true), "food").where(
|
220
|
+
PigeonLetter.where(
|
221
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
222
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
223
|
+
).and(
|
224
|
+
PigeonLetter.arel_table[:letter_type].in(["hello"])
|
225
|
+
)
|
226
|
+
).exists
|
227
|
+
).where(
|
228
|
+
PigeonLetter.where(
|
229
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
230
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
231
|
+
).and(
|
232
|
+
PigeonLetter.arel_table[:letter_type].in(["hello"])
|
233
|
+
).and(
|
234
|
+
PigeonLetter.arel_table[:created_at].gt(1.hour.ago)
|
235
|
+
)
|
236
|
+
).exists.not
|
237
|
+
),
|
238
|
+
letter_not_exists(simple_scope(Dragon.scoped).where(slept: true), "sleep").where(
|
239
|
+
PigeonLetter.where(
|
240
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
241
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
242
|
+
).and(
|
243
|
+
PigeonLetter.arel_table[:letter_type].in(["hello","food"])
|
244
|
+
)
|
245
|
+
).exists
|
246
|
+
).where(
|
247
|
+
PigeonLetter.where(
|
248
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
249
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
250
|
+
).and(
|
251
|
+
PigeonLetter.arel_table[:letter_type].in(["hello","food"])
|
252
|
+
).and(
|
253
|
+
PigeonLetter.arel_table[:created_at].gt(Time.now)
|
254
|
+
)
|
255
|
+
).exists.not
|
256
|
+
),
|
257
|
+
letter_not_exists(simple_scope(Dragon.scoped), "goodnight").where(
|
258
|
+
PigeonLetter.where(
|
259
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
260
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
261
|
+
).and(
|
262
|
+
PigeonLetter.arel_table[:letter_type].in(["hello","food","sleep"])
|
263
|
+
)
|
264
|
+
).exists
|
265
|
+
).where(
|
266
|
+
PigeonLetter.where(
|
267
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
268
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
269
|
+
).and(
|
270
|
+
PigeonLetter.arel_table[:letter_type].in(["hello","food","sleep"])
|
271
|
+
).and(
|
272
|
+
PigeonLetter.arel_table[:created_at].gt(30.minutes.ago)
|
273
|
+
)
|
274
|
+
).exists.not
|
275
|
+
),
|
276
|
+
letter_not_exists(simple_scope(Dragon.scoped), "nightcap").where(
|
277
|
+
PigeonLetter.where(
|
278
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
279
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
280
|
+
).and(
|
281
|
+
PigeonLetter.arel_table[:letter_type].in(["goodnight"])
|
282
|
+
)
|
283
|
+
).exists
|
284
|
+
).where(
|
285
|
+
PigeonLetter.where(
|
286
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
287
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
288
|
+
).and(
|
289
|
+
PigeonLetter.arel_table[:letter_type].in(["goodnight"])
|
290
|
+
).and(
|
291
|
+
PigeonLetter.arel_table[:created_at].gt(Time.now)
|
292
|
+
)
|
293
|
+
).exists.not
|
294
|
+
) ]
|
295
|
+
},
|
296
|
+
# Finally, we're going to test events
|
297
|
+
# First, simply
|
298
|
+
{
|
299
|
+
name: "all dragons get a birth certificate letter after hatching",
|
300
|
+
config: { flights: { aflight: [ "all dragons get a birth certificate letter after hatching" ] } },
|
301
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "birth_certificate").where("hatched_at < ?", Time.now) ]
|
302
|
+
},
|
303
|
+
# Now, we're test a more complex event
|
304
|
+
{
|
305
|
+
name: "all dragons get a congratulations letter after defeating Hodor.",
|
306
|
+
config: { flights: { aflight: [ "all dragons get a congratulations letter after defeating Hodor." ] } },
|
307
|
+
expected: [ letter_not_exists(simple_scope(Dragon.scoped), "congratulations").where(
|
308
|
+
Battle.joins(:orc).where(
|
309
|
+
Battle.arel_table[:dragon_id].eq(Dragon.arel_table[:id]).and(
|
310
|
+
Orc.arel_table[:name].eq("Hodor")
|
311
|
+
).and(
|
312
|
+
Battle.arel_table[:created_at].lt(Time.now)
|
313
|
+
).and(
|
314
|
+
Battle.arel_table[:is_dragon_victor].eq(true)
|
315
|
+
)
|
316
|
+
).exists
|
317
|
+
) ]
|
318
|
+
},
|
319
|
+
# A recurring event
|
320
|
+
{
|
321
|
+
name: "pixies getting a level up email",
|
322
|
+
config: { flights: { aflight: [ "pixies get a level up letter every time after leveling up" ] } },
|
323
|
+
expected: [ simple_scope(Pixie.scoped).where(
|
324
|
+
PigeonLetter.where(
|
325
|
+
PigeonLetter.arel_table[:cargo_id].eq(Pixie.arel_table[:id]).and(
|
326
|
+
PigeonLetter.arel_table[:cargo_type].eq(Pixie.arel_table.name.classify)
|
327
|
+
).and(
|
328
|
+
PigeonLetter.arel_table[:created_at].gt(Time.now)
|
329
|
+
).and(
|
330
|
+
PigeonLetter.arel_table[:letter_type].eq("level_up")
|
331
|
+
)
|
332
|
+
).exists.not
|
333
|
+
).where(
|
334
|
+
Level.where(
|
335
|
+
Level.arel_table[:pixie_id].eq(Pixie.arel_table[:id]).and(
|
336
|
+
Level.arel_table[:created_at].lt(Time.now)
|
337
|
+
)
|
338
|
+
).exists
|
339
|
+
) ]
|
340
|
+
},
|
341
|
+
# Finally, we're going to test base changes
|
342
|
+
{
|
343
|
+
name: "some dragons, but then other dragons",
|
344
|
+
config: { flights: { aflight: [ "red dragons get a red letter",
|
345
|
+
"then get a redder letter 1 hour after that",
|
346
|
+
"blue dragons get a blue letter",
|
347
|
+
"then get a bluer letter 1 fortnight" ] } },
|
348
|
+
expected: [ letter_not_exists(simple_scope(Dragon.where(color: 'red')), "red"),
|
349
|
+
letter_not_exists(simple_scope(Dragon.where(color: 'red')), "redder").where(
|
350
|
+
PigeonLetter.where(
|
351
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
352
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
353
|
+
).and(
|
354
|
+
PigeonLetter.arel_table[:letter_type].in(["red"])
|
355
|
+
)
|
356
|
+
).exists
|
357
|
+
).where(
|
358
|
+
PigeonLetter.where(
|
359
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
360
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
361
|
+
).and(
|
362
|
+
PigeonLetter.arel_table[:letter_type].in(["red"])
|
363
|
+
).and(
|
364
|
+
PigeonLetter.arel_table[:created_at].gt(1.hour.ago)
|
365
|
+
)
|
366
|
+
).exists.not
|
367
|
+
),
|
368
|
+
letter_not_exists(simple_scope(Dragon.where(color: 'blue')), "blue"),
|
369
|
+
letter_not_exists(simple_scope(Dragon.where(color: 'blue')), "bluer").where(
|
370
|
+
PigeonLetter.where(
|
371
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
372
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
373
|
+
).and(
|
374
|
+
PigeonLetter.arel_table[:letter_type].in(["blue"])
|
375
|
+
)
|
376
|
+
).exists
|
377
|
+
).where(
|
378
|
+
PigeonLetter.where(
|
379
|
+
PigeonLetter.arel_table[:cargo_id].eq(Dragon.arel_table[:id]).and(
|
380
|
+
PigeonLetter.arel_table[:cargo_type].eq(Dragon.arel_table.name.classify)
|
381
|
+
).and(
|
382
|
+
PigeonLetter.arel_table[:letter_type].in(["blue"])
|
383
|
+
).and(
|
384
|
+
PigeonLetter.arel_table[:created_at].gt(1.fortnight.ago)
|
385
|
+
)
|
386
|
+
).exists.not
|
387
|
+
) ]
|
388
|
+
},
|
389
|
+
# Next, we'll test two flights
|
390
|
+
{
|
391
|
+
name: "two flights - some dragons, but then other dragons",
|
392
|
+
config: { flights: { aflight: [ "red dragons get a red letter" ], bflight: [ "red dragons get a blue letter" ] } },
|
393
|
+
expected: {
|
394
|
+
aflight: [ letter_not_exists(simple_scope(Dragon.where(color: 'red'),2,0), "red") ],
|
395
|
+
bflight: [ letter_not_exists(simple_scope(Dragon.where(color: 'red'),2,1), "blue") ]
|
396
|
+
}
|
397
|
+
}
|
398
|
+
]
|
399
|
+
|
400
|
+
context "in end-to-end test flights" do
|
401
|
+
setup do
|
402
|
+
Pigeons::Settings.pigeon_config_file = "ignored"
|
403
|
+
File.stubs(:exists? => true)
|
404
|
+
|
405
|
+
PigeonMailer.stubs(respond_to?: true)
|
406
|
+
::Time.stubs(now: ::NOW) # Make this static for a test
|
407
|
+
::Time.stubs(current: ::CURRENT)
|
408
|
+
end
|
409
|
+
puts test_flights.map { |flight| flight[:config][:flights][:aflight].join("\n") }.join("\n")
|
410
|
+
|
411
|
+
test_flights.each do |flight_test|
|
412
|
+
config = flight_test[:config]
|
413
|
+
expected = flight_test[:expected]
|
414
|
+
|
415
|
+
should "for #{flight_test[:name]}" do
|
416
|
+
File.stubs(:read => config.to_json)
|
417
|
+
flights = Pigeons.assemble
|
418
|
+
if flights['bflight'].nil? # Test just one
|
419
|
+
assert_same_elements expected.map { |e| e.to_sql }, flights['aflight'].map { |l| l[:scope].to_sql }
|
420
|
+
else
|
421
|
+
assert_same_elements expected[:aflight].map { |e| e.to_sql }, flights['aflight'].map { |l| l[:scope].to_sql }
|
422
|
+
assert_same_elements expected[:bflight].map { |e| e.to_sql }, flights['bflight'].map { |l| l[:scope].to_sql }
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pigeons
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1pre
|
5
|
+
prerelease: 5
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Geoff Hayes
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: actionmailer
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mocha
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: shoulda
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: test-unit
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rake
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: sqlite3
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: Pigeons makes it a breeze to send your users lifecycle e-mails.
|
143
|
+
email:
|
144
|
+
- geoff@safeshepherd.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- Gemfile
|
151
|
+
- LICENSE.txt
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- lib/generators/pigeons/install_generator.rb
|
155
|
+
- lib/generators/pigeons/templates/pigeon_letter.rb
|
156
|
+
- lib/generators/pigeons/templates/pigeon_letter_migration.rb
|
157
|
+
- lib/generators/pigeons/templates/pigeon_mailer.rb
|
158
|
+
- lib/generators/pigeons/templates/pigeons.json
|
159
|
+
- lib/pigeons.rb
|
160
|
+
- lib/pigeons/checks.rb
|
161
|
+
- lib/pigeons/elements.rb
|
162
|
+
- lib/pigeons/errors.rb
|
163
|
+
- lib/pigeons/extensions.rb
|
164
|
+
- lib/pigeons/logger.rb
|
165
|
+
- lib/pigeons/pigeons.rb
|
166
|
+
- lib/pigeons/pigeons_tasks.rb
|
167
|
+
- lib/pigeons/scope.rb
|
168
|
+
- lib/pigeons/version.rb
|
169
|
+
- pigeons.gemspec
|
170
|
+
- tasks/pigeons.rake
|
171
|
+
- test/test_helper.rb
|
172
|
+
- test/test_pigeons.rb
|
173
|
+
homepage: https://github.com/hayesgm/pigeons
|
174
|
+
licenses: []
|
175
|
+
post_install_message:
|
176
|
+
rdoc_options: []
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
none: false
|
181
|
+
requirements:
|
182
|
+
- - ! '>='
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ! '>'
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 1.3.1
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 1.8.25
|
194
|
+
signing_key:
|
195
|
+
specification_version: 3
|
196
|
+
summary: Pigeons provides an extensible way to send our lifecycle e-mails through
|
197
|
+
simple human-readable syntax
|
198
|
+
test_files:
|
199
|
+
- test/test_helper.rb
|
200
|
+
- test/test_pigeons.rb
|