maruto 0.0.5 → 0.0.6

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/lib/maruto.rb CHANGED
@@ -3,8 +3,4 @@ require 'maruto/magento_config'
3
3
  require 'maruto/magento_instance'
4
4
 
5
5
  module Maruto
6
- def self.warnings(magento_root)
7
- magento = MagentoInstance.load(magento_root)
8
- magento[:warnings]
9
- end
10
6
  end
@@ -75,37 +75,6 @@ module Maruto
75
75
  end
76
76
  end
77
77
 
78
- ##########################################################################################
79
- # EVENTS
80
-
81
- # TODO same for:
82
- # '/config/frontend/events/*'
83
- # '/config/adminhtml/events/*'
84
- doc.xpath('/config/global/events/*').each do |node|
85
- # puts node
86
- event = node.name
87
- observers = @global_events_observers[event] ||= {}
88
-
89
- node.xpath("observers/*").each do |observer_node|
90
- observer_name = observer_node.name
91
- if observers.include? observer_name
92
- mod_first = observers[observer_name][:defined]
93
- mod_second = mm_name
94
- @warnings << "event:#{event} observer:#{observer_name} - defined in #{mod_first} and redefined in #{mod_second}"
95
- # TODO check if there is a dependency path between mod_first and mod_second
96
- # print_module(mod_first)
97
- # print_module(mod_second)
98
- end
99
- observers[observer_name] = {
100
- :class => observer_node.at_xpath('class').content,
101
- :method => observer_node.at_xpath('method').content,
102
- :defined => mm_name,
103
- }
104
- end
105
-
106
- @global_events_observers[event] = observers
107
- end
108
-
109
78
  end
110
79
  end # modules().each
111
80
 
@@ -9,10 +9,11 @@ module Maruto::MagentoInstance
9
9
  sorted_modules, active_modules = Maruto::ModuleDefinition.analyse_module_definitions(all_modules)
10
10
 
11
11
  sorted_modules.each do |m|
12
- Maruto::ModuleConfiguration.load(m)
13
- # ModuleConfiguration.analyse(m, active_modules)
12
+ Maruto::ModuleConfiguration.parse_module_configuration(m)
14
13
  end
15
14
 
15
+ event_observers = Maruto::ModuleConfiguration.collect_event_observers(sorted_modules)
16
+
16
17
  # TODO move to function: collect_warnings + write spec
17
18
  warnings = []
18
19
  all_modules.each do |m|
@@ -20,10 +21,11 @@ module Maruto::MagentoInstance
20
21
  end
21
22
 
22
23
  {
23
- :active_modules => active_modules.map{|k,v| v},
24
- :all_modules => all_modules,
25
- :sorted_modules => sorted_modules,
26
- :warnings => warnings,
24
+ :active_modules => active_modules,
25
+ :all_modules => Hash[all_modules.collect { |m| [m[:name], m]}],
26
+ :sorted_modules => sorted_modules,
27
+ :event_observers => event_observers,
28
+ :warnings => warnings,
27
29
  }
28
30
  end
29
31
  end
@@ -3,46 +3,58 @@ require 'nokogiri'
3
3
 
4
4
  module Maruto::ModuleConfiguration
5
5
 
6
- def self.load(m)
6
+ def self.parse_module_configuration(m)
7
7
  f = File.open(m[:config_path])
8
- doc = Nokogiri::XML(f) { |config| config.strict }
8
+ xml_root = Nokogiri::XML(f) { |config| config.strict }.root
9
9
  f.close
10
10
 
11
- read_module_version(m, doc.root)
11
+ version_warnings = parse_module_version(m, xml_root)
12
+ event_warnings = parse_all_event_observers(m, xml_root)
13
+
14
+ config_warnings = version_warnings + event_warnings
15
+
16
+ all_module_warnings = m[:warnings] || []
17
+ all_module_warnings.concat(config_warnings.map { |msg| { :file => m[:config_path], :message => msg } })
18
+
19
+ m[:warnings] = all_module_warnings unless all_module_warnings.size == 0
20
+
21
+ m
12
22
  end
13
23
 
14
- def self.read_module_version(m, xml_root)
24
+ def self.parse_module_version(m, xml_root)
15
25
  xml_node = xml_root.at_xpath('/config/modules')
26
+
16
27
  if xml_node.nil?
17
- m[:warnings] ||= []
18
- m[:warnings] << { :file => m[:config_path], :message => "config.xml is missing a /config/modules node" }
19
- return m
28
+ return ["config.xml is missing a /config/modules node"]
20
29
  end
21
30
 
31
+ warnings = []
32
+
22
33
  unless xml_node.at_xpath("./#{m[:name]}")
23
- m[:warnings] ||= []
24
- m[:warnings] << { :file => m[:config_path], :message => "config.xml is missing a /config/modules/#{m[:name]} node" }
34
+ warnings << "config.xml is missing a /config/modules/#{m[:name]} node"
25
35
  end
26
36
 
27
37
  xml_node.xpath("./*").each do |n|
28
38
  unless n.name.to_sym == m[:name]
29
- m[:warnings] ||= []
30
- m[:warnings] << { :file => m[:config_path], :message => "config.xml contains configuration for a different module (/config/modules/#{n.name})" }
39
+ warnings << "config.xml contains configuration for a different module (/config/modules/#{n.name})"
31
40
  end
32
41
  end
33
42
 
