olfactory 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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