olfactory 0.0.1 → 0.1.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.
@@ -9,6 +9,7 @@ module Olfactory
9
9
  end
10
10
 
11
11
  def build(block, options = {})
12
+ self.add_defaults(:before) if options[:defaults].nil? || options[:defaults]
12
13
  if block # Block can be nil (when we want only defaults)
13
14
  if options[:value]
14
15
  block.call(self, options[:value])
@@ -16,61 +17,189 @@ module Olfactory
16
17
  block.call(self)
17
18
  end
18
19
  end
19
- if options[:defaults].nil? || options[:defaults]
20
- # Only set defaults if configuration wasn't specified
21
- self.add_defaults
22
- end
20
+ self.add_defaults(:after) if options[:defaults].nil? || options[:defaults]
23
21
 
24
22
  self
25
23
  end
26
24
 
25
+ def save!
26
+ # Items, then subtemplates
27
+ [self.definition.t_items, self.definition.t_subtemplates].each do |field_group_definitions|
28
+ field_group_definitions.each do |field_name, field_definition|
29
+ if field_value = self[field_name]
30
+ if field_definition[:collection] && field_definition[:collection] <= Array
31
+ field_value.each { |value| value.save! if value.respond_to?(:save!) }
32
+ elsif field_definition[:collection] && field_definition[:collection] <= Hash
33
+ field_value.values.each { |value| value.save! if value.respond_to?(:save!) }
34
+ else
35
+ field_value.save! if field_value.respond_to?(:save!)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
27
42
  def method_missing(meth, *args, &block)
28
43
  # Explicit fields
29
- if (definition.t_macros.has_key?(meth) || definition.t_subtemplates.has_key?(meth) || definition.t_items.has_key?(meth))
30
- if definition.t_macros.has_key?(meth)
31
- field_type = :macro
32
- definition_of_field = definition.t_macros[meth]
33
- field_value = build_macro(definition_of_field, args, block)
34
- elsif definition.t_subtemplates.has_key?(meth) && !(self.default_mode && self.has_key?(meth))
35
- field_type = :subtemplate
36
- definition_of_field = definition.t_subtemplates[meth]
37
- subtemplate_name = definition_of_field.has_key?(:template) ? definition_of_field[:template] : meth
38
- field_value = build_subtemplate(Olfactory.templates[subtemplate_name], args, block)
39
- elsif definition.t_items.has_key?(meth) && !(self.default_mode && self.has_key?(meth))
40
- field_type = :item
41
- definition_of_field = definition.t_items[meth]
42
- field_value = build_item(definition_of_field, args, block)
43
- end
44
- # Add field value to template
45
- if field_type && field_type != :macro
46
- if definition_of_field[:collection]
47
- self[meth] = [] if !self.has_key?(meth)
48
- if field_type == :subtemplate && field_value.class <= Array
49
- self[meth].concat(field_value)
44
+ if field_definition = self.definition.find_field_definition(meth)
45
+ populate_field(field_definition, meth, args, block)
46
+ else
47
+ super # Unknown method
48
+ end
49
+ end
50
+ def can_set_field?(meth)
51
+ !(self.default_mode && self.has_key?(meth))
52
+ end
53
+ def extract_variable_name(args)
54
+ variable_name = args.first
55
+ raise "Must provide a name when adding to a named field!" if variable_name.nil?
56
+ variable_name
57
+ end
58
+ def populate_field(field_definition, meth, args, block)
59
+ if field_definition[:type] == :macro
60
+ field_value = build_macro(field_definition, args, block)
61
+ do_not_set_value = true
62
+ elsif field_definition[:type] == :subtemplate && can_set_field?(meth)
63
+ subtemplate_name = field_definition.has_key?(:template) ? field_definition[:template] : field_definition[:name]
64
+ subtemplate_definition = Olfactory.templates[subtemplate_name]
65
+ subtemplate_definition ||= Olfactory.templates[field_definition[:singular]]
66
+ if subtemplate_definition
67
+ if field_definition[:collection] && field_definition[:collection] <= Array
68
+ # Embeds many
69
+ if meth == field_definition[:singular]
70
+ # Singular
71
+ grammar = :singular
72
+ preset_name = args.first
73
+
74
+ field_value = build_one_subtemplate(subtemplate_definition, preset_name, block)
75
+ else
76
+ # Plural
77
+ grammar = :plural
78
+ quantity = args.detect { |value| value.class <= Integer }
79
+ preset_name = args.detect { |value| value != quantity }
80
+
81
+ field_value = build_many_subtemplates(subtemplate_definition, quantity, preset_name, block)
82
+ do_not_set_value if field_value.nil?
83
+ end
84
+ elsif field_definition[:collection] && field_definition[:collection] <= Hash
85
+ # Embeds many named
86
+ if meth == field_definition[:singular]
87
+ # Singular
88
+ grammar = :singular
89
+ variable_name = extract_variable_name(args)
90
+ args = args[1..(args.size-1)]
91
+ preset_name = args.first
92
+
93
+ field_value = build_one_subtemplate(subtemplate_definition, preset_name, block)
94
+ do_not_set_value if field_value.nil? # || field_value.empty?
50
95
  else