34
- if xml_node.at_xpath("./#{m[:name]}/version")
35
- m[:version] = xml_node.at_xpath("./#{m[:name]}/version").content
36
- end
43
+ m[:version] = xml_node.at_xpath("./#{m[:name]}/version").content unless xml_node.at_xpath("./#{m[:name]}/version").nil?
37
44
 
38
- m
45
+ warnings
39
46
  end
40
47
 
41
- def self.parse_scoped_events_observers(base_path, xml_node)
48
+ def self.parse_scoped_event_observers(base_path, xml_node)
49
+
50
+ return [],[] if xml_node.nil?
42
51
 
43
- return [] if xml_node.nil?
52
+ events = []
53
+ warnings = []
44
54
 
45
- events = []
55
+ if xml_node.size > 1
56
+ warnings << "duplicate element in config.xml (#{base_path})"
57
+ end
46
58
 
47
59
  xml_node.xpath('events/*').each do |e|
48
60
  event = {
@@ -56,9 +68,25 @@ module Maruto::ModuleConfiguration
56
68
  :name => o.name,
57
69
  :path => event[:path] + '/observers/' + o.name,
58
70
  }
59
- observer[:type] = o.at_xpath('type').content
60
- observer[:class] = o.at_xpath('class').content
61
- observer[:method] = o.at_xpath('method').content
71
+ type = o.at_xpath('type').content unless o.at_xpath('type').nil?
72
+ observer[:class] = o.at_xpath('class').content unless o.at_xpath('class').nil?
73
+ observer[:method] = o.at_xpath('method').content unless o.at_xpath('method').nil?
74
+
75
+ # see Mage_Core_Model_App::dispatchEvent
76
+ if type.nil?
77
+ # default is singleton
78
+ observer[:type] = :singleton
79
+ elsif type == 'object'
80
+ # object is an alias for model
81
+ observer[:type] = :model
82
+ warnings << "#{observer[:path]}/type 'object' is an alias for 'model'"
83
+ elsif /^(disabled|model|singleton)$/ =~ type
84
+ observer[:type] = type.to_sym
85
+ else
86
+ # everything else => default (with warning)
87
+ observer[:type] = :singleton
88
+ warnings << "#{observer[:path]}/type replaced with 'singleton', was '#{type}' (possible values: 'disabled', 'model', 'singleton', or nothing)"
89
+ end
62
90
 
63
91
  event[:observers] << observer
64
92
  end
@@ -66,17 +94,65 @@ module Maruto::ModuleConfiguration
66
94
  events << event
67
95
  end
68
96
 
97
+ return events, warnings
98
+ end
99
+
100
+ def self.parse_all_event_observers(m, xml_node)
101
+ areas = [:global, :frontend, :adminhtml, :crontab]
102
+ events = {}
103
+ warnings = []
104
+ areas.each do |area|
105
+ e, w = parse_scoped_event_observers("/config/#{area}", xml_node.xpath("/config/#{area}"))
106
+
107
+ events[area] = e if e.size > 0
108
+ warnings.concat w
109
+ end
110
+ m[:events] = events if events.keys.size > 0
111
+
112
+ warnings << "the 'admin' area should not contain events (/config/admin/events)" unless xml_node.at_xpath("/config/admin/events").nil?
113
+
114
+ return warnings
115
+ end
116
+
117
+ def self.collect_scoped_event_observers(area, sorted_modules)
118
+ events = Hash.new
119
+
120
+ sorted_modules.each do |m|
121
+ if m.include? :events and m[:events].include? area then
122
+ m[:events][area].each do |event|
123
+ event_name = event[:name]
124
+ events[event_name] ||= Hash.new
125
+ event[:observers].each do |observer|
126
+ observer_name = observer[:name]
127
+ if events[event_name].include? observer_name
128
+ add_module_config_warning(m, "event_observer:#{area}/#{event_name}/#{observer_name} - defined in #{events[event_name][observer_name][:module]} and redefined in #{m[:name]}")
129
+ end
130
+ events[event_name][observer_name] = observer
131
+ events[event_name][observer_name][:module] = m[:name]
132
+ end
133
+ end
134
+ end
135
+ end
136
+
69
137
  events
70
138
  end
71
139
 
72
- def self.parse_all_events_observers(base_path, xml_node)
73
- # TODO handle multiple global / frontend / adminhtml nodes
74
- h = {
75
- :global => parse_scoped_events_observers(base_path + '/global', xml_node.at_xpath('global')),
76
- :frontend => parse_scoped_events_observers(base_path + '/frontend', xml_node.at_xpath('frontend')),
77
- :adminhtml => parse_scoped_events_observers(base_path + '/adminhtml', xml_node.at_xpath('adminhtml')),
78
- }
79
- h.delete_if {|k,v| v.size == 0}
140
+ def self.collect_event_observers(sorted_modules)
141
+ areas = [:global, :frontend, :adminhtml, :crontab]
142
+ events = {}
143
+
144
+ areas.each do |area|
145
+ events[area] = collect_scoped_event_observers(area, sorted_modules)
146
+ end
147
+
148
+ events
149
+ end
150
+
151
+ private
152
+
153
+ def self.add_module_config_warning(m, msg)
154
+ m[:warnings] ||= []
155
+ m[:warnings] << { :file => m[:config_path], :message => msg }
80
156
  end
81
157
 
82
158
  end
@@ -90,6 +90,11 @@ module Maruto::ModuleDefinition
90
90
  m[:active] = false
91
91
  end
92
92
  end
