olfactory 0.2.0 → 0.2.1
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/README.md +110 -8
- data/lib/olfactory.rb +9 -7
- data/lib/olfactory/sequence.rb +7 -6
- data/lib/olfactory/template.rb +97 -42
- data/lib/olfactory/template_definition.rb +48 -22
- data/lib/olfactory/version.rb +1 -1
- data/olfactory.gemspec +1 -1
- data/spec/olfactory/sequence_spec.rb +23 -11
- data/spec/olfactory/template_spec.rb +411 -42
- metadata +32 -39
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2aa21e7c9e5db497a09aae366070f380c53fd06d
|
4
|
+
data.tar.gz: 8cc46a8a5c7651ee48a2ab9aa63c85670409f2e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 86b174607b353e2daf29dfbee9f19d7594b95433c629f925e4feecb58b50e205f2f58a43014d2dc107c987530518154c8612930e9b8cba3859a81a6dd57a66ef
|
7
|
+
data.tar.gz: ae15150ca718660d8de6b7a8252ef61e8e7e633337eb28b44fcd57df76864915e8f882d7831f12ef3d582d568ac5a2fa5c09dc4b3eef9c624c447677f4cd9a55
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Olfactory
|
|
8
8
|
|
9
9
|
Olfactory is a factory extension for creating complex object sets, as a supplement to `factory_girl`, `fabrication`, or other factories.
|
10
10
|
|
11
|
-
It introduces the concept of **templates**:
|
11
|
+
It introduces the concept of **templates**: a group of named values/objects (as a `Hash`.) You define what objects (or other templates) your template can contain (similar to a factory), then you can create instances of that template using the `#build` or `#create` functions. These templates can be used to make test setup much quicker and easier. Templates are not intended to replace factories, but bridge the gap where `factory_girl` and `fabrication` factories fall short.
|
12
12
|
|
13
13
|
They are most useful when:
|
14
14
|
- Your models are weakly related or non-relational (e.g. not joined by ActiveRecord associations)
|
@@ -54,8 +54,14 @@ context "networkable people" do
|
|
54
54
|
end
|
55
55
|
```
|
56
56
|
|
57
|
+
In this sample, the `let(:user_group)` block returns a `Hash` object, that contains structured, pre-fabricated data of our choosing that we can use for our `it` example.
|
58
|
+
|
57
59
|
### Usage
|
58
60
|
|
61
|
+
##### What is a template?
|
62
|
+
|
63
|
+
Templates are effectively `Hash` schemas. By defining a template, you are specifying which named-values can appear in a `Hash` instance of that template. When defining a template, you can also define custom presets, sequences, and other named options. You can leverage these features to simplify how you create complex objects & test data.
|
64
|
+
|
59
65
|
##### Defining templates
|
60
66
|
|
61
67
|
Templates are defined in `spec/templates/**/*.rb` files. Define a template using the `Olfactory#template` method.
|
@@ -67,7 +73,17 @@ Templates are defined in `spec/templates/**/*.rb` files. Define a template using
|
|
67
73
|
Once defined, these templates can be instantiated using `build` and `create`, which are analogous to the same `factory_girl`/`fabrication` methods
|
68
74
|
|
69
75
|
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!
|
76
|
+
Olfactory.create :computer # Creates objects, and attempts to save all items in the Hash that respond to #save!
|
77
|
+
|
78
|
+
Invoking these two methods will return a `Hash` matching the template schema, populated with either custom or preset values.
|
79
|
+
|
80
|
+
##### Defining template relationships using `has` & `embeds`
|
81
|
+
|
82
|
+
Every template is composed of two kinds of named values: *fields* or other *templates*.
|
83
|
+
|
84
|
+
Fields hold actual values: integers, strings, objects, etc. The `has` relation is used define a field. `#has_one` holds one object, and '#has_many' holds a collection of objects (`Array` or `Hash`.)
|
85
|
+
|
86
|
+
You can also embed a template within another template. This is useful if you have a template composed of other smaller sub-templates (e.g. a Computer template composed of Processor and Memory templates.) Use the `embeds` relation to nest a template. `#embeds_one` will embed a single instance of a template. `#embeds_many` will embed a collection of template instances (in the form of an `Array` or `Hash`.)
|
71
87
|
|
72
88
|
##### #has_one
|
73
89
|
|
@@ -513,12 +529,14 @@ Sample:
|
|
513
529
|
|
514
530
|
##### #transient
|
515
531
|
|
516
|
-
Similar to `factory_girl`'s transients, `transient` defines a temporary variable. You can store values in here to compose conditional logic or more sophisticated templates. When a template contains an embedded template, it will pass down all of its transients to the embedded template. Invoking `transients` on an instance of a template will return a
|
532
|
+
Similar to `factory_girl`'s transients, `transient` defines a temporary variable. You can store values in here to compose conditional logic or more sophisticated templates. When a template contains an embedded template, it will pass down all of its transients to the embedded template. Invoking `transients` on an instance of a template will return a `Hash` of its transient variables.
|
517
533
|
|
518
534
|
Usage:
|
519
|
-
> **transient** name, Object
|
535
|
+
> **transient** name, Object # Sets value
|
536
|
+
>
|
537
|
+
> **transient** name { Object } # Sets value (lazily)
|
520
538
|
>
|
521
|
-
> **transients**[name]
|
539
|
+
> **transients**[name] # Gets value
|
522
540
|
|
523
541
|
Sample:
|
524
542
|
|
@@ -557,10 +575,15 @@ Sample:
|
|
557
575
|
Defines default values, which are used to fill in any empty `has`, `embeds` or `transient` fields, before and after respectively. They will *not* overwrite any non-nil value.
|
558
576
|
|
559
577
|
Definition:
|
560
|
-
> **before** { |instance| &block }
|
578
|
+
> **before**(*:context, :run => Symbol*) { |instance| &block }
|
561
579
|
>
|
562
580
|
> **after** { |instance| &block }
|
563
581
|
|
582
|
+
- `:context` defines when this before should run. Specifying `:embedded` means it runs just before embedded objects are added to the instance. Default (by providing no value) is to run immediately as instance is created.
|
583
|
+
- `:run` defines how many times this before can be invoked for an instance. Specifying `:once` means it can only be invoked once (singleton-style.) Default is to always execute. Can only be specified if `:context` is also specified.
|
584
|
+
|
585
|
+
The latter two options can be useful if you are embedding a template that reads the parent's fields or transients.
|
586
|
+
|
564
587
|
Sample:
|
565
588
|
|
566
589
|
# Template defintion
|
@@ -597,6 +620,85 @@ Sample:
|
|
597
620
|
end
|
598
621
|
# Result
|
599
622
|
{
|
600
|
-
:
|
601
|
-
:
|
623
|
+
:memory_size => "1GB",
|
624
|
+
:cpu => "ARM"
|
625
|
+
}
|
626
|
+
|
627
|
+
Another `before` sample using `:context` and `:run` options:
|
628
|
+
|
629
|
+
# Template defintions
|
630
|
+
Olfactory.template :widget do |t|
|
631
|
+
t.embeds_many :doodads, :singular => :doodad
|
632
|
+
t.has_one :thingamabob
|
633
|
+
t.macro :quality do |m, type|
|
634
|
+
m.transient :attribute, type.to_s
|
635
|
+
end
|
636
|
+
t.before(:embedded) do |d|
|
637
|
+
d.quality :dull
|
638
|
+
d.thingamabob "thingamabob" if d[:doodads] && d[:doodads].count > 0
|
639
|
+
end
|
640
|
+
end
|
641
|
+
Olfactory.template :doodad do |t|
|
642
|
+
t.has_one :gizmo
|
643
|
+
t.after do |d|
|
644
|
+
d.gizmo "#{d.transients[:attribute]} doodad"
|
645
|
+
end
|
646
|
+
end
|
647
|
+
# Build instance of template
|
648
|
+
Olfactory.build :widget do |w|
|
649
|
+
w.doodad
|
650
|
+
w.quality :shiny
|
651
|
+
w.doodad
|
652
|
+
end
|
653
|
+
# Result
|
654
|
+
{
|
655
|
+
:doodads => [{ :gizmo => "dull doodad" },
|
656
|
+
{ :gizmo => "shiny doodad" }]
|
657
|
+
# NOTE: A 'thingamabob' wasn't added. This is because the #before only ran once.
|
602
658
|
}
|
659
|
+
|
660
|
+
##### #instantiate
|
661
|
+
|
662
|
+
Defines an 'instantiator': a function you can call to build custom objects from an instance of a template. The block can accept arguments or use the template instance as input, and should return an object or collection. Invoke the block using the `#build` or `#create` method to get the return value from the instantiator. When `#create` is used, any object that responds to `#save!` will be saved.
|
663
|
+
|
664
|
+
Definition:
|
665
|
+
> **instantiate** :name { |instance, *args..| &block }
|
666
|
+
|
667
|
+
When using:
|
668
|
+
> **build**(name*[, arg1, arg2...]*)
|
669
|
+
>
|
670
|
+
> **create**(name*[, arg1, arg2...]*)
|
671
|
+
|
672
|
+
Sample:
|
673
|
+
|
674
|
+
# Template defintion
|
675
|
+
Olfactory.template :widget do |t|
|
676
|
+
t.has_one :doodad
|
677
|
+
t.instantiate :doodad do |i, j|
|
678
|
+
String.new("#{i[:doodad]}-instance-#{j}")
|
679
|
+
end
|
680
|
+
end
|
681
|
+
# Build instance of template
|
682
|
+
instance = Olfactory.build :widget do |w| w.doodad "doodad" end
|
683
|
+
instance.build(:doodad, 1)
|
684
|
+
# Result
|
685
|
+
"doodad-instance-1"
|
686
|
+
|
687
|
+
### Changelog
|
688
|
+
|
689
|
+
#### Version 0.2.1
|
690
|
+
|
691
|
+
- Added: `context` and `run` options to `before` blocks.
|
692
|
+
- Added: `dimension` option to sequences, to allow scoping.
|
693
|
+
- Fixed: Default values not being overridden in special cases.
|
694
|
+
- Fixed: Defaults adding to item and subtemplate collections.
|
695
|
+
|
696
|
+
#### Version 0.2.0
|
697
|
+
|
698
|
+
- Added: Sequences (like factory_girl's)
|
699
|
+
- Added: Dictionaries (generic hash storage)
|
700
|
+
- Changed: `#build_template` and `#create_template` have been renamed to `#build` and `#create` respectively.
|
701
|
+
|
702
|
+
#### Version 0.1.0
|
703
|
+
|
704
|
+
- Initial version of Olfactory (templates, transients, macros, presets, has/embeds relations)
|
data/lib/olfactory.rb
CHANGED
@@ -35,16 +35,16 @@ module Olfactory
|
|
35
35
|
|
36
36
|
# Invocations
|
37
37
|
def self.build(name, options = {}, &block)
|
38
|
-
self.templates[name].
|
38
|
+
self.templates[name].construct(block, options)
|
39
39
|
end
|
40
40
|
def self.create(name, options = {}, &block)
|
41
|
-
template = self.templates[name].
|
41
|
+
template = self.templates[name].construct(block, options)
|
42
42
|
template.save!
|
43
43
|
template
|
44
44
|
end
|
45
45
|
def self.generate(name, options = {}, &block)
|
46
46
|
if sequence = self.sequences[name]
|
47
|
-
sequence.generate(
|
47
|
+
sequence.generate(options, block)
|
48
48
|
else
|
49
49
|
raise "Unknown sequence '#{name}'!"
|
50
50
|
end
|
@@ -67,8 +67,9 @@ module Olfactory
|
|
67
67
|
self.sequences[name].reset
|
68
68
|
end
|
69
69
|
end
|
70
|
-
def self.reset_template_sequences(template, *names)
|
71
|
-
|
70
|
+
def self.reset_template_sequences(template = nil, *names)
|
71
|
+
templates = template.nil? ? self.templates.values : [self.templates[template]].compact
|
72
|
+
templates.each do |template|
|
72
73
|
template.reset_sequences(*names)
|
73
74
|
end
|
74
75
|
end
|
@@ -78,8 +79,9 @@ module Olfactory
|
|
78
79
|
self.dictionaries[name].reset
|
79
80
|
end
|
80
81
|
end
|
81
|
-
def self.reset_template_dictionaries(template, *names)
|
82
|
-
|
82
|
+
def self.reset_template_dictionaries(template = nil, *names)
|
83
|
+
templates = template.nil? ? self.templates.values : [self.templates[template]].compact
|
84
|
+
templates.each do |template|
|
83
85
|
template.reset_dictionaries(*names)
|
84
86
|
end
|
85
87
|
end
|
data/lib/olfactory/sequence.rb
CHANGED
@@ -6,19 +6,20 @@ module Olfactory
|
|
6
6
|
self[:evaluator] = block
|
7
7
|
self[:scope] = (options[:scope] || :global)
|
8
8
|
self[:seed] = (options[:seed] || 0)
|
9
|
-
self[:
|
9
|
+
self[:dimensions] = { nil => { :current_seed => (options[:seed] || 0) } }
|
10
10
|
end
|
11
11
|
|
12
|
-
def generate(
|
13
|
-
seed = options[:seed] || self[:current_seed]
|
12
|
+
def generate(options = {}, block)
|
13
|
+
seed = options[:seed] || (self[:dimensions][options[:dimension]] ||= { :current_seed => self[:seed] })[:current_seed]
|
14
14
|
target = block || self[:evaluator]
|
15
|
-
value = target.call(seed, options.reject { |k,v|
|
16
|
-
self[:current_seed] += 1 if !options.has_key?(:seed)
|
15
|
+
value = target.call(seed, options.reject { |k,v| [:seed, :dimension].include?(k) })
|
16
|
+
self[:dimensions][options[:dimension]][:current_seed] += 1 if !options.has_key?(:seed)
|
17
17
|
|
18
18
|
value
|
19
19
|
end
|
20
20
|
def reset
|
21
|
-
self[:current_seed] = self[:seed]
|
21
|
+
self[:dimensions].values.each { |v| v[:current_seed] = self[:seed] }
|
22
|
+
self
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
data/lib/olfactory/template.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
module Olfactory
|
3
3
|
class Template < Hash
|
4
|
-
attr_accessor :definition,
|
4
|
+
attr_accessor :definition,
|
5
|
+
:transients,
|
6
|
+
:sequences,
|
7
|
+
:dictionaries,
|
8
|
+
:default_mode,
|
9
|
+
:default_populated,
|
10
|
+
:default_populated_transients,
|
11
|
+
:block_invocations
|
5
12
|
|
6
13
|
def initialize(definition, options = {})
|
7
14
|
self.definition = definition
|
8
15
|
self.transients = options[:transients] ? options[:transients].clone : {}
|
9
16
|
self.sequences = options[:sequences] ? options[:sequences].clone : {}
|
10
17
|
self.dictionaries = options[:dictionaries] ? options[:dictionaries].clone : {}
|
18
|
+
self.default_populated = {}
|
19
|
+
self.default_populated_transients = {}
|
20
|
+
self.block_invocations = []
|
11
21
|
end
|
12
22
|
|
13
|
-
def
|
23
|
+
def construct(block, options = {})
|
14
24
|
self.add_defaults(:before) if options[:defaults].nil? || options[:defaults]
|
15
25
|
if block # Block can be nil (when we want only defaults)
|
16
26
|
if options[:value]
|
@@ -20,10 +30,8 @@ module Olfactory
|
|
20
30
|
end
|
21
31
|
end
|
22
32
|
self.add_defaults(:after) if options[:defaults].nil? || options[:defaults]
|
23
|
-
|
24
33
|
self
|
25
34
|
end
|
26
|
-
|
27
35
|
def save!
|
28
36
|
# Items, then subtemplates
|
29
37
|
[self.definition.t_items, self.definition.t_subtemplates].each do |field_group_definitions|
|
@@ -49,8 +57,8 @@ module Olfactory
|
|
49
57
|
super # Unknown method
|
50
58
|
end
|
51
59
|
end
|
52
|
-
def can_set_field?(
|
53
|
-
!(self.default_mode && self.has_key?(
|
60
|
+
def can_set_field?(name)
|
61
|
+
!(self.default_mode && self.has_key?(name)) || (self.default_mode && (self.default_populated[name] == true))
|
54
62
|
end
|
55
63
|
def extract_variable_name(args)
|
56
64
|
variable_name = args.first
|
@@ -59,13 +67,20 @@ module Olfactory
|
|
59
67
|
end
|
60
68
|
def populate_field(field_definition, meth, args, block)
|
61
69
|
if field_definition[:type] == :macro
|
62
|
-
field_value =
|
70
|
+
field_value = construct_macro(field_definition, args, block)
|
63
71
|
do_not_set_value = true
|
64
|
-
elsif field_definition[:type] == :subtemplate && can_set_field?(
|
72
|
+
elsif field_definition[:type] == :subtemplate && can_set_field?(field_definition[:name])
|
65
73
|
subtemplate_name = field_definition.has_key?(:template) ? field_definition[:template] : field_definition[:name]
|
66
74
|
subtemplate_definition = Olfactory.templates[subtemplate_name]
|
67
75
|
subtemplate_definition ||= Olfactory.templates[field_definition[:singular]]
|
68
76
|
if subtemplate_definition
|
77
|
+
# Invoke before clauses
|
78
|
+
self.add_defaults(:before_embedded)
|
79
|
+
# self.default_mode = true
|
80
|
+
# before_block = field_definition[:evaluator]
|
81
|
+
# before_block.call(self) if before_block
|
82
|
+
# self.default_mode = false
|
83
|
+
|
69
84
|
if field_definition[:collection] && field_definition[:collection] <= Array
|
70
85
|
# Embeds many
|
71
86
|
if meth == field_definition[:singular]
|
@@ -73,14 +88,14 @@ module Olfactory
|
|
73
88
|
grammar = :singular
|
74
89
|
preset_name = args.first
|
75
90
|
|
76
|
-
field_value =
|
91
|
+
field_value = construct_one_subtemplate(subtemplate_definition, preset_name, block)
|
77
92
|
else
|
78
93
|
# Plural
|
79
94
|
grammar = :plural
|
80
95
|
quantity = args.detect { |value| value.class <= Integer }
|
81
96
|
preset_name = args.detect { |value| value != quantity }
|
82
97
|
|
83
|
-
field_value =
|
98
|
+
field_value = construct_many_subtemplates(subtemplate_definition, quantity, preset_name, block)
|
84
99
|
do_not_set_value if field_value.nil?
|
85
100
|
end
|
86
101
|
elsif field_definition[:collection] && field_definition[:collection] <= Hash
|
@@ -92,7 +107,7 @@ module Olfactory
|
|
92
107
|
args = args[1..(args.size-1)]
|
93
108
|
preset_name = args.first
|
94
109
|
|
95
|
-
field_value =
|
110
|
+
field_value = construct_one_subtemplate(subtemplate_definition, preset_name, block)
|
96
111
|
do_not_set_value if field_value.nil? # || field_value.empty?
|
97
112
|
else
|
98
113
|
# Plural
|
@@ -104,13 +119,16 @@ module Olfactory
|
|
104
119
|
# Embeds one
|
105
120
|
preset_name = args.first
|
106
121
|
|
107
|
-
field_value =
|
122
|
+
field_value = construct_one_subtemplate(subtemplate_definition, preset_name, block)
|
108
123
|
do_not_set_value if field_value.nil?
|
109
124
|
end
|
125
|
+
|
126
|
+
# Invoke after clauses
|
127
|
+
self.add_defaults(:after_embedded)
|
110
128
|
else
|
111
129
|
raise "Could not find a template matching '#{subtemplate_name}'!"
|
112
130
|
end
|
113
|
-
elsif field_definition[:type] == :item && can_set_field?(
|
131
|
+
elsif field_definition[:type] == :item && can_set_field?(field_definition[:name])
|
114
132
|
if field_definition[:collection] && field_definition[:collection] <= Array
|
115
133
|
# Has many
|
116
134
|
if meth == field_definition[:singular]
|
@@ -118,7 +136,7 @@ module Olfactory
|
|
118
136
|
grammar = :singular
|
119
137
|
obj = args.count == 1 ? args.first : args
|
120
138
|
|
121
|
-
field_value =
|
139
|
+
field_value = construct_one_item(field_definition, obj, block)
|
122
140
|
do_not_set_value = true if field_value.nil?
|
123
141
|
else
|
124
142
|
# Plural
|
@@ -126,7 +144,7 @@ module Olfactory
|
|
126
144
|
quantity = args.first if block && args.first.class <= Integer
|
127
145
|
arr = args.first if args.count == 1 && args.first.class <= Array
|
128
146
|
|
129
|
-
field_value =
|
147
|
+
field_value = construct_many_items(field_definition, quantity, arr, args, block)
|
130
148
|
do_not_set_value = true if field_value.empty?
|
131
149
|
end
|
132
150
|
elsif field_definition[:collection] && field_definition[:collection] <= Hash
|
@@ -138,7 +156,7 @@ module Olfactory
|
|
138
156
|
args = args[1..(args.size-1)]
|
139
157
|
obj = args.first
|
140
158
|
|
141
|
-
field_value =
|
159
|
+
field_value = construct_one_item(field_definition, obj, block)
|
142
160
|
do_not_set_value = true if field_value.nil?
|
143
161
|
else
|
144
162
|
# Plural
|
@@ -155,7 +173,7 @@ module Olfactory
|
|
155
173
|
# Has one
|
156
174
|
obj = args.first
|
157
175
|
|
158
|
-
field_value =
|
176
|
+
field_value = construct_one_item(field_definition, obj, block)
|
159
177
|
end
|
160
178
|
elsif field_definition.class == Olfactory::Dictionary
|
161
179
|
if field_definition.scope == :template
|
@@ -188,79 +206,116 @@ module Olfactory
|
|
188
206
|
else
|
189
207
|
return_value = self[field_definition[:name]] = field_value
|
190
208
|
end
|
209
|
+
if self.default_mode && (self.default_populated[field_definition[:name]] != false)
|
210
|
+
self.default_populated[field_definition[:name]] = true
|
211
|
+
else
|
212
|
+
self.default_populated[field_definition[:name]] = false
|
213
|
+
end
|
191
214
|
end
|
192
215
|
return_value
|
193
216
|
end
|
194
|
-
def transient(name, value)
|
195
|
-
|
217
|
+
def transient(name, value = nil, &block)
|
218
|
+
if !(self.default_mode && self.transients.has_key?(name)) || (self.default_mode && (self.default_populated_transients[name] == true))
|
219
|
+
self.transients[name] = (block ? block.call : value)
|
220
|
+
if self.default_mode && (self.default_populated_transients[name] != false)
|
221
|
+
self.default_populated_transients[name] = true
|
222
|
+
else
|
223
|
+
self.default_populated_transients[name] = false
|
224
|
+
end
|
225
|
+
end
|
196
226
|
end
|
197
227
|
def generate(name, options = {}, &block)
|
198
228
|
sequence = self.definition.t_sequences[name]
|
199
229
|
# Template scope
|
200
230
|
if sequence && sequence[:scope] == :template
|
201
|
-
value = sequence.generate(
|
231
|
+
value = sequence.generate(options, block)
|
202
232
|
# Instance scope
|
203
233
|
elsif sequence && sequence[:scope] == :instance
|
204
|
-
self.sequences[name] ||=
|
205
|
-
value =
|
206
|
-
self.sequences[name][:current_seed] += 1 if !options.has_key?(:seed)
|
234
|
+
self.sequences[name] ||= sequence.dup.reset
|
235
|
+
value = self.sequences[name].generate(options, block)
|
236
|
+
# self.sequences[name][:current_seed] += 1 if !options.has_key?(:seed)
|
207
237
|
else
|
208
238
|
raise "Unknown sequence '#{name}'!"
|
209
239
|
end
|
210
240
|
value
|
211
241
|
end
|
242
|
+
def build(name, *args)
|
243
|
+
if instantiator_definition = self.definition.t_instantiators[name]
|
244
|
+
instantiator_definition[:evaluator].call(self, *args)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
def create(name, *args)
|
248
|
+
obj = self.build(name, *args)
|
249
|
+
if obj.class <= Array
|
250
|
+
obj.each { |o| o.save! if o.respond_to?(:save!) }
|
251
|
+
elsif obj.class <= Hash
|
252
|
+
obj.values.each { |o| o.save! if o.respond_to?(:save!) }
|
253
|
+
elsif obj.respond_to?(:save!)
|
254
|
+
obj.save!
|
255
|
+
end
|
256
|
+
obj
|
257
|
+
end
|
212
258
|
def add_defaults(mode)
|
213
259
|
# Prevents overwrites of custom values by defaults
|
214
260
|
self.default_mode = true # Hackish for sure, but its efficient...
|
215
261
|
|
216
262
|
case mode
|
217
263
|
when :before
|
218
|
-
|
264
|
+
default_definitions = definition.t_befores[:all]
|
219
265
|
when :after
|
220
|
-
|
266
|
+
default_definitions = definition.t_afters[:all]
|
267
|
+
when :before_embedded
|
268
|
+
default_definitions = definition.t_befores[:embedded]
|
269
|
+
when :after_embedded
|
270
|
+
default_definitions = definition.t_afters[:embedded]
|
221
271
|
end
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
272
|
+
default_definitions ||= []
|
273
|
+
default_definitions.reject! { |dfn| dfn[:run] == :once && self.block_invocations.include?(dfn.object_id) }
|
274
|
+
|
275
|
+
default_definitions.each do |default_definition|
|
276
|
+
if default_definition[:evaluator]
|
277
|
+
default_definition[:evaluator].call(self)
|
278
|
+
elsif default_definition[:preset]
|
279
|
+
preset_definition = definition.find_preset_definition(default_definition[:preset])
|
280
|
+
preset_definition[:evaluator].call(self)
|
281
|
+
end
|
282
|
+
self.block_invocations << default_definition.object_id if default_definition[:run] # Mark block as invoked
|
228
283
|
end
|
229
284
|
|
230
285
|
self.default_mode = false
|
231
286
|
end
|
232
|
-
def
|
287
|
+
def construct_macro(macro_definition, args, block)
|
233
288
|
if macro_definition[:evaluator]
|
234
289
|
macro_definition[:evaluator].call(self, *args)
|
235
290
|
end
|
236
291
|
end
|
237
|
-
def
|
292
|
+
def construct_one_subtemplate(subtemplate_definition, preset_name, block)
|
238
293
|
# Block
|
239
294
|
if block
|
240
|
-
subtemplate_definition.
|
295
|
+
subtemplate_definition.construct(block, :transients => self.transients)
|
241
296
|
# Preset Name
|
242
297
|
elsif preset_name
|
243
|
-
subtemplate_definition.
|
298
|
+
subtemplate_definition.construct_preset(preset_name, 1, :transients => self.transients)
|
244
299
|
# Default (nothing)
|
245
300
|
else
|
246
|
-
subtemplate_definition.
|
301
|
+
subtemplate_definition.construct(nil, :transients => self.transients)
|
247
302
|
end
|
248
303
|
end
|
249
|
-
def
|
304
|
+
def construct_many_subtemplates(subtemplate_definition, quantity, preset_name, block)
|
250
305
|
# Integer, Block
|
251
306
|
if quantity && block
|
252
|
-
Array.new(quantity) { subtemplate_definition.
|
307
|
+
Array.new(quantity) { subtemplate_definition.construct(block, :transients => self.transients) }
|
253
308
|
# Integer, Preset Name
|
254
309
|
elsif quantity && preset_name
|
255
|
-
subtemplate_definition.
|
310
|
+
subtemplate_definition.construct_preset(preset_name, quantity, :transients => self.transients)
|
256
311
|
# Integer
|
257
312
|
elsif quantity
|
258
|
-
Array.new(quantity) { subtemplate_definition.
|
313
|
+
Array.new(quantity) { subtemplate_definition.construct(nil, :transients => self.transients) }
|
259
314
|
else
|
260
315
|
nil
|
261
316
|
end
|
262
317
|
end
|
263
|
-
def
|
318
|
+
def construct_one_item(item_definition, obj, block)
|
264
319
|
if block
|
265
320
|
block.call
|
266
321
|
elsif obj
|
@@ -269,7 +324,7 @@ module Olfactory
|
|
269
324
|
nil
|
270
325
|
end
|
271
326
|
end
|
272
|
-
def
|
327
|
+
def construct_many_items(item_definition, quantity, arr, args, block)
|
273
328
|
# Integer, Block
|
274
329
|
if quantity && block
|
275
330
|
Array.new(quantity) { block.call }
|