51
- self[meth] << field_value
96
+ # Plural
97
+ grammar = :plural
98
+ do_not_set_value = true
99
+ # UNSUPPORTED
52
100
  end
53
101
  else
54
- self[meth] = field_value
102
+ # Embeds one
103
+ preset_name = args.first
104
+
105
+ field_value = build_one_subtemplate(subtemplate_definition, preset_name, block)
106
+ do_not_set_value if field_value.nil?
107
+ end
108
+ else
109
+ raise "Could not find a template matching '#{subtemplate_name}'!"
110
+ end
111
+ elsif field_definition[:type] == :item && can_set_field?(meth)
112
+ if field_definition[:collection] && field_definition[:collection] <= Array
113
+ # Has many
114
+ if meth == field_definition[:singular]
115
+ # Singular
116
+ grammar = :singular
117
+ obj = args.count == 1 ? args.first : args
118
+
119
+ field_value = build_one_item(field_definition, obj, block)
120
+ do_not_set_value = true if field_value.nil?
121
+ else
122
+ # Plural
123
+ grammar = :plural
124
+ quantity = args.first if block && args.first.class <= Integer
125
+ arr = args.first if args.count == 1 && args.first.class <= Array
126
+
127
+ field_value = build_many_items(field_definition, quantity, arr, args, block)
128
+ do_not_set_value = true if field_value.empty?
129
+ end
130
+ elsif field_definition[:collection] && field_definition[:collection] <= Hash
131
+ # Has many named
132
+ if meth == field_definition[:singular]
133
+ # Singular
134
+ grammar = :singular
135
+ variable_name = extract_variable_name(args)
136
+ args = args[1..(args.size-1)]
137
+ obj = args.first
138
+
139
+ field_value = build_one_item(field_definition, obj, block)
140
+ do_not_set_value = true if field_value.nil?
141
+ else
142
+ # Plural
143
+ grammar = :plural
144
+ hash = args.first if args.first.class <= Hash
145
+
146
+ # Hash
147
+ if hash
148
+ field_value = hash
149
+ end
150
+ do_not_set_value = true if field_value.nil? || field_value.empty?
55
151
  end
152
+ else
153
+ # Has one
154
+ obj = args.first
155
+
156
+ field_value = build_one_item(field_definition, obj, block)
56
157
  end
57
- # No field definition
58
158
  else
59
- super # Unknown method
159
+ do_not_set_value = true
160
+ end
161
+
162
+ # Add field value to template
163
+ if !do_not_set_value
164
+ if field_definition[:collection]
165
+ self[field_definition[:name]] ||= field_definition[:collection].new
166
+ if field_definition[:collection] <= Array
167
+ if grammar == :plural
168
+ return_value = self[field_definition[:name]].concat(field_value)
169
+ elsif grammar == :singular
170
+ return_value = self[field_definition[:name]] << field_value
171
+ end
172
+ elsif field_definition[:collection] <= Hash
173
+ if grammar == :plural
174
+ return_value = self[field_definition[:name]].merge!(field_value)
175
+ elsif grammar == :singular
176
+ return_value = self[field_definition[:name]][variable_name] = field_value
177
+ end
178
+ end
179
+ else
180
+ return_value = self[field_definition[:name]] = field_value
181
+ end
60
182
  end