93
+ else
94
+ if m[:code_pool] == :core and m[:name].to_s.start_with? 'Mage_' then
95
+ m[:warnings] ||= []
96
+ m[:warnings] << { :file => m[:defined], :message => "core/#{m[:name]} is inactive, but models and helpers will still be loaded" }
97
+ end
93
98
  end
94
99
  end
95
100
  # remove inactive modules
data/lib/maruto/runner.rb CHANGED
@@ -43,6 +43,7 @@ class Maruto::Runner < Thor
43
43
 
44
44
  desc "warnings", "list potential problems found in the config"
45
45
  method_option :magento_root, :aliases => "-m", :default => "."
46
+ method_option :with_core, :type => :boolean, :aliases => "-c", :default => false
46
47
  def warnings()
47
48
 
48
49
  magento_root = check_magento_folder()
@@ -52,11 +53,19 @@ class Maruto::Runner < Thor
52
53
 
53
54
  # next gen maruto:
54
55
 
55
- all_warnings = Maruto::warnings magento_root
56
- all_warnings.group_by { |e| e[:module] }.each do |m,module_warnings|
57
- puts "[module:#{m}]"
58
- module_warnings.each do |w|
59
- puts " [file:#{w[:file]}] #{w[:message]}"
56
+ with_core = options[:with_core]
57
+
58
+ magento = Maruto::MagentoInstance.load(magento_root)
59
+
60
+ magento[:warnings].group_by { |e| e[:module] }.each do |m,module_warnings|
61
+ if with_core or magento[:all_modules][m][:code_pool] != :core then
62
+ puts "[module:#{m}]"
63
+ module_warnings.group_by { |e| e[:file] }.each do |file,warnings|
64
+ puts " [file:#{file}]"
65
+ warnings.each do |w|
66
+ puts " #{w[:message]}"
67
+ end
68
+ end
60
69
  end
61
70
  end
62
71
 
@@ -77,18 +86,41 @@ class Maruto::Runner < Thor
77
86
 
78
87
  end
79
88
 
80
- desc "observers", "list observers sorted and grouped by their events"
89
+ desc "observers", "list observers sorted and grouped by their event or area"
81
90
  method_option :magento_root, :aliases => "-m", :default => "."
91
+ method_option :group_by_scope, :type => :boolean, :aliases => "-s", :default => false
82
92
  def observers()
83
93
 
84
94
  magento_root = check_magento_folder()
85
95
 
86
- magento_config = Maruto::MagentoConfig.new magento_root
96
+ magento = Maruto::MagentoInstance.load(magento_root)
97
+
98
+ group_by_scope = options[:group_by_scope]
87
99
 
88
- magento_config.observers.sort_by { |k, v| k }.each do |event, observers|
89
- puts event
90
- observers.each do |observer|
91
- puts " #{observer}"
100
+ if group_by_scope then
101
+ magento[:event_observers].each do |area, events|
102
+ events.each do |event, observers|
103
+ puts "#{area}/#{event}"
104
+ observers.each do |name, observer|
105
+ puts " #{name} (module:#{observer[:module]} type:#{observer[:type]} class:#{observer[:class]} method:#{observer[:method]})"
106
+ end
107
+ end
108
+ end
109
+ else
110
+ grouped_by_events = Hash.new
111
+ magento[:event_observers].each do |area, events|
112
+ events.each do |event, observers|
113
+ grouped_by_events[event] ||= Hash.new
114
+ grouped_by_events[event][area] = observers
115
+ end
116
+ end
117
+ grouped_by_events.sort_by { |k, v| k }.each do |event, areas|
118
+ puts "#{event}"
119
+ areas.each do |area, observers|
120
+ observers.each do |name, observer|
121
+ puts " #{area}/#{name} (module:#{observer[:module]} type:#{observer[:type]} class:#{observer[:class]} method:#{observer[:method]})"
122
+ end
123
+ end
92
124
  end
93
125
  end
94
126
 
@@ -1,3 +1,3 @@
1
1
  module Maruto
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -61,6 +61,9 @@ describe Maruto::ModuleDefinition do
61
61
  end
62
62
 
63
63
  it "will find dependencies" do
64
+ h = Maruto::ModuleDefinition.parse_module_definition(@xml_nodes[:Mage_Core])
65
+ h.wont_include :dependencies
66
+
64
67
  h = Maruto::ModuleDefinition.parse_module_definition(@xml_nodes[:Mage_Eav])
65
68
  h.must_include :dependencies
66
69
  h[:dependencies].size.must_equal 1
@@ -179,15 +182,25 @@ describe Maruto::ModuleDefinition do
179
182
  end
180
183
  it "will not include inactive modules (in Array or Hash)" do
181
184
  parsed_module_definitions = [
182
- @module_a.merge({ :active => false}),
185
+ @module_a.merge({ :active => false }),
186
+ ]
187
+ a,h = Maruto::ModuleDefinition.analyse_module_definitions(parsed_module_definitions)
188
+ a.size.must_equal 0
189
+ h.size.must_equal 0
190
+ end
191
+ it "will warn when a core/Mage_ module is inactive" do
192
+ parsed_module_definitions = [
193
+ @module_a.merge({ :active => false, :warnings => ['first warning'] }),
183
194
  ]
184
195
  a,h = Maruto::ModuleDefinition.analyse_module_definitions(parsed_module_definitions)
185
196
  a.size.must_equal 0
186
197
  h.size.must_equal 0
