olfactory 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +9 -0
- data/LICENSE +1 -1
- data/README.md +144 -14
- data/Rakefile +30 -0
- data/lib/olfactory/dictionary.rb +13 -0
- data/lib/olfactory/sequence.rb +24 -0
- data/lib/olfactory/template.rb +33 -1
- data/lib/olfactory/template_definition.rb +27 -1
- data/lib/olfactory/version.rb +1 -1
- data/lib/olfactory.rb +61 -4
- data/olfactory.gemspec +3 -1
- data/spec/olfactory/dictionary_spec.rb +20 -0
- data/spec/olfactory/olfactory_spec.rb +0 -0
- data/spec/olfactory/sequence_spec.rb +83 -0
- data/spec/olfactory/template_spec.rb +508 -111
- data/spec/spec_helper.rb +0 -3
- metadata +44 -8
data/.travis.yml
ADDED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
Olfactory
|
2
2
|
==========
|
3
3
|
|
4
|
+
[![Build Status](https://travis-ci.org/delner/olfactory.svg?branch=master)](https://travis-ci.org/delner/olfactory) ![Gem Version](https://badge.fury.io/rb/olfactory.svg)
|
5
|
+
###### *For Ruby 1.9.3, 2.0.0, 2.1.0*
|
6
|
+
|
4
7
|
### Introduction
|
5
8
|
|
6
9
|
Olfactory is a factory extension for creating complex object sets, as a supplement to `factory_girl`, `fabrication`, or other factories.
|
@@ -40,7 +43,7 @@ Instead, we can use templates to define shorthand notation:
|
|
40
43
|
```ruby
|
41
44
|
context "networkable people" do
|
42
45
|
let(:user_group) do
|
43
|
-
Olfactory.
|
46
|
+
Olfactory.create :user_group do |group|
|
44
47
|
group.user :desktop_user { |user| user.phone { |phone| phone.apps :facebook, :twitter } }
|
45
48
|
group.user :tablet_user { |user| user.tablet { |tablet| tablet.app :facebook } }
|
46
49
|
group.user :phone_user { |user| user.desktop { |desktop| desktop.app :twitter } }
|
@@ -61,10 +64,10 @@ Templates are defined in `spec/templates/**/*.rb` files. Define a template using
|
|
61
64
|
...
|
62
65
|
end
|
63
66
|
|
64
|
-
Once defined, these templates can be instantiated using `
|
67
|
+
Once defined, these templates can be instantiated using `build` and `create`, which are analogous to the same `factory_girl`/`fabrication` methods
|
65
68
|
|
66
|
-
Olfactory.
|
67
|
-
Olfactory.
|
69
|
+
Olfactory.build :computer # Creates objects, but does not implictly save them
|
70
|
+
Olfactory.create :computer # Creates objects, and attempts to save all items that respond to #save!
|
68
71
|
|
69
72
|
##### #has_one
|
70
73
|
|
@@ -87,7 +90,7 @@ Sample:
|
|
87
90
|
t.has_one :cpu, :alias => :processor
|
88
91
|
end
|
89
92
|
# Build instance of template
|
90
|
-
Olfactory.
|
93
|
+
Olfactory.build :computer do |c|
|
91
94
|
c.keyboard "X4 Sidewinder"
|
92
95
|
c.mouse { FactoryGirl::build(:mouse) }
|
93
96
|
c.processor "Intel Xeon"
|
@@ -152,7 +155,7 @@ Sample:
|
|
152
155
|
t.has_many :drives, :singular => :drive, :named => true
|
153
156
|
end
|
154
157
|
# Build instance of template
|
155
|
-
Olfactory.
|
158
|
+
Olfactory.build :computer do |c|
|
156
159
|
# Generic
|
157
160
|
c.cpus "Intel i7", "Onboard graphics"
|
158
161
|
c.memory_stick "2GB"
|
@@ -206,7 +209,7 @@ Sample:
|
|
206
209
|
end
|
207
210
|
end
|
208
211
|
# Build instance of template
|
209
|
-
Olfactory.
|
212
|
+
Olfactory.build :computer do |computer|
|
210
213
|
computer.cpu do |cpu|
|
211
214
|
cpu.cores "Intel Core", "Intel Core"
|
212
215
|
end
|
@@ -294,7 +297,7 @@ Sample:
|
|
294
297
|
end
|
295
298
|
end
|
296
299
|
# Build instance of template
|
297
|
-
Olfactory.
|
300
|
+
Olfactory.build :computer do |c|
|
298
301
|
# Generic
|
299
302
|
computer.cpu :amd
|
300
303
|
computer.cpu do |cpu|
|
@@ -315,6 +318,133 @@ Sample:
|
|
315
318
|
end
|
316
319
|
end
|
317
320
|
|
321
|
+
##### #sequence
|
322
|
+
|
323
|
+
Defines a sequence, similar to `factory_girl`'s sequences. Can be combined with `Faker` to provide auto-generated test data. They can be defined at the global level, or nested within a template.
|
324
|
+
|
325
|
+
###### When defining at the global level
|
326
|
+
|
327
|
+
Definition:
|
328
|
+
> **Olfactory.sequence** :name*[, :seed => Integer]* { |iterator[,options]| &block }
|
329
|
+
|
330
|
+
- `seed` defines the starting value, which increments by 1 after each invocation. Default 0.
|
331
|
+
- `options` in the form of hash args can be passed to the block.
|
332
|
+
- `iterator` is the current seed, which starts at 0 by default.
|
333
|
+
- `block` generates and returns the value.
|
334
|
+
|
335
|
+
Usage:
|
336
|
+
> **Olfactory.generate** :name
|
337
|
+
>
|
338
|
+
> **Olfactory.generate** :name, :option1 => Object, :option2 => Object...
|
339
|
+
>
|
340
|
+
> **Olfactory.generate** :name, :seed => Integer
|
341
|
+
>
|
342
|
+
> **Olfactory.generate** :name, :seed => Integer, :option1 => Object, :option2 => Object...
|
343
|
+
>
|
344
|
+
> **Olfactory.generate** :name { |iterator, options| &block }
|
345
|
+
|
346
|
+
- `seed` can be provided to override whatever the current seed is. When you provide an overriding seed, the sequence will *not* increment its internal seed. (Will act like it was never called.)
|
347
|
+
- `options` in the form of hash args can be passed to the block.
|
348
|
+
- `block` can be provided to override the block used for the call. When you provide an overriding block, the sequence will still increment its internal seed.
|
349
|
+
|
350
|
+
Sample:
|
351
|
+
|
352
|
+
Olfactory.sequence :ip_address, :seed => 1 do |n, options|
|
353
|
+
options[:ipv6] ? "fe80::2a18:78ff:feba:#{n}" : "192.168.1.#{n % 255}"
|
354
|
+
end
|
355
|
+
Olfactory.generate(:ip_address) # => "192.168.1.1"
|
356
|
+
Olfactory.generate(:ip_address, :seed => 255) # => "192.168.1.255"
|
357
|
+
Olfactory.generate(:ip_address) # => "192.168.1.2"
|
358
|
+
Olfactory.generate(:ip_address, :ipv6 => true) # => "fe80::2a18:78ff:feba:3"
|
359
|
+
Olfactory.generate(:ip_address) do |n|
|
360
|
+
"172.168.1.#{n % 255}"
|
361
|
+
end # => "172.168.1.4"
|
362
|
+
|
363
|
+
###### When defining at the template level
|
364
|
+
|
365
|
+
`Olfactory` sequences don't provide much benefit over other gems at the global level, but they really shine when used within templates. Sequences work exactly the same within templates, except they can be bound by `scope`, allowing the author a great deal of control over when sequences reset.
|
366
|
+
|
367
|
+
`scope` can either be `:instance`, which resets the seed for each instance of the template, or `:template`, which shares the seed across all instances of the template.
|
368
|
+
|
369
|
+
Sample:
|
370
|
+
|
371
|
+
# Template definition
|
372
|
+
Olfactory.template :computer do |t|
|
373
|
+
t.has_one :serial_number
|
374
|
+
t.sequence :serial_number, :scope => :template do |n|
|
375
|
+
(10000 + n)
|
376
|
+
end
|
377
|
+
t.has_many :registers, :singular => :register
|
378
|
+
t.sequence :register, :scope => :instance do |n|
|
379
|
+
"Register #{n+1}"
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
Olfactory.build :computer do |c|
|
384
|
+
c.serial_number { c.generate(:serial_number) }
|
385
|
+
c.registers 2 { c.generate(:register) }
|
386
|
+
end
|
387
|
+
# => { :serial_number => 10000, :registers => ["Register 1", "Register 2"] }
|
388
|
+
|
389
|
+
Olfactory.build :computer do |c|
|
390
|
+
c.serial_number { c.generate(:serial_number) }
|
391
|
+
c.registers 2 { c.generate(:register) }
|
392
|
+
end
|
393
|
+
# => { :serial_number => 10001, :registers => ["Register 1", "Register 2"] }
|
394
|
+
|
395
|
+
##### #dictionary
|
396
|
+
|
397
|
+
Defines a dictionary, which is just a simple data store (Hash.) They can be defined at the global level, or nested within a template.
|
398
|
+
|
399
|
+
###### When defining at the global level
|
400
|
+
|
401
|
+
Definition:
|
402
|
+
> **Olfactory.dictionary** :name
|
403
|
+
|
404
|
+
Usage:
|
405
|
+
> **Olfactory.dictionaries**[name] # Returns Hash to read/write from
|
406
|
+
|
407
|
+
Sample:
|
408
|
+
|
409
|
+
Olfactory.dictionary :manfacturer_codes
|
410
|
+
Olfactory.dictionaries[:manfacturer_codes]["DELL"] = "Dell Computing"
|
411
|
+
|
412
|
+
###### When defining at the template level
|
413
|
+
|
414
|
+
A hash data-store isn't that special at the global level, but is much more useful within templates. Definition is the same, but the usage only differs by name. Like sequences, dictionaries can define `scope` to separate or share data across templates. Combining them with sequences, we can synchronize data in some really cool ways.
|
415
|
+
|
416
|
+
`scope` can either be `:instance`, which resets the hash for each instance of the template, or `:template`, which shares the hash across all instances of the template.
|
417
|
+
|
418
|
+
Sample:
|
419
|
+
|
420
|
+
Olfactory.template :computer do |t|
|
421
|
+
t.has_one :hdd_manfacturer_code
|
422
|
+
t.has_one :gpu_manfacturer_code
|
423
|
+
t.has_one :cpu_manfacturer_code
|
424
|
+
t.dictionary :manfacturer_codes, :scope => :template
|
425
|
+
t.sequence :manfacturer_code, :scope => :template do |n|
|
426
|
+
(10000 + n)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
Olfactory.build :computer do |c|
|
431
|
+
c.hdd_manfacturer_code { c.manfacturer_codes["SAMSUNG"] ||= c.generate(:manfacturer_code) }
|
432
|
+
c.cpu_manfacturer_code { c.manfacturer_codes["AMD"] ||= c.generate(:manfacturer_code) }
|
433
|
+
c.gpu_manfacturer_code { c.manfacturer_codes["AMD"] ||= c.generate(:manfacturer_code) }
|
434
|
+
end
|
435
|
+
# => { :hdd_manfacturer_code => 10001,
|
436
|
+
# :cpu_manfacturer_code => 10002,
|
437
|
+
# :cpu_manfacturer_code => 10002 }
|
438
|
+
|
439
|
+
Olfactory.build :computer do |c|
|
440
|
+
c.hdd_manfacturer_code { c.manfacturer_codes["SEAGATE"] ||= c.generate(:manfacturer_code) }
|
441
|
+
c.cpu_manfacturer_code { c.manfacturer_codes["AMD"] ||= c.generate(:manfacturer_code) }
|
442
|
+
c.gpu_manfacturer_code { c.manfacturer_codes["INTEL"] ||= c.generate(:manfacturer_code) }
|
443
|
+
end
|
444
|
+
# => { :hdd_manfacturer_code => 10003,
|
445
|
+
# :cpu_manfacturer_code => 10002,
|
446
|
+
# :cpu_manfacturer_code => 10004 }
|
447
|
+
|
318
448
|
##### #preset
|
319
449
|
|
320
450
|
Defines a preset of values.
|
@@ -323,7 +453,7 @@ Definition:
|
|
323
453
|
> **preset** :name { |instance| &block }
|
324
454
|
|
325
455
|
When using:
|
326
|
-
> Olfactory.
|
456
|
+
> Olfactory.build template_name, :preset => preset_name, :quantity => quantity:Integer
|
327
457
|
|
328
458
|
See above sections for usage within templates.
|
329
459
|
|
@@ -342,7 +472,7 @@ Sample:
|
|
342
472
|
t.has_one :cpu_core
|
343
473
|
end
|
344
474
|
# Build instance of template
|
345
|
-
Olfactory.
|
475
|
+
Olfactory.build :computer, :preset => :dual_core
|
346
476
|
# Result
|
347
477
|
{
|
348
478
|
:cpus => [{
|
@@ -373,7 +503,7 @@ Sample:
|
|
373
503
|
end
|
374
504
|
end
|
375
505
|
# Build instance of template
|
376
|
-
Olfactory.
|
506
|
+
Olfactory.build :computer do |c|
|
377
507
|
c.memory_sticks 2
|
378
508
|
end
|
379
509
|
# Result
|
@@ -407,7 +537,7 @@ Sample:
|
|
407
537
|
t.has_one :available_memory
|
408
538
|
end
|
409
539
|
# Build instance of template
|
410
|
-
Olfactory.
|
540
|
+
Olfactory.build :computer do |c|
|
411
541
|
c.memory_stick_size 2
|
412
542
|
c.memory_sticks 2
|
413
543
|
c.cpu do |cpu|
|
@@ -443,7 +573,7 @@ Sample:
|
|
443
573
|
end
|
444
574
|
end
|
445
575
|
# Build instance of template
|
446
|
-
Olfactory.
|
576
|
+
Olfactory.build :computer do |c|
|
447
577
|
c.cpu "AMD Athlon"
|
448
578
|
end
|
449
579
|
# Result
|
@@ -462,7 +592,7 @@ Sample:
|
|
462
592
|
end
|
463
593
|
end
|
464
594
|
# Build instance of template
|
465
|
-
Olfactory.
|
595
|
+
Olfactory.build :phone do |c|
|
466
596
|
c.memory_size "1GB"
|
467
597
|
end
|
468
598
|
# Result
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
task :default => :spec
|
7
|
+
task :test => :spec
|
8
|
+
|
9
|
+
namespace :doc do
|
10
|
+
begin
|
11
|
+
require 'yard'
|
12
|
+
rescue LoadError
|
13
|
+
# ignore
|
14
|
+
else
|
15
|
+
YARD::Rake::YardocTask.new do |task|
|
16
|
+
task.files = ['lib/**/*.rb']
|
17
|
+
task.options = [
|
18
|
+
'--protected',
|
19
|
+
'--output-dir', 'doc/yard',
|
20
|
+
'--tag', 'format:Supported formats',
|
21
|
+
'--markup', 'markdown',
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Open an irb session preloaded with this library"
|
28
|
+
task :console do
|
29
|
+
sh "irb -rubygems -I lib -r olfactory.rb"
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Olfactory
|
3
|
+
class Sequence < Hash
|
4
|
+
def initialize(name, options, block)
|
5
|
+
self[:name] = name
|
6
|
+
self[:evaluator] = block
|
7
|
+
self[:scope] = (options[:scope] || :global)
|
8
|
+
self[:seed] = (options[:seed] || 0)
|
9
|
+
self[:current_seed] = (options[:seed] || 0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate(name, options, block)
|
13
|
+
seed = options[:seed] || self[:current_seed]
|
14
|
+
target = block || self[:evaluator]
|
15
|
+
value = target.call(seed, options.reject { |k,v| k == :seed })
|
16
|
+
self[:current_seed] += 1 if !options.has_key?(:seed)
|
17
|
+
|
18
|
+
value
|
19
|
+
end
|
20
|
+
def reset
|
21
|
+
self[:current_seed] = self[:seed]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/olfactory/template.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
module Olfactory
|
3
3
|
class Template < Hash
|
4
|
-
attr_accessor :definition, :transients, :default_mode
|
4
|
+
attr_accessor :definition, :transients, :sequences, :dictionaries, :default_mode
|
5
5
|
|
6
6
|
def initialize(definition, options = {})
|
7
7
|
self.definition = definition
|
8
8
|
self.transients = options[:transients] ? options[:transients].clone : {}
|
9
|
+
self.sequences = options[:sequences] ? options[:sequences].clone : {}
|
10
|
+
self.dictionaries = options[:dictionaries] ? options[:dictionaries].clone : {}
|
9
11
|
end
|
10
12
|
|
11
13
|
def build(block, options = {})
|
@@ -155,6 +157,13 @@ module Olfactory
|
|
155
157
|
|
156
158
|
field_value = build_one_item(field_definition, obj, block)
|
157
159
|
end
|
160
|
+
elsif field_definition.class == Olfactory::Dictionary
|
161
|
+
if field_definition.scope == :template
|
162
|
+
return_value = field_definition
|
163
|
+
elsif field_definition.scope == :instance
|
164
|
+
return_value = (self.dictionaries[meth] ||= {})
|
165
|
+
end
|
166
|
+
do_not_set_value = true
|
158
167
|
else
|
159
168
|
do_not_set_value = true
|
160
169
|
end
|
@@ -185,6 +194,21 @@ module Olfactory
|
|
185
194
|
def transient(name, value)
|
186
195
|
self.transients[name] = value if !(self.default_mode && self.transients.has_key?(name))
|
187
196
|
end
|
197
|
+
def generate(name, options = {}, &block)
|
198
|
+
sequence = self.definition.t_sequences[name]
|
199
|
+
# Template scope
|
200
|
+
if sequence && sequence[:scope] == :template
|
201
|
+
value = sequence.generate(name, options, block)
|
202
|
+
# Instance scope
|
203
|
+
elsif sequence && sequence[:scope] == :instance
|
204
|
+
self.sequences[name] ||= { :current_seed => (options[:seed] || sequence[:seed]) }
|
205
|
+
value = sequence.generate(name, options.merge(:seed => self.sequences[name][:current_seed]), block)
|
206
|
+
self.sequences[name][:current_seed] += 1 if !options.has_key?(:seed)
|
207
|
+
else
|
208
|
+
raise "Unknown sequence '#{name}'!"
|
209
|
+
end
|
210
|
+
value
|
211
|
+
end
|
188
212
|
def add_defaults(mode)
|
189
213
|
# Prevents overwrites of custom values by defaults
|
190
214
|
self.default_mode = true # Hackish for sure, but its efficient...
|
@@ -257,5 +281,13 @@ module Olfactory
|
|
257
281
|
args
|
258
282
|
end
|
259
283
|
end
|
284
|
+
def reset_sequences(*names)
|
285
|
+
names = self.sequences.keys if names.empty?
|
286
|
+
names.each { |name| self.sequences[name].reset }
|
287
|
+
end
|
288
|
+
def reset_dictionaries(*names)
|
289
|
+
names = self.dictionaries.keys if names.empty?
|
290
|
+
names.each { |name| self.dictionaries[name].reset }
|
291
|
+
end
|
260
292
|
end
|
261
293
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
module Olfactory
|
3
3
|
class TemplateDefinition
|
4
|
-
attr_accessor :t_items, :t_subtemplates, :t_macros, :t_presets, :t_before, :t_after
|
4
|
+
attr_accessor :t_items, :t_subtemplates, :t_sequences, :t_dictionaries, :t_macros, :t_presets, :t_before, :t_after
|
5
5
|
|
6
6
|
def initialize
|
7
7
|
self.t_items = {}
|
8
8
|
self.t_subtemplates = {}
|
9
|
+
self.t_sequences = {}
|
10
|
+
self.t_dictionaries = {}
|
9
11
|
self.t_macros = {}
|
10
12
|
self.t_presets = {}
|
11
13
|
self.t_before = {}
|
@@ -55,6 +57,7 @@ module Olfactory
|
|
55
57
|
definition = find_macro_definition(name)
|
56
58
|
definition ||= find_subtemplate_definition(name)
|
57
59
|
definition ||= find_item_definition(name)
|
60
|
+
definition ||= find_dictionary_definition(name)
|
58
61
|
definition
|
59
62
|
end
|
60
63
|
|
@@ -82,6 +85,10 @@ module Olfactory
|
|
82
85
|
definition
|
83
86
|
end
|
84
87
|
|
88
|
+
def find_dictionary_definition(name)
|
89
|
+
self.t_dictionaries[name]
|
90
|
+
end
|
91
|
+
|
85
92
|
def find_preset_definition(name)
|
86
93
|
preset_definition = self.find_definition_in_list(name, self.t_presets)
|
87
94
|
if preset_definition.nil?
|
@@ -92,6 +99,15 @@ module Olfactory
|
|
92
99
|
preset_definition
|
93
100
|
end
|
94
101
|
|
102
|
+
def reset_sequences(*names)
|
103
|
+
names = self.t_sequences.keys if names.empty?
|
104
|
+
names.each { |name| self.t_sequences[name].reset }
|
105
|
+
end
|
106
|
+
def reset_dictionaries(*names)
|
107
|
+
names = self.t_dictionaries.keys if names.empty?
|
108
|
+
names.each { |name| self.t_dictionaries[name].reset }
|
109
|
+
end
|
110
|
+
|
95
111
|
# Defines a value holding field
|
96
112
|
def has_one(name, options = {}, &block)
|
97
113
|
self.t_items[name] = { :type => :item,
|
@@ -114,6 +130,16 @@ module Olfactory
|
|
114
130
|
self.embeds_one(name, options.merge(:collection => (options[:named] ? Hash : Array)), &block)
|
115
131
|
end
|
116
132
|
|
133
|
+
# Defines a sequence
|
134
|
+
def sequence(name, options = {}, &block)
|
135
|
+
self.t_sequences[name] = Olfactory::Sequence.new(name, { :scope => :instance }.merge(options), block)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Defines a dictionary
|
139
|
+
def dictionary(name, options = {})
|
140
|
+
self.t_dictionaries[name] = Olfactory::Dictionary.new(name, { :scope => :instance }.merge(options))
|
141
|
+
end
|
142
|
+
|
117
143
|
# Defines a macro
|
118
144
|
def macro(name, options = {}, &block)
|
119
145
|
self.t_macros[name] = { :type => :macro,
|
data/lib/olfactory/version.rb
CHANGED
data/lib/olfactory.rb
CHANGED
@@ -1,29 +1,86 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
require 'olfactory/dictionary'
|
3
|
+
require 'olfactory/sequence'
|
2
4
|
require 'olfactory/template_definition'
|
3
5
|
require 'olfactory/template'
|
4
6
|
|
5
7
|
module Olfactory
|
6
8
|
@@templates = {}
|
9
|
+
@@sequences = {}
|
10
|
+
@@dictionaries = {}
|
11
|
+
|
12
|
+
# Getters
|
7
13
|
def self.templates
|
8
14
|
@@templates
|
9
15
|
end
|
16
|
+
def self.sequences
|
17
|
+
@@sequences
|
18
|
+
end
|
19
|
+
def self.dictionaries
|
20
|
+
@@dictionaries
|
21
|
+
end
|
10
22
|
|
23
|
+
# Definitions
|
11
24
|
def self.template(name, &block)
|
12
|
-
new_template_definition = TemplateDefinition.new
|
25
|
+
new_template_definition = Olfactory::TemplateDefinition.new
|
13
26
|
block.call(new_template_definition)
|
14
27
|
self.templates[name] = new_template_definition
|
15
28
|
end
|
29
|
+
def self.sequence(name, options = {}, &block)
|
30
|
+
sequences[name] = Olfactory::Sequence.new(name, options, block)
|
31
|
+
end
|
32
|
+
def self.dictionary(name)
|
33
|
+
dictionaries[name] = Olfactory::Dictionary.new(name)
|
34
|
+
end
|
16
35
|
|
17
|
-
|
36
|
+
# Invocations
|
37
|
+
def self.build(name, options = {}, &block)
|
18
38
|
self.templates[name].build(block, options)
|
19
39
|
end
|
20
|
-
def self.
|
40
|
+
def self.create(name, options = {}, &block)
|
21
41
|
template = self.templates[name].build(block, options)
|
22
42
|
template.save!
|
23
43
|
template
|
24
44
|
end
|
45
|
+
def self.generate(name, options = {}, &block)
|
46
|
+
if sequence = self.sequences[name]
|
47
|
+
sequence.generate(name, options, block)
|
48
|
+
else
|
49
|
+
raise "Unknown sequence '#{name}'!"
|
50
|
+
end
|
51
|
+
end
|
25
52
|
|
26
|
-
def self.
|
53
|
+
def self.clear
|
27
54
|
@@templates = {}
|
55
|
+
@@sequences = {}
|
56
|
+
@@dictionaries = {}
|
57
|
+
end
|
58
|
+
def self.reset
|
59
|
+
self.reset_sequences
|
60
|
+
self.reset_dictionaries
|
61
|
+
self.reset_template_sequences
|
62
|
+
self.reset_template_dictionaries
|
63
|
+
end
|
64
|
+
def self.reset_sequences(*names)
|
65
|
+
names = self.sequences.keys if names.empty?
|
66
|
+
names.each do |name|
|
67
|
+
self.sequences[name].reset
|
68
|
+
end
|
69
|
+
end
|
70
|
+
def self.reset_template_sequences(template, *names)
|
71
|
+
if template = self.templates[template]
|
72
|
+
template.reset_sequences(*names)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def self.reset_dictionaries(*names)
|
76
|
+
names = self.dictionaries.keys if names.empty?
|
77
|
+
names.each do |name|
|
78
|
+
self.dictionaries[name].reset
|
79
|
+
end
|
80
|
+
end
|
81
|
+
def self.reset_template_dictionaries(template, *names)
|
82
|
+
if template = self.templates[template]
|
83
|
+
template.reset_dictionaries(*names)
|
84
|
+
end
|
28
85
|
end
|
29
86
|
end
|
data/olfactory.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = 'david@davidelner.com'
|
11
11
|
s.files = `git ls-files`.split("\n")
|
12
12
|
s.test_files = `git ls-files -- {spec,features,gemfiles}/*`.split("\n")
|
13
|
-
s.homepage = 'https://github.com/
|
13
|
+
s.homepage = 'https://github.com/delner/olfactory'
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
16
16
|
s.require_paths = ['lib']
|
@@ -20,4 +20,6 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_development_dependency("pry", "~> 0.10")
|
21
21
|
s.add_development_dependency("pry-nav", "~> 0.2")
|
22
22
|
s.add_development_dependency("pry-stack_explorer", "~> 0.4.9")
|
23
|
+
s.add_development_dependency('rake', '~> 10.0.4')
|
24
|
+
s.add_development_dependency('yard', '~> 0.8.7.6')
|
23
25
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Olfactory::Dictionary do
|
4
|
+
before(:context) do
|
5
|
+
Olfactory.dictionary :streets
|
6
|
+
end
|
7
|
+
|
8
|
+
context "given a value" do
|
9
|
+
before(:context) do
|
10
|
+
Olfactory.dictionaries[:streets][:borough => 1, :street => "BROADWAY"] = 10001
|
11
|
+
end
|
12
|
+
let(:key) do
|
13
|
+
{ :borough => 1, :street => "BROADWAY" }
|
14
|
+
end
|
15
|
+
let(:value) { 10001 }
|
16
|
+
|
17
|
+
subject { Olfactory.dictionaries[:streets][key] }
|
18
|
+
it { expect(subject).to eq(value) }
|
19
|
+
end
|
20
|
+
end
|
File without changes
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Olfactory::Sequence do
|
4
|
+
context "with no seed" do
|
5
|
+
before(:example) do
|
6
|
+
Olfactory.sequence :address do |n, options|
|
7
|
+
"#{(n + (n % 2)) + 2} #{"#{options[:prefix]} " if options[:prefix]}BROADWAY"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
context "given nothing" do
|
11
|
+
subject do
|
12
|
+
Olfactory.generate(:address)
|
13
|
+
end
|
14
|
+
it { expect(subject).to eq("2 BROADWAY") }
|
15
|
+
end
|
16
|
+
context "given options" do
|
17
|
+
subject do
|
18
|
+
Olfactory.generate(:address, :prefix => "WEST")
|
19
|
+
end
|
20
|
+
it { expect(subject).to eq("2 WEST BROADWAY") }
|
21
|
+
end
|
22
|
+
context "given a block" do
|
23
|
+
subject do
|
24
|
+
Olfactory.generate(:address) do |n|
|
25
|
+
"#{(n + (n % 2)) + 2} JOHN STREET"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
it { expect(subject).to eq("2 JOHN STREET") }
|
29
|
+
end
|
30
|
+
context "given options and a block" do
|
31
|
+
subject do
|
32
|
+
Olfactory.generate(:address, :suffix => "ROAD") do |n, options|
|
33
|
+
"#{(n + (n % 2)) + 2} JOHN#{options[:suffix] ? " #{options[:suffix]}" : " STREET"}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
it { expect(subject).to eq("2 JOHN ROAD") }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context "with a seed" do
|
40
|
+
before(:example) do
|
41
|
+
Olfactory.sequence :address, :seed => 10 do |n, options|
|
42
|
+
"#{(n + (n % 2))} #{"#{options[:prefix]} " if options[:prefix]}BROADWAY"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
context "given nothing" do
|
46
|
+
subject do
|
47
|
+
Olfactory.generate(:address)
|
48
|
+
end
|
49
|
+
it { expect(subject).to eq("10 BROADWAY") }
|
50
|
+
end
|
51
|
+
context "given options" do
|
52
|
+
subject do
|
53
|
+
Olfactory.generate(:address, :prefix => "WEST")
|
54
|
+
end
|
55
|
+
it { expect(subject).to eq("10 WEST BROADWAY") }
|
56
|
+
end
|
57
|
+
context "given a block" do
|
58
|
+
subject do
|
59
|
+
Olfactory.generate(:address) do |n|
|
60
|
+
"#{(n + (n % 2))} JOHN STREET"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
it { expect(subject).to eq("10 JOHN STREET") }
|
64
|
+
end
|
65
|
+
context "given options and a block" do
|
66
|
+
subject do
|
67
|
+
Olfactory.generate(:address, :suffix => "ROAD") do |n, options|
|
68
|
+
"#{(n + (n % 2))} JOHN#{options[:suffix] ? " #{options[:suffix]}" : " STREET"}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
it { expect(subject).to eq("10 JOHN ROAD") }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "given sequential invocations" do
|
75
|
+
before(:context) do
|
76
|
+
Olfactory.sequence :address do |n, options|
|
77
|
+
"#{(n + (n % 2)) + 2} #{"#{options[:prefix]} " if options[:prefix]}BROADWAY"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
it { expect(Olfactory.generate(:address)).to eq("2 BROADWAY") }
|
81
|
+
it { expect(Olfactory.generate(:address)).to eq("4 BROADWAY") }
|
82
|
+
end
|
83
|
+
end
|