183
+ return_value
61
184
  end
62
185
  def transient(name, value)
63
186
  self.transients[name] = value if !(self.default_mode && self.transients.has_key?(name))
64
187
  end
65
- def add_defaults
188
+ def add_defaults(mode)
66
189
  # Prevents overwrites of custom values by defaults
67
190
  self.default_mode = true # Hackish for sure, but its efficient...
68
191
 
69
- default_definition = definition.t_default
192
+ case mode
193
+ when :before
194
+ default_definition = definition.t_before
195
+ when :after
196
+ default_definition = definition.t_after
197
+ end
198
+
70
199
  if default_definition[:evaluator]
71
200
  default_definition[:evaluator].call(self)
72
- elsif default_definition[:preset_name]
73
- preset_definition = definition.find_preset_definition(default_definition[:preset_name])
201
+ elsif default_definition[:preset]
202
+ preset_definition = definition.find_preset_definition(default_definition[:preset])
74
203
  preset_definition[:evaluator].call(self)
75
204
  end
76
205
 
@@ -81,22 +210,51 @@ module Olfactory
81
210
  macro_definition[:evaluator].call(self, *args)
82
211
  end
83
212
  end
84
- def build_subtemplate(subtemplate_definition, args, block)
213
+ def build_one_subtemplate(subtemplate_definition, preset_name, block)
214
+ # Block
85
215
  if block
86
216
  subtemplate_definition.build(block, :transients => self.transients)
217
+ # Preset Name
218
+ elsif preset_name
219
+ subtemplate_definition.build_preset(preset_name, 1, :transients => self.transients)
220
+ # Default (nothing)
221
+ else
222
+ subtemplate_definition.build(nil, :transients => self.transients)
223
+ end
224
+ end
225
+ def build_many_subtemplates(subtemplate_definition, quantity, preset_name, block)
226
+ # Integer, Block
227
+ if quantity && block
228
+ Array.new(quantity) { subtemplate_definition.build(block, :transients => self.transients) }
229
+ # Integer, Preset Name
230
+ elsif quantity && preset_name
231
+ subtemplate_definition.build_preset(preset_name, quantity, :transients => self.transients)
232
+ # Integer
233
+ elsif quantity
234
+ Array.new(quantity) { subtemplate_definition.build(nil, :transients => self.transients) }
87
235
  else
88
- subtemplate_definition.build_preset((args.count == 1 ? args.first : args), :transients => self.transients)
236
+ nil
89
237
  end
90
238
  end
91
- def build_item(item_definition, args, block)
239
+ def build_one_item(item_definition, obj, block)
92
240
  if block
93
- block.call(*args)
241
+ block.call
242
+ elsif obj
243
+ obj
94
244
  else
95
- if item_definition[:evaluator]
96
- item_definition[:evaluator].call(*args)
97
- else
98
- args.count == 1 ? args.first : argsnew
99
- end
245
+ nil
246
+ end
247
+ end
248
+ def build_many_items(item_definition, quantity, arr, args, block)
249
+ # Integer, Block
250
+ if quantity && block
251
+ Array.new(quantity) { block.call }
252
+ # Array
253
+ elsif arr
254
+ arr
255
+ # Object, Object...
256
+ else
257
+ args
100
258
  end
101
259
  end
102
260
  end
@@ -1,82 +1,147 @@
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_default
4
+ attr_accessor :t_items, :t_subtemplates, :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
9
  self.t_macros = {}
10
10
  self.t_presets = {}
11
- self.t_default = {}
11
+ self.t_before = {}
12
+ self.t_after = {}
12
13
  end
13
14
 
14
15
  def build(block, options = {})