198
+ parsed_module_definitions[0][:warnings].size.must_equal 2
199
+ parsed_module_definitions[0][:warnings][-1][:message].must_include 'inactive'
187
200
  end
188
201
  it "will remove missing dependencies and add a warning" do
189
202
  parsed_module_definitions = [
190
- @module_a.merge({ :active => true, :dependencies => [:Mage_B, :Mage_C], :warnings => ['first warning']}),
203
+ @module_a.merge({ :active => true, :dependencies => [:Mage_B, :Mage_C], :warnings => ['first warning'] }),
191
204
  @module_b.merge({ :active => true }),
192
205
  @module_c.merge({ :active => false }),
193
206
  ]
@@ -198,7 +211,7 @@ describe Maruto::ModuleDefinition do
198
211
  end
199
212
  it "will remove duplicate dependencies and add a warning" do
200
213
  parsed_module_definitions = [
201
- @module_a.merge({ :active => true, :dependencies => [:Mage_B, :Mage_C, :Mage_B], :warnings => ['first warning']}),
214
+ @module_a.merge({ :active => true, :dependencies => [:Mage_B, :Mage_C, :Mage_B], :warnings => ['first warning'] }),
202
215
  @module_b.merge({ :active => true }),
203
216
  @module_c.merge({ :active => true }),
204
217
  ]
@@ -6,166 +6,426 @@ module Maruto
6
6
 
7
7
  describe ModuleConfiguration do
8
8
 
9
- describe "when parsing a module config.xml" do
10
- describe "and reading events observers" do
11
- before do
12
- @xml_node = Nokogiri::XML('''
13
- <events>
14
- <first_event>
15
- <observers>
16
- <first_observer>
17
- <type>model</type> <!-- model, object or singleton -->
18
- <class>Mage_A_Model_Observer</class> <!-- observers class or class alias -->
19
- <method>methodName</method> <!-- observer\'s method to be called -->
20
- <args></args> <!-- additional arguments passed to observer -->
21
- </first_observer>
22
- </observers>
23
- </first_event>
24
- <second_event>
25
- <observers>
26
- <second_observer>
27
- <type>object</type>
28
- <class>Mage_B_Model_Observer</class>
29
- <method>doSomething</method>
30
- </second_observer>
31
- <third_observer>
32
- <type>singleton</type>
33
- <class>Mage_C_Model_Observer</class>
34
- <method>helloWorld</method>
35
- </third_observer>
36
- </observers>
37
- </second_event>
38
- </events>
39
- ''').root.at_xpath('/')
40
- end
41
- it "will return a array of events" do
42
- e = ModuleConfiguration.parse_scoped_events_observers('', @xml_node)
43
- e.must_be_kind_of Array
44
- e.size.must_equal 2
45
- end
46
- it "will handle a missing <events> element or a nil node" do
47
- node = Nokogiri::XML('''
48
- <hello></hello>
49
- ''').root.at_xpath('/')
50
- e = ModuleConfiguration.parse_scoped_events_observers('', node)
51
- e.must_be_kind_of Array
52
- e.size.must_equal 0
53
-
54
- e = ModuleConfiguration.parse_scoped_events_observers('', nil)
55
- e.must_be_kind_of Array
56
- e.size.must_equal 0
57
- end
58
- it "will build the path to an event" do
59
- e = ModuleConfiguration.parse_scoped_events_observers('root', @xml_node)
60
- e[0][:path].must_equal 'root/events/first_event'
61
- end
62
- it "will read the name type class and method of an observer" do
63
- e = ModuleConfiguration.parse_scoped_events_observers('', @xml_node)
64
- e[0][:observers][0][:name].must_equal 'first_observer'
65
- e[0][:observers][0][:type].must_equal 'model'
66
- e[0][:observers][0][:class].must_equal 'Mage_A_Model_Observer'
67
- e[0][:observers][0][:method].must_equal 'methodName'
68
- end
69
- it "will build the path to an event observer" do
70
- e = ModuleConfiguration.parse_scoped_events_observers('root', @xml_node)
71
- e[0][:observers][0][:path].must_equal 'root/events/first_event/observers/first_observer'
72
- end
73
- it "will group observers by events" do
74
- e = ModuleConfiguration.parse_scoped_events_observers('', @xml_node)
75
- e.size.must_equal 2
76
- e[0].must_include :name
77
- e[0].must_include :observers
78
- e[0][:name].must_equal 'first_event'
79
- e[0][:observers].size.must_equal 1
80
- e[1][:name].must_equal 'second_event'
81
- e[1][:observers].size.must_equal 2
82
- end
83
9
 
