puppet7 0.2.0.beta1
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/LICENSE +1 -0
- data/README +80 -0
- data/lib/puppet7/common.rb +59 -0
- data/lib/puppet7/configuration.rb +38 -0
- data/lib/puppet7/domain.rb +136 -0
- data/lib/puppet7/element.rb +62 -0
- data/lib/puppet7/exceptions.rb +17 -0
- data/lib/puppet7/extend.rb +173 -0
- data/lib/puppet7/javascript/json.js +527 -0
- data/lib/puppet7/javascript/on_page_loading.js +78 -0
- data/lib/puppet7/javascript/selenium_extension.js +5 -0
- data/lib/puppet7/page.rb +138 -0
- data/lib/puppet7/page_part.rb +140 -0
- data/lib/puppet7/puppet.rb +79 -0
- data/lib/puppet7/puppet_formatter.rb +121 -0
- data/lib/puppet7/puppet_report.rb +292 -0
- data/lib/puppet7/report/_example_group.erb +24 -0
- data/lib/puppet7/report/_examples.erb +22 -0
- data/lib/puppet7/report/puppet_report.erb +187 -0
- data/lib/puppet7/selenium_actions.rb +20 -0
- data/lib/puppet7/selenium_locator_actions.rb +302 -0
- data/lib/puppet7/selenium_support.rb +56 -0
- data/lib/puppet7/sql_agent.rb +98 -0
- data/lib/puppet7/sql_exec.rb +148 -0
- data/lib/puppet7/utils.rb +72 -0
- data/lib/puppet7.rb +28 -0
- metadata +136 -0
data/LICENSE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
== puppet7
|
data/README
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
== puppet7
|
2
|
+
|
3
|
+
=== Introduction
|
4
|
+
Puppet7 is page object library for selenium web testing.
|
5
|
+
it's integrated RSpec2. You can write ui test code first, and then
|
6
|
+
define page objects later.
|
7
|
+
|
8
|
+
When undefined page object makes error, Puppet7 treats it pending result of rspec (yellow).
|
9
|
+
|
10
|
+
|
11
|
+
=== Quick start
|
12
|
+
|
13
|
+
You can start to write web page action and validation codes first.
|
14
|
+
|
15
|
+
require "puppet7"
|
16
|
+
describe_domain :MySite do
|
17
|
+
describe :mainPage do
|
18
|
+
it "should have search input and button" do
|
19
|
+
# all it blocks are executed in domain context
|
20
|
+
mainPage.open do # page context block
|
21
|
+
searchInput.should be_visible
|
22
|
+
searchButton.should be_visible
|
23
|
+
end
|
24
|
+
end
|
25
|
+
it "should do that you can search something." do
|
26
|
+
mainPage.open do
|
27
|
+
searchInput.type "keyword"
|
28
|
+
searchButton.click
|
29
|
+
end
|
30
|
+
searchResultPage do
|
31
|
+
should be_current
|
32
|
+
searchInput.text.should == "keyword"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
'describe_domain' is kind of a magic keyword. It'll initialize selenium session and page objects
|
39
|
+
and it make you be able to write rspec test in page object context.
|
40
|
+
|
41
|
+
Then you run this test by rspec, you can get report like below.
|
42
|
+
|
43
|
+
PENDING: 'MySite' is undefined domain!
|
44
|
+
|
45
|
+
Now you define page objects.
|
46
|
+
|
47
|
+
def_domain :MySite do
|
48
|
+
baseurl "http://my_so_cool_site.com"
|
49
|
+
page :mainPage
|
50
|
+
page :searchResultPage
|
51
|
+
end
|
52
|
+
|
53
|
+
def_page :mainPage do
|
54
|
+
url "/"
|
55
|
+
end
|
56
|
+
|
57
|
+
def_page :searchResultPage do
|
58
|
+
url "/search"
|
59
|
+
end
|
60
|
+
|
61
|
+
Run again rspec test. Maybe you can get report like below.
|
62
|
+
|
63
|
+
PENDING: 'searchInput' is undefined page action or ui element!
|
64
|
+
|
65
|
+
Ok. It's not bad. Let's define ui element in page definition.
|
66
|
+
|
67
|
+
def_page :mainPage do
|
68
|
+
url "/"
|
69
|
+
element :searchInput, ""
|
70
|
+
element :searchButton, ""
|
71
|
+
end
|
72
|
+
|
73
|
+
Run again! You can get a report.
|
74
|
+
|
75
|
+
ERROR in UI['mainPage.searchInput'] ...
|
76
|
+
|
77
|
+
Like above, you can write ui testing code step by step.
|
78
|
+
And more importantly you can write ui testing scenarios without web page.
|
79
|
+
RSpec'll generate cool document as test result, this document is a good checklist as acceptance criteria.
|
80
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
|
3
|
+
require "puppet7/puppet"
|
4
|
+
require "puppet7/exceptions"
|
5
|
+
module Puppet7
|
6
|
+
module KernelExtension
|
7
|
+
# pending method for rspec. (used internally)
|
8
|
+
def do_pending msg, ex_klass=PuppetException
|
9
|
+
if Puppet.puppet_testing
|
10
|
+
throw :pending_declared_in_example, msg
|
11
|
+
else
|
12
|
+
raise ex_klass.new msg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# load page definitions. (relative path based current file path)
|
17
|
+
#
|
18
|
+
# example
|
19
|
+
# load_pages "pages" # load all page definitions in "./pages"
|
20
|
+
#
|
21
|
+
# def_domain :naver do
|
22
|
+
# ...
|
23
|
+
# end
|
24
|
+
def load_pages rel_path="pages"
|
25
|
+
def __loading_rubyfiles dir
|
26
|
+
Dir.new(dir).each do |filename|
|
27
|
+
next if filename == '.' or filename == '..'
|
28
|
+
file = File.join(dir, filename)
|
29
|
+
if File.directory?(file)
|
30
|
+
__loading_rubyfiles file
|
31
|
+
elsif file.end_with?(".rb")
|
32
|
+
require file
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
caller_file = caller[0].gsub /:\d+:in.*$/, ""
|
37
|
+
__loading_rubyfiles File.join(File.dirname(caller_file), rel_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# return a class given name(e.g. 'MyModule::MyClass')
|
42
|
+
def class_by_name module_name, &block
|
43
|
+
klass = Kernel
|
44
|
+
module_name.split("::").each do |name|
|
45
|
+
if klass.const_defined?(name.to_sym)
|
46
|
+
klass = klass.const_get(name.to_sym)
|
47
|
+
else
|
48
|
+
klass = nil
|
49
|
+
break
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if klass and block_given?
|
53
|
+
klass.class_eval &block
|
54
|
+
end
|
55
|
+
klass
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
include Puppet7::KernelExtension
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Puppet7
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :js_ext_files, :use_report, :report_file
|
6
|
+
|
7
|
+
@@selenium_default_configs = {
|
8
|
+
:host => 'localhost',
|
9
|
+
:port => 4444,
|
10
|
+
:browser => '*firefox',
|
11
|
+
:timeout_in_seconds => 15,
|
12
|
+
:highlight_located_element => true
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@js_ext_files = []
|
17
|
+
@use_report = false
|
18
|
+
@report_file = "report.html"
|
19
|
+
@selenium_configs = @@selenium_default_configs.clone
|
20
|
+
end
|
21
|
+
|
22
|
+
def selenium_configs
|
23
|
+
@selenium_configs
|
24
|
+
end
|
25
|
+
|
26
|
+
def selenium_configs= configs
|
27
|
+
@selenium_configs.merge!(configs)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.configuration
|
32
|
+
@configuration ||= Puppet7::Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.configure
|
36
|
+
yield configuration if block_given?
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "puppet7/puppet"
|
4
|
+
require "puppet7/page"
|
5
|
+
require "puppet7/page_part"
|
6
|
+
require "puppet7/utils"
|
7
|
+
require "puppet7/exceptions"
|
8
|
+
require "puppet7/puppet_report"
|
9
|
+
|
10
|
+
module Puppet7
|
11
|
+
class Domain < Puppet
|
12
|
+
attr_accessor :baseurl, :encoding, :pages, :selenium
|
13
|
+
include Puppet7::Utils
|
14
|
+
|
15
|
+
class << self
|
16
|
+
include Puppet7::Utils
|
17
|
+
def baseurl baseurl
|
18
|
+
self.class_eval do
|
19
|
+
@__baseurl = parse_url(baseurl).baseurl
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def encoding encoding
|
24
|
+
self.class_eval do
|
25
|
+
@__encoding = encoding
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def page page_name
|
30
|
+
self.class_eval do
|
31
|
+
@__pages ||= []
|
32
|
+
@__pages << page_name.to_sym
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def new *args
|
37
|
+
# init instance
|
38
|
+
instance = super
|
39
|
+
instance.instance_eval do
|
40
|
+
@pages = {}
|
41
|
+
end
|
42
|
+
# init instance vars.
|
43
|
+
self.class_eval do
|
44
|
+
@__baseurl ||= nil
|
45
|
+
@__encoding ||= "UTF-8"
|
46
|
+
@__pages ||= []
|
47
|
+
instance.baseurl = @__baseurl
|
48
|
+
instance.encoding = @__encoding
|
49
|
+
@__pages.each do |page_name|
|
50
|
+
begin
|
51
|
+
instance.pages[page_name.to_sym] = Page.get_instance(page_name, instance)
|
52
|
+
make_page_accessor page_name
|
53
|
+
rescue NotDefinedException
|
54
|
+
make_pending_page_accessor page_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
# check vars
|
59
|
+
if instance.baseurl == nil
|
60
|
+
Puppet7.report.add_undefined_object :domain, "#{name}.baseurl"
|
61
|
+
do_pending "Domain[#{instance.puppet_name}] need to define 'baseurl'!"
|
62
|
+
end
|
63
|
+
instance
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def make_page_accessor page_name
|
68
|
+
self.class_eval <<-END
|
69
|
+
def #{page_name} &block
|
70
|
+
page = pages[:'#{page_name}']
|
71
|
+
if block_given?
|
72
|
+
@block_binding = block.binding
|
73
|
+
page.instance_eval &block
|
74
|
+
@block_binding = nil
|
75
|
+
end
|
76
|
+
page
|
77
|
+
end
|
78
|
+
END
|
79
|
+
end
|
80
|
+
|
81
|
+
def make_pending_page_accessor page_name
|
82
|
+
self.class_eval <<-END
|
83
|
+
def #{page_name} &block
|
84
|
+
Puppet7.report.add_undefined_object :page, "#{page_name}"
|
85
|
+
do_pending "'#{page_name}' page is Not defined!"
|
86
|
+
end
|
87
|
+
END
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def baseurl= url
|
92
|
+
if url
|
93
|
+
@baseurl = parse_url(url).baseurl
|
94
|
+
else
|
95
|
+
@baseurl = nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def current_url_info
|
100
|
+
parse_url(selenium.location)
|
101
|
+
end
|
102
|
+
|
103
|
+
def page_title
|
104
|
+
selenium.title.force_encoding(encoding)
|
105
|
+
end
|
106
|
+
|
107
|
+
def selenium
|
108
|
+
if @selenium == nil
|
109
|
+
do_pending "Selenium is Not running!"
|
110
|
+
else
|
111
|
+
@selenium
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def selenium_init?
|
116
|
+
@selenium != nil and @selenium.session_started?
|
117
|
+
end
|
118
|
+
|
119
|
+
def method_missing sym, *args, &b
|
120
|
+
Puppet7.report.add_undefined_object :page, sym
|
121
|
+
do_pending "'#{sym}' is Not defined Domain Action or it is Not defined Page!"
|
122
|
+
end
|
123
|
+
|
124
|
+
def image_ok? img_url
|
125
|
+
selenium.run_script <<-EOF
|
126
|
+
var __img = new Image();
|
127
|
+
var __img_check_result = "'CANNOT CHECKING'";
|
128
|
+
__img.onerror= function(){__img_check_result = false;};
|
129
|
+
__img.onload= function(){__img_check_result = true;};
|
130
|
+
__img.src = '#{img_url}';
|
131
|
+
EOF
|
132
|
+
sleep 1 # wait for check img
|
133
|
+
eval(selenium.get_eval('window.__img_check_result;'))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "puppet7/selenium_locator_actions"
|
4
|
+
|
5
|
+
module Puppet7
|
6
|
+
class Element
|
7
|
+
include Puppet7::SeleniumLocatorActions
|
8
|
+
|
9
|
+
attr_reader :parent, :name, :nclick, :xpath
|
10
|
+
def initialize parent, name, xpath=:pending, nclick=:pending
|
11
|
+
@parent = parent
|
12
|
+
@name = name.to_sym
|
13
|
+
@xpath = xpath
|
14
|
+
end
|
15
|
+
|
16
|
+
def image_ok?
|
17
|
+
img_url = attr("src")
|
18
|
+
@parent.image_ok? img_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
if count == 1
|
23
|
+
yield self
|
24
|
+
elsif count > 1
|
25
|
+
count.times do |idx|
|
26
|
+
yield Element.new(@parent, "#{@name}_#{idx}", _xpath_list_access(idx), @nclick)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def [] idx
|
32
|
+
Element.new(@parent, "#{@name}_#{idx}", _xpath_list_access(idx), @nclick)
|
33
|
+
end
|
34
|
+
|
35
|
+
def child rel_xpath
|
36
|
+
Element.new(@parent, "#{name}_#{rel_xpath}", "#{@xpath}#{rel_xpath}", @nclick)
|
37
|
+
end
|
38
|
+
|
39
|
+
def fullname
|
40
|
+
"#{@parent.fullname}.#{@name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
private
|
45
|
+
def selenium
|
46
|
+
if @xpath == :pending
|
47
|
+
try_pending "#{fullname} has no xpath!"
|
48
|
+
end
|
49
|
+
begin
|
50
|
+
return yield(@parent.selenium)
|
51
|
+
rescue => err
|
52
|
+
raise "Error in UI[#{fullname}] : #{err}"
|
53
|
+
end if block_given?
|
54
|
+
@parent.selenium
|
55
|
+
end
|
56
|
+
|
57
|
+
def _xpath_list_access idx
|
58
|
+
xpath_list = @xpath.sub(/^xpath=/, "")
|
59
|
+
"xpath=(#{xpath_list})[#{idx +1}]"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
|
3
|
+
module Puppet7
|
4
|
+
# Exception for all puppet7 error
|
5
|
+
class PuppetException < Exception;end
|
6
|
+
|
7
|
+
# Exception for using not defined Puppets
|
8
|
+
class NotDefinedException < PuppetException ;end
|
9
|
+
|
10
|
+
# Exception for duplicated Elements.
|
11
|
+
class DuplicatedUIElementException < PuppetException ;end
|
12
|
+
class DuplicatedPagePartException < PuppetException ;end
|
13
|
+
|
14
|
+
# Exception for error while initializing selenium.
|
15
|
+
class SeleniumException < PuppetException ;end
|
16
|
+
class SeleniumInitError < SeleniumException ;end
|
17
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# coding:utf-8
|
2
|
+
|
3
|
+
require "puppet7/common"
|
4
|
+
require "puppet7/page_part"
|
5
|
+
require "puppet7/page"
|
6
|
+
require "puppet7/domain"
|
7
|
+
require "puppet7/exceptions"
|
8
|
+
|
9
|
+
module Puppet7
|
10
|
+
module KernelExtension
|
11
|
+
# Define a PagePart.
|
12
|
+
#
|
13
|
+
# Create a subclass of PagePart given name, and then evaluation given block in the class context.
|
14
|
+
#
|
15
|
+
# - example.
|
16
|
+
# def_part :header do
|
17
|
+
# element :logo, by_xpath('//*[id="logo"]')
|
18
|
+
# part :gnb
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
def def_part part_name, &block
|
22
|
+
PagePart.subclass(part_name).class_eval &block
|
23
|
+
end
|
24
|
+
|
25
|
+
# Define a Page
|
26
|
+
def def_page page_name, &block
|
27
|
+
Page.subclass(page_name.to_sym).class_eval &block if block
|
28
|
+
end
|
29
|
+
|
30
|
+
# Define a Domain
|
31
|
+
def def_domain domain_name, &block
|
32
|
+
Domain.subclass(domain_name.to_sym).class_eval &block if block
|
33
|
+
end
|
34
|
+
|
35
|
+
# initialze domain.
|
36
|
+
#
|
37
|
+
# - create domain object and pages...
|
38
|
+
# - initialize selenium session.
|
39
|
+
# - execute code block and finish domain if given block.
|
40
|
+
#
|
41
|
+
def begin_domain domain_name, &block
|
42
|
+
Puppet.begin_testing
|
43
|
+
if Domain.subclass?(domain_name)
|
44
|
+
# create domain
|
45
|
+
@domain = Domain.get_instance(domain_name)
|
46
|
+
# init selenium
|
47
|
+
begin
|
48
|
+
@domain.selenium = SeleniumSupport.init_selenium :url => @domain.baseurl
|
49
|
+
rescue SeleniumInitError => err # ignore exception
|
50
|
+
STDERR.puts "#{err}"
|
51
|
+
end
|
52
|
+
begin
|
53
|
+
yield @domain
|
54
|
+
rescue => err
|
55
|
+
STDERR.puts "#{err}"
|
56
|
+
end if block_given?
|
57
|
+
@domain
|
58
|
+
else
|
59
|
+
Puppet7.report.add_undefined_object :domain, domain_name
|
60
|
+
do_pending "Not Defined Domain : #{domain_name}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# end selenium session of domain initialized.
|
65
|
+
def end_domain
|
66
|
+
Puppet.end_testing
|
67
|
+
return unless @domain and @domain.selenium_init?
|
68
|
+
sleep 1; # delay for image error checking
|
69
|
+
|
70
|
+
# get error images
|
71
|
+
image_errors = @domain.selenium.get_stored_var :imageErrors;
|
72
|
+
image_errors.each do |page, images|
|
73
|
+
Puppet7.report.add_image_errors(page, images)
|
74
|
+
end
|
75
|
+
|
76
|
+
# get js errors
|
77
|
+
js_errors = @domain.selenium.get_stored_var :jsErrors;
|
78
|
+
js_errors.each do |page, errs|
|
79
|
+
Puppet7.report.add_js_errors(page, errs)
|
80
|
+
end
|
81
|
+
@domain.selenium.stop
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
# get database accessor
|
87
|
+
def get_dbaccessor dbi_url, user, passwd, dir
|
88
|
+
sqlexec = Puppet7::SQLExec.new dbi_url, user, passwd
|
89
|
+
caller_file = caller[0].gsub /:\d+:in.*$/, ""
|
90
|
+
sqldir = File.join(File.dirname(caller_file), dir)
|
91
|
+
Puppet7::SQLAgent.new(sqlexec, sqldir)
|
92
|
+
end
|
93
|
+
|
94
|
+
# it is shortcut for begin_domain .. end_domain.
|
95
|
+
#
|
96
|
+
# It supply domain object and domain context when you write tests with rspec.
|
97
|
+
#
|
98
|
+
# example
|
99
|
+
# describe_domain :naver do
|
100
|
+
# describe "main_page" do
|
101
|
+
# it "should have search input and search button." do
|
102
|
+
# # you could write test in domain context.
|
103
|
+
# main_page.open do
|
104
|
+
# # you could write code in page context in code block of page
|
105
|
+
# search_input.should be_visible?
|
106
|
+
# search_button.should be_visible?
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
# it "should move login page, when click login button." do
|
110
|
+
# main_page.open.login_button.click
|
111
|
+
# login_page do
|
112
|
+
# # page block checks url automatically
|
113
|
+
# # if current location is not login page, error occurrs.
|
114
|
+
# user_input.should be_visible
|
115
|
+
# passwd_input.should be_visible
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
def describe_domain domain_name, *args, &block
|
121
|
+
describe "#{domain_name}", *args do
|
122
|
+
before(:each) do
|
123
|
+
begin_domain domain_name.to_sym
|
124
|
+
end
|
125
|
+
after(:each) do
|
126
|
+
end_domain
|
127
|
+
end
|
128
|
+
self.instance_exec &block
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
include Puppet7::KernelExtension
|
134
|
+
|
135
|
+
# hook method_missing of RSpec Example group.
|
136
|
+
#
|
137
|
+
# it is able to write test in domain context.
|
138
|
+
class_by_name "RSpec::Core::ExampleGroup" do
|
139
|
+
def method_missing sym, *args, &block
|
140
|
+
begin # first try super
|
141
|
+
super
|
142
|
+
rescue # hook later...
|
143
|
+
@domain.send sym, *args, &block
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# hook example
|
149
|
+
class_by_name "RSpec::Core::Example" do
|
150
|
+
alias_method :__run_before_each, :run_before_each
|
151
|
+
def run_before_each
|
152
|
+
__run_before_each
|
153
|
+
begin
|
154
|
+
desc = self.description
|
155
|
+
@example_group_instance.instance_eval do
|
156
|
+
@domain.selenium.set_stored_var :testing_message, desc
|
157
|
+
end
|
158
|
+
rescue
|
159
|
+
end if @example_group_instance.instance_eval("@domain && @domain.selenium_init?")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class Array
|
164
|
+
def each_with *other_arrays
|
165
|
+
other_arrays.each do |arr|
|
166
|
+
raise "Error: not same array length." if arr.length != length
|
167
|
+
end
|
168
|
+
|
169
|
+
each_index do |i|
|
170
|
+
yield self[i], *(other_arrays.collect {|arr| arr[i]})
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|