15
- if options[:preset]
16
- self.build_preset(options[:preset], options.reject { |k,v| k == :preset })
16
+ if options[:preset] || options[:quantity]
17
+ self.build_preset(options[:preset], (options[:quantity] || 1), options.reject { |k,v| [:preset, :quantity].include?(k) })
17
18
  else
18
19
  new_template = Template.new(self, options)
19
20
  new_template.build(block, options)
20
21
  end
21
22
  new_template
22
23
  end
23
- def build_preset(preset_name, options = {})
24
- if preset_name.class <= Integer
24
+ def build_preset(preset_name, quantity, options = {})
25
+ raise "Quantity must be an integer!" if !(quantity.class <= Integer)
26
+
27
+ if quantity > 1
25
28
  # Build multiple
26
- Array.new(preset_name) { self.build(nil, options) }
27
- elsif preset_definition = self.find_preset_definition(preset_name)
28
- # Build single
29
- new_template = Template.new(self, options)
30
- preset_block = preset_definition[:evaluator]
31
- if preset_definition[:preset_name].class == Regexp
32
- new_template.build(preset_block, options.merge(:value => preset_name))
29
+ if preset_name
30
+ Array.new(quantity) { self.build_preset(preset_name, 1, options) }
33
31
  else
34
- new_template.build(preset_block, options)
32
+ Array.new(quantity) { self.build(nil, options) }
35
33
  end
36
- else
37
- raise "Missing preset matching '#{preset_value}' for template!"
34
+ elsif quantity == 1
35
+ if preset_definition = self.find_preset_definition(preset_name)
36
+ # Build single
37
+ new_template = Template.new(self, options)
38
+ preset_block = preset_definition[:evaluator]
39
+ if preset_definition[:regexp]
40
+ new_template.build(preset_block, options.merge(:value => preset_name))
41
+ else
42
+ new_template.build(preset_block, options)
43
+ end
44
+ elsif preset_name.nil?
45
+ self.build(nil, options)
46
+ else
47
+ raise "Missing preset matching '#{preset_name}' for template!"
48
+ end
49
+ else quantity <= 0
50
+ raise "Can't build 0 or less items!"
38
51
  end
39
52
  end
40
53
 
41
- def find_preset_definition(preset_name)
42
- preset_definition = self.t_presets[preset_name]
54
+ def find_field_definition(name)
55
+ definition = find_macro_definition(name)
56
+ definition ||= find_subtemplate_definition(name)
57
+ definition ||= find_item_definition(name)
58
+ definition
59
+ end
60
+
61
+ def find_definition_in_list(name, definition_list)
62
+ definition = definition_list[name]
63
+ definition ||= definition_list.values.detect do |v|
64
+ v.has_key?(:alias) && (v[:alias] == name || (v.respond_to?("include") && v.include?(name)))
65
+ end
66
+ definition
67
+ end
68
+
69
+ def find_macro_definition(name)
70
+ self.find_definition_in_list(name, self.t_macros)
71
+ end
72
+
73
+ def find_subtemplate_definition(name)
74
+ definition = self.find_definition_in_list(name, self.t_subtemplates)
75
+ definition ||= self.t_subtemplates.values.detect { |v| v.has_key?(:singular) && v[:singular] == name }
76
+ definition
77
+ end
78
+
79
+ def find_item_definition(name)
80
+ definition = self.find_definition_in_list(name, self.t_items)
81
+ definition ||= self.t_items.values.detect { |v| v.has_key?(:singular) && v[:singular] == name }
82
+ definition
83
+ end
84
+
85
+ def find_preset_definition(name)
86
+ preset_definition = self.find_definition_in_list(name, self.t_presets)
43
87
  if preset_definition.nil?
44
88
  # Try to find a regexp named preset that matches
45
- preset_name = self.t_presets.keys.detect { |p_name| p_name.class == Regexp && p_name.match(preset_name.to_s) }
46
- preset_definition = self.t_presets[preset_name] if preset_name
89
+ name = self.t_presets.keys.detect { |p_name| p_name.class == Regexp && p_name.match(name.to_s) }
90
+ preset_definition = self.t_presets[name] if name
47
91
  end