84
- # TODO multiple global xml nodes?
85
-
86
- # it "will warn when an event has no observers" do
87
- # @xml_node_no_obs = Nokogiri::XML('''
88
- # <events><first_event></first_event></events>
89
- # ''').root.at_xpath('/events')
90
- # e = ModuleConfiguration.parse_events_observers(@xml_node_no_obs)
91
- # e.size.must_equal 1
92
- # e[0].must_include :warnings
93
- # e[0][:warnings].size.must_equal 1
94
- # end
95
- # it "will warn when an event has an empty observers node" do
96
- # @xml_node_empty_obs = Nokogiri::XML('''
97
- # <events><first_event><observers></observers></first_event></events>
98
- # ''').root.at_xpath('/events')
99
- # e = ModuleConfiguration.parse_events_observers(@xml_node_empty_obs)
100
- # e.size.must_equal 1
101
- # e[0][:warnings].size.must_equal 1
102
- # end
103
- # it "will warn if an observers type is invalid" do
104
- # e = ModuleConfiguration.parse_events_observers(@xml_node)
105
- # e[1][:warnings].size.must_equal 1
106
- # e[1][:warnings][0].must_include '<type>something</type>'
107
- # end
108
- # it "will warn if an observers class is invalid" do
109
- # # TODO
110
- # end
10
+ describe "when parsing a module config.xml and reading scoped event observers" do
11
+
12
+ before do
13
+ @events_root = Nokogiri::XML('''
14
+ <events>
15
+ <first_event>
16
+ <observers>
17
+ <first_observer>
18
+ <type>model</type> <!-- model, singleton or disabled, default is singleton (object is an alias for model) -->
19
+ <class>Mage_A_Model_Observer</class> <!-- observers class or class alias -->
20
+ <method>methodName</method> <!-- observer\'s method to be called -->
21
+ <args></args> <!-- additional arguments passed to observer -->
22
+ </first_observer>
23
+ </observers>
24
+ </first_event>
25
+ <second_event>
26
+ <observers>
27
+ <second_observer>
28
+ <class>Mage_B_Model_Observer</class>
29
+ <method>doSomething</method>
30
+ </second_observer>
31
+ <third_observer>
32
+ <type>object</type>
33
+ <class>Mage_C_Model_Observer</class>
34
+ <method>helloWorld</method>
35
+ </third_observer>
36
+ <disabled_observer>
37
+ <type>disabled</type>
38
+ <class>Mage_C_Model_Observer</class>
39
+ <method>helloWorld</method>
40
+ </disabled_observer>
41
+ </observers>
42
+ </second_event>
43
+ </events>
44
+ ''').root.xpath('/')
111
45
  end
112
46
 
113
- it "will group events by scope and build the correct path" do
47
+ it "will return a array of events" do
48
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
49
+ events.must_be_kind_of Array
50
+ events.size.must_equal 2
51
+ end
52
+ it "will return a array of warnings" do
53
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
54
+ warnings.must_be_kind_of Array
55
+ end
56
+ it "will handle a missing <events> element or a nil node" do
57
+ node = Nokogiri::XML('''
58
+ <hello></hello>
59
+ ''').root.xpath('/')
60
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', node)
61
+ events.must_be_kind_of Array
62
+ events.size.must_equal 0
63
+
64
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', nil)
65
+ events.must_be_kind_of Array
66
+ events.size.must_equal 0
67
+ end
68
+ it "will build the path to an event" do
69
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', @events_root)
70
+ events[0][:path].must_equal 'root/events/first_event'
71
+ end
72
+ it "will read the name, class, and method of an observer" do
73
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
74
+ events[0][:observers][0][:name].must_equal 'first_observer'
75
+ events[0][:observers][0][:class].must_equal 'Mage_A_Model_Observer'
76
+ events[0][:observers][0][:method].must_equal 'methodName'
77
+ end
78
+ # see Mage_Core_Model_App::dispatchEvent
79
+ it "will read the type of an observer" do
80
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
81
+ events[0][:observers][0][:type].must_equal :model
82
+ end
83
+ it "will handle observer declarations with default type" do
84
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
85
+ events[1][:observers][0][:type].must_equal :singleton
86
+ end
87
+ it "will treat type 'object' as an alias to 'model'" do
88
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
89
+ events[1][:observers][1][:type].must_equal :model
90
+ end
91
+ it "will handle disabled observers" do
92
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
93
+ events[1][:observers][2][:type].must_equal :disabled
94
+ end
95
+ it "will handle observers with an invalid type and add a warning" do
96
+ node = Nokogiri::XML('''
97
+ <events>
98
+ <first_event>
99
+ <observers>
100
+ <invalid_type>
101
+ <type>something</type>
102
+ <class>Hello_World</class>
103
+ <method>helloWorld</method>
104
+ </invalid_type>
105
+ </observers>
106
+ </first_event>
107
+ </events>
108
+ ''').root.xpath('/')
109
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', node)
110
+ events.size.must_equal 1
111
+ events[0][:observers].size.must_equal 1
112
+ events[0][:observers][0].must_include :type
113
+ events[0][:observers][0][:type].must_equal :singleton
114
+ warnings.size.must_equal 1
115
+ warnings[0].must_include 'root/events/first_event/observers/invalid_type/type'
116
+ warnings[0].must_include 'singleton'
117
+ warnings[0].must_include 'something'
118
+ end
119
+ it "will build the path to an event observer" do
120
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', @events_root)
121
+ events[0][:observers][0][:path].must_equal 'root/events/first_event/observers/first_observer'
122
+ end
123
+ it "will group observers by event" do
124
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('', @events_root)
125
+ events.size.must_equal 2
126
+ events[0].must_include :name
127
+ events[0].must_include :observers
128
+ events[0][:name].must_equal 'first_event'
129
+ events[0][:observers].size.must_equal 1
130
+ events[1][:name].must_equal 'second_event'
131
+ events[1][:observers].size.must_equal 3
132
+ end
133
+ it "will handle incomplete observer declarations" do
134
+ node = Nokogiri::XML('''
135
+ <events>
136
+ <first_event>
137
+ <observers>
138
+ <o1></o1>
139
+ <o2>
140
+ <class></class>
141
+ <method></method>
142
+ </o2>
143
+ </observers>
144
+ </first_event>
145
+ </events>
146
+ ''').root.xpath('/')
147
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', node)
148
+ events.size.must_equal 1
149
+ events[0][:observers].size.must_be :>, 0
150
+ end
151
+ # it "will warn if an observers class is invalid" do
152
+ # # TODO
153
+ # end
154
+ it "will handle duplicate areas and add a warning" do
114
155
  node = Nokogiri::XML('''<config>
115
- <global>
156
+ <area>
116
157
  <events>
117
158
  <e1><observers></observers></e1>
118
159
  </events>
119
- </global>
120
- <frontend>
160
+ </area>
161
+ <area>
121
162
  <events>
122
163
  <e2><observers></observers></e2>
123
- <e3></e3>
124
164
  </events>
125
- </frontend>
126
- <adminhtml>
127
- <events>
128
- <e4></e4>
129
- <e5></e5>
130
- <e6></e6>
131
- </events>
132
- </adminhtml>
165
+ </area>
133
166
  </config>''').root
167
+ events, warnings = ModuleConfiguration.parse_scoped_event_observers('/config/area', node.xpath('/config/area'))
168
+ events.size.must_equal 2
169
+ warnings.size.must_equal 1
170
+ warnings[0].must_include '/config/area'
171
+ end
172
+ end
173
+
174
+ describe "when parsing all event observers" do
175
+
176
+ before do
177
+ @module_a = { :name => :Mage_A, :active => true, :code_pool => :core, :defined => 'a', :config_path => 'app/code/core/Mage/A/etc/config.xml' }
178
+ @xml_root = Nokogiri::XML('''
179
+ <config>
180
+ <global>
181
+ <events>
182
+ <e1><observers><o1><type>invalid</type></o1></observers></e1>
183
+ </events>
184
+ </global>
185
+ <frontend>
186
+ <events>
187
+ <e2><observers></observers></e2>
188
+ <e3></e3>
189
+ </events>
190
+ </frontend>
191
+ <adminhtml>
192
+ <events>
193
+ <e4></e4>
194
+ <e5></e5>
195
+ <e6></e6>
196
+ </events>
197
+ </adminhtml>
198
+ <crontab>
199
+ <events>
200
+ <e1></e1>
201
+ </events>
202
+ </crontab>
203
+ </config>
204
+ ''').root
205
+ end
206
+
207
+ it "will add events to the module" do
208
+ @module_a.wont_include :events
209
+ ModuleConfiguration.parse_all_event_observers(@module_a, @xml_root)
210
+ @module_a.must_include :events
211
+ end
212
+
213
+ it "will collect warnings into an array" do
214
+ warnings = ModuleConfiguration.parse_all_event_observers(@module_a, @xml_root)
215
+ warnings.size.must_be :>, 0
216
+ end
217
+
218
+
219
+ it "will group events by scope and build the correct path" do
220
+ @module_a.wont_include :events
221
+ ModuleConfiguration.parse_all_event_observers(@module_a, @xml_root)
222
+ @module_a.must_include :events
134
223
 
135
- e = ModuleConfiguration.parse_all_events_observers('root', node)
224
+ @module_a[:events].must_be_kind_of Hash
225
+ @module_a[:events].must_include :global
226
+ @module_a[:events].must_include :frontend
227
+ @module_a[:events].must_include :adminhtml
136
228
 
137
- e.must_be_kind_of Hash
138
- e.must_include :global
139
- e.must_include :frontend
140
- e.must_include :adminhtml
229
+ @module_a[:events][:global].size.must_equal 1
230
+ @module_a[:events][:global][0][:name].must_equal 'e1'
231
+ @module_a[:events][:global][0][:path].must_include '/config/global/events'
141
232
 
142
- e[:global].size.must_equal 1
143
- e[:global][0][:name].must_equal 'e1'
144
- e[:global][0][:path].must_include 'root/global/events'
233
+ @module_a[:events][:frontend].size.must_equal 2
234
+ @module_a[:events][:frontend][0][:name].must_equal 'e2'
235
+ @module_a[:events][:frontend][0][:path].must_include '/config/frontend/events'
145
236
 
146
- e[:frontend].size.must_equal 2
147
- e[:frontend][0][:name].must_equal 'e2'
148
- e[:frontend][0][:path].must_include 'root/frontend/events'
237
+ @module_a[:events][:adminhtml].size.must_equal 3
238
+ @module_a[:events][:adminhtml][0][:name].must_equal 'e4'
239
+ @module_a[:events][:adminhtml][0][:path].must_include '/config/adminhtml/events'
149
240
 
150
- e[:adminhtml].size.must_equal 3
151
- e[:adminhtml][0][:name].must_equal 'e4'
152
- e[:adminhtml][0][:path].must_include 'root/adminhtml/events'
241
+ @module_a[:events][:crontab].size.must_equal 1
242
+ @module_a[:events][:crontab][0][:name].must_equal 'e1'
243
+ @module_a[:events][:crontab][0][:path].must_include '/config/crontab/events'
153
244
  end
245
+
154
246
  it "will skip scopes without events" do