48
92
  preset_definition
49
93
  end
50
94
 
51
95
  # Defines a value holding field
52
96
  def has_one(name, options = {}, &block)
53
- self.t_items[name] = { :evaluator => block }.merge(options)
97
+ self.t_items[name] = { :type => :item,
98
+ :name => name,
99
+ :evaluator => block
100
+ }.merge(options)
54
101
  end
55
102
  def has_many(name, options = {}, &block)
56
- self.t_items[name] = { :evaluator => block, :collection => true }.merge(options)
103
+ self.has_one(name, options.merge(:collection => (options[:named] ? Hash : Array)), &block)
57
104
  end
58
105
 
59
106
  # Defines a hash-holding field
60
107
  def embeds_one(name, options = {}, &block)
61
- self.t_subtemplates[name] = { :evaluator => block }.merge(options)
108
+ self.t_subtemplates[name] = { :type => :subtemplate,
109
+ :name => name,
110
+ :evaluator => block
111
+ }.merge(options)
62
112
  end
63
113
  def embeds_many(name, options = {}, &block)
64
- self.t_subtemplates[name] = { :evaluator => block, :collection => true }.merge(options)
114
+ self.embeds_one(name, options.merge(:collection => (options[:named] ? Hash : Array)), &block)
65
115
  end
66
116
 
67
117
  # Defines a macro
68
118
  def macro(name, options = {}, &block)
69
- self.t_macros[name] = { :evaluator => block }.merge(options)
119
+ self.t_macros[name] = { :type => :macro,
120
+ :name => name,
121
+ :evaluator => block
122
+ }.merge(options)
70
123
  end
71
124
 
72
125
  # Defines a preset of values
73
126
  def preset(name, options = {}, &block)
74
- self.t_presets[name] = { :evaluator => block }.merge(options)
127
+ self.t_presets[name] = { :type => :preset,
128
+ :name => name,
129
+ :evaluator => block
130
+ }.merge(options)
131
+ self.t_presets[name] = self.t_presets[name].merge(:regexp => name) if name.class <= Regexp
75
132
  end
76
133
 
77
134
  # Defines defaults
78
- def default(preset_name = nil, options = {}, &block)
79
- self.t_default = { :preset_name => preset_name, :evaluator => block }.merge(options)
135
+ def before(options = {}, &block)
136
+ self.t_before = { :type => :default,
137
+ :evaluator => block
138
+ }.merge(options)
139
+ self.t_before = self.t_before.merge(:preset => options[:preset]) if options[:preset]
140
+ end
141
+ def after(options = {}, &block)
142
+ self.t_after = { :type => :default,
143
+ :evaluator => block }.merge(options)
144
+ self.t_after = self.t_after.merge(:preset => options[:preset]) if options[:preset]
80
145
  end
81
146
  end
82
147
  end
@@ -1,3 +1,3 @@
1
1
  module Olfactory
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/olfactory.rb CHANGED
@@ -17,6 +17,11 @@ module Olfactory
17
17
  def self.build_template(name, options = {}, &block)
18
18
  self.templates[name].build(block, options)
19
19
  end
20
+ def self.create_template(name, options = {}, &block)
21
+ template = self.templates[name].build(block, options)
22
+ template.save!
23
+ template
24
+ end
20
25
 
21
26
  def self.reload
22
27
  @@templates = {}
data/olfactory.gemspec CHANGED
@@ -16,8 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ['lib']
17
17
  s.required_ruby_version = Gem::Requirement.new(">= 1.9.2")
18
18
 
19
- s.add_development_dependency("rspec", "~> 1.3.2")
20
- s.add_development_dependency("pry")
21
- s.add_development_dependency("pry-nav")
22
- s.add_development_dependency("pry-stack_explorer")
19
+ s.add_development_dependency("rspec", "~> 3.1")
20
+ s.add_development_dependency("pry", "~> 0.10")
21
+ s.add_development_dependency("pry-nav", "~> 0.2")
22
+ s.add_development_dependency("pry-stack_explorer", "~> 0.4.9")
23
23
  end