155
- node = Nokogiri::XML('''
247
+ node = Nokogiri::XML('''<config>
248
+ <global>
249
+ <events>
250
+ <e1><observers><o1></o1></observers></e1>
251
+ </events>
252
+ </global>
253
+ </config>''').root
254
+
255
+ @module_a.wont_include :events
256
+ ModuleConfiguration.parse_all_event_observers(@module_a, node)
257
+ @module_a.must_include :events
258
+
259
+ @module_a[:events].wont_include :frontend
260
+ @module_a[:events].wont_include :adminhtml
261
+ end
262
+
263
+ it "will skip :events without events" do
264
+ node = Nokogiri::XML('''<config>
156
265
  <global>
157
266
  <events>
158
267
  </events>
159
268
  </global>
160
269
  <frontend>
161
270
  </frontend>
162
- ''').root
163
- e = ModuleConfiguration.parse_all_events_observers('', node)
164
- e.must_be_kind_of Hash
165
- e.wont_include :global
166
- e.wont_include :frontend
167
- e.wont_include :adminhtml
271
+ </config>''').root
272
+
273
+ @module_a.wont_include :events
274
+ warnings = ModuleConfiguration.parse_all_event_observers(@module_a, node)
275
+ @module_a.wont_include :events
276
+
277
+ warnings.size.must_equal 0
278
+ end
279
+
280
+ it "will add a warning for events in the 'admin' area" do
281
+ node = Nokogiri::XML('''<config>
282
+ <admin>
283
+ <events>
284
+ </events>
285
+ </admin>
286
+ </config>''').root
287
+
288
+ @module_a.wont_include :events
289
+ warnings = ModuleConfiguration.parse_all_event_observers(@module_a, node)
290
+ @module_a.wont_include :events
291
+
292
+ warnings.size.must_equal 1
293
+ end
294
+
295
+ end
296
+
297
+ describe "when collecting all event observers" do
298
+ before do
299
+ @module_a = { :name => :Mage_A, :active => true, :code_pool => :core, :defined => 'a', :config_path => 'app/code/core/Mage/A/etc/config.xml' }
300
+ @module_b = { :name => :Mage_B, :active => true, :code_pool => :core, :defined => 'b', :config_path => 'app/code/core/Mage/B/etc/config.xml' }
301
+ @module_c = { :name => :Mage_C, :active => true, :code_pool => :core, :defined => 'c', :config_path => 'app/code/core/Mage/C/etc/config.xml' }
302
+ @module_d = { :name => :Mage_D, :active => true, :code_pool => :core, :defined => 'd', :config_path => 'app/code/core/Mage/D/etc/config.xml' }
303
+
304
+ @observer_1 = { :name => 'a_o1', :type => 'singleton', :class => 'Mage_A_Model_Observer', :method => 'global_1' }
305
+
306
+ @module_a[:events] = {
307
+ :global => [{ :name => 'e1', :observers => [
308
+ @observer_1,
309
+ { :name => 'a_o2', :type => 'singleton', :class => 'Mage_A_Model_Observer', :method => 'global_2' }
310
+ ] }],
311
+ :adminhtml => [{ :name => 'e1', :observers => [
312
+ { :name => 'a_o1', :type => 'singleton', :class => 'Mage_A_Model_Observer', :method => 'adminhtml_1' }
313
+ ] }],
314
+ :frontend => [{ :name => 'e1', :observers => [
315
+ { :name => 'a_o1', :type => 'singleton', :class => 'Mage_A_Model_Observer', :method => 'frontend_1' }
316
+ ] }]
317
+ }
318
+
319
+ @sorted_modules = [@module_a]
320
+ end
321
+
322
+ it "will group observers by event" do
323
+ h = ModuleConfiguration.collect_scoped_event_observers(:global, @sorted_modules)
324
+ h.must_include 'e1'
325
+ h['e1'].must_include 'a_o1'
326
+ h['e1'].must_include 'a_o2'
327
+
328
+ [:name, :type, :class, :method].each do |key|
329
+ h['e1']['a_o1'].must_include key
330
+ end
331
+
332
+ @module_a.wont_include :warnings
333
+ end
334
+ it "will add the source module to the observer" do
335
+ h, w = ModuleConfiguration.collect_scoped_event_observers(:global, @sorted_modules)
336
+ h['e1']['a_o1'].must_include :module
337
+ h['e1']['a_o1'][:module].must_equal :Mage_A
338
+ @module_a.wont_include :warnings
168
339
  end
340
+ it "will add a warning when overwriting an observer" do
341
+ @module_b[:events] = {
342
+ :global => [{ :name => 'e1', :observers => [ @observer_1 ] }]
343
+ }
344
+ h = ModuleConfiguration.collect_scoped_event_observers(:global, [@module_a, @module_b])
345
+
346
+ @module_b.must_include :warnings
347
+ @module_b[:warnings].size.must_equal 1
348
+ end
349
+ it "will add a warning when overwriting an observer without module dependency" do
350
+ # TODO
351
+ end
352
+ it "wont add a warning when overwriting an observer to disable it" do
353
+ # TODO
354
+ end
355
+ it "will add a warning when disabling a non-existing observer" do
356
+ # TODO
357
+ end
358
+
359
+
360
+ it "will include all areas" do
361
+ h = ModuleConfiguration.collect_event_observers([])
362
+
363
+ h.keys.size.must_equal 4
364
+
365
+ h.must_include :global
366
+ h.must_include :adminhtml
367
+ h.must_include :frontend
368
+ h.must_include :crontab
369
+
370
+ h[:global].must_be_kind_of Hash
371
+ h[:global].size.must_equal 0
372
+
373
+ h[:adminhtml].must_be_kind_of Hash
374
+ h[:adminhtml].size.must_equal 0
375
+
376
+ h[:frontend].must_be_kind_of Hash
377
+ h[:frontend].size.must_equal 0
378
+
379
+ h[:crontab].must_be_kind_of Hash
380
+ h[:crontab].size.must_equal 0
381
+ end
382
+
383
+ it "will group observers by area" do
384
+ h = ModuleConfiguration.collect_event_observers(@sorted_modules)
385
+
386
+ h[:global].must_include 'e1'
387
+ h[:global]['e1'].must_include 'a_o1'
388
+ h[:global]['e1'].must_include 'a_o2'
389
+
390
+ h[:adminhtml].must_include 'e1'
391
+ h[:adminhtml]['e1'].must_include 'a_o1'
392
+ h[:adminhtml]['e1'].wont_include 'a_o2'
393
+
394
+ h[:frontend].must_include 'e1'
395
+ h[:frontend]['e1'].must_include 'a_o1'
396
+ h[:frontend]['e1'].wont_include 'a_o2'
397
+
398
+ h[:crontab].wont_include 'e1'
399
+
400
+ @module_a.wont_include :warnings
401
+ end
402
+ end
403
+
404
+ describe "when analysing event observers" do
405
+
406
+ it "will add a warning when an observer has already been declared" do
407
+
408
+ end
409
+
410
+ # it "will warn when an event has no observers" do
411
+ # xml_node_no_obs = Nokogiri::XML('''
412
+ # <config><scope><events><first_event></first_event></events></scope></config>
413
+ # ''').root
414
+ # events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', xml_node_no_obs.xpath('/config/scope'))
415
+ # events.size.must_equal 0
416
+ # warnings.size.must_equal 1
417
+ # warnings[0].must_include 'root/events/first_event'
418
+ # end
419
+ # it "will warn when an event has an empty observers node" do
420
+ # xml_node_empty_obs = Nokogiri::XML('''
421
+ # <config><scope><events><first_event><observers></observers></first_event></events></scope></config>
422
+ # ''').root
423
+ # events, warnings = ModuleConfiguration.parse_scoped_event_observers('root', xml_node_empty_obs.xpath('/config/scope'))
424
+ # events.size.must_equal 0
425
+ # warnings.size.must_equal 1
426
+ # warnings[0].must_include 'root/events/first_event/observers'
427
+ # end
428
+
169
429
  end
170
430
  end
171
431
  end
@@ -20,29 +20,24 @@ module Maruto
20
20
  ''').root
21
21
  end
22
22
 
23
- it "will return the module definition hash" do
24
- m = ModuleConfiguration.read_module_version(@module_a, @xml_config_root)
25
- m.must_be_kind_of Hash
26
- m.must_equal @module_a
23
+ it "will return the an array of warnings" do
24
+ warnings = ModuleConfiguration.parse_module_version(@module_a, @xml_config_root)
25
+ warnings.must_be_kind_of Array
27
26
  end
28
- it "will add the version to the hash" do
29
- m = ModuleConfiguration.read_module_version(@module_a, @xml_config_root)
30
- m.must_include :version
31
- m[:version].must_equal '0.0.1'
27
+ it "will add the version to the module" do
28
+ ModuleConfiguration.parse_module_version(@module_a, @xml_config_root)
29
+ @module_a.must_include :version
30
+ @module_a[:version].must_equal '0.0.1'
32
31
  end
33
32
  it "will add a warning when then modules node is missing" do
34
- w = @module_a.merge({ :warnings => ['first warning'] })
35
- m = ModuleConfiguration.read_module_version(w, Nokogiri::XML('<config><a></a></config>').root)
36
- m[:warnings].size.must_equal 2
37
- m[:warnings][-1][:file].must_equal m[:config_path]
38
- m[:warnings][-1][:message].must_include '/config/modules'
33
+ warnings = ModuleConfiguration.parse_module_version(@module_a, Nokogiri::XML('<config><a></a></config>').root)
34
+ warnings.size.must_equal 1
35
+ warnings[0].must_include '/config/modules'
39
36
  end
40
37
  it "will add a warning when then node with the module's name is missing" do
41
- w = @module_a.merge({ :warnings => ['first warning'] })
42
- m = ModuleConfiguration.read_module_version(w, Nokogiri::XML('<config><modules></modules></config>').root)
43
- m[:warnings].size.must_equal 2
44
- m[:warnings][-1][:file].must_equal m[:config_path]
45
- m[:warnings][-1][:message].must_include '/config/modules/Mage_A'
38
+ warnings = ModuleConfiguration.parse_module_version(@module_a, Nokogiri::XML('<config><modules></modules></config>').root)
39
+ warnings.size.must_equal 1
40
+ warnings[0].must_include '/config/modules/Mage_A'
46
41
  end
47
42
  it "will add a warning when there is a node from a different module" do
48
43
  xml_config_root = Nokogiri::XML('''
@@ -55,13 +50,10 @@ module Maruto
55
50
  </Mage_B>
56
51
  </modules></config>
57
52
  ''').root
58
- w = @module_a.merge({ :warnings => ['first warning'] })
59
- m = ModuleConfiguration.read_module_version(w, xml_config_root)
60
- m[:warnings].size.must_equal 2
61
- m[:warnings][-1][:file].must_equal m[:config_path]
62
- m[:warnings][-1][:message].must_include 'Mage_B'
53
+ warnings = ModuleConfiguration.parse_module_version(@module_a, xml_config_root)
54
+ warnings.size.must_equal 1
55
+ warnings[0].must_include 'Mage_B'
63
56
  end
64
57
 
65
58
  end
66
-
67
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maruto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-03 00:00:00.000000000 Z
12
+ date: 2013-05-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri