marty 2.5.2 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +11 -589
- data/Gemfile +9 -9
- data/Gemfile.lock +1 -1
- data/Rakefile +1 -3
- data/app/components/marty/api_auth_view.rb +3 -3
- data/app/components/marty/api_config_view.rb +8 -8
- data/app/components/marty/api_log_view.rb +16 -20
- data/app/components/marty/auth_app.rb +6 -6
- data/app/components/marty/base_rule_view.rb +27 -19
- data/app/components/marty/config_view.rb +12 -9
- data/app/components/marty/data_grid_view.rb +26 -26
- data/app/components/marty/delorean_rule_view.rb +0 -1
- data/app/components/marty/event_view.rb +27 -27
- data/app/components/marty/extras/layout.rb +26 -26
- data/app/components/marty/extras/misc.rb +2 -2
- data/app/components/marty/grid.rb +13 -13
- data/app/components/marty/grid_append_only.rb +0 -1
- data/app/components/marty/import_type_view.rb +13 -13
- data/app/components/marty/import_view.rb +17 -16
- data/app/components/marty/log_view.rb +16 -14
- data/app/components/marty/main_auth_app.rb +59 -59
- data/app/components/marty/main_auth_app/client/main_auth_app.js +3 -3
- data/app/components/marty/mcfly_grid_panel.rb +10 -10
- data/app/components/marty/new_posting_form.rb +11 -11
- data/app/components/marty/new_posting_window.rb +0 -1
- data/app/components/marty/posting_grid.rb +12 -13
- data/app/components/marty/promise_view.rb +6 -6
- data/app/components/marty/report_form.rb +50 -53
- data/app/components/marty/report_select.rb +27 -27
- data/app/components/marty/reporting.rb +4 -4
- data/app/components/marty/script_form.rb +40 -42
- data/app/components/marty/script_grid.rb +24 -24
- data/app/components/marty/script_tester.rb +40 -42
- data/app/components/marty/scripting.rb +25 -27
- data/app/components/marty/simple_app.rb +24 -9
- data/app/components/marty/tag_grid.rb +12 -13
- data/app/components/marty/user_view.rb +35 -35
- data/app/controllers/marty/application_controller.rb +3 -4
- data/app/controllers/marty/components_controller.rb +1 -1
- data/app/controllers/marty/delayed_job_controller.rb +1 -0
- data/app/controllers/marty/diagnostic/controller.rb +4 -6
- data/app/controllers/marty/job_controller.rb +6 -6
- data/app/controllers/marty/report_controller.rb +11 -11
- data/app/controllers/marty/rpc_controller.rb +15 -16
- data/app/helpers/marty/script_set.rb +4 -4
- data/app/models/marty/api_auth.rb +4 -5
- data/app/models/marty/api_config.rb +1 -1
- data/app/models/marty/base.rb +9 -8
- data/app/models/marty/base_rule.rb +18 -13
- data/app/models/marty/config.rb +4 -5
- data/app/models/marty/data_grid.rb +157 -181
- data/app/models/marty/delorean_rule.rb +63 -62
- data/app/models/marty/enum.rb +1 -1
- data/app/models/marty/event.rb +56 -59
- data/app/models/marty/helper.rb +38 -6
- data/app/models/marty/import_type.rb +6 -6
- data/app/models/marty/log.rb +3 -2
- data/app/models/marty/name_validator.rb +3 -2
- data/app/models/marty/pg_enum.rb +3 -4
- data/app/models/marty/posting.rb +20 -24
- data/app/models/marty/promise.rb +28 -30
- data/app/models/marty/script.rb +30 -28
- data/app/models/marty/tag.rb +8 -8
- data/app/models/marty/token.rb +2 -2
- data/app/models/marty/user.rb +24 -23
- data/app/models/marty/vw_promise.rb +10 -11
- data/config/routes.rb +2 -2
- data/delorean/blame_report.dl +268 -0
- data/{spec/dummy/delorean/fields.dl → delorean/marty_fields.dl} +8 -0
- data/delorean/table_report.dl +34 -0
- data/docker-compose.dummy.yml +2 -3
- data/lib/marty/aws/base.rb +8 -8
- data/lib/marty/aws/request.rb +4 -4
- data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -0
- data/lib/marty/content_handler.rb +25 -25
- data/lib/marty/data_change.rb +49 -71
- data/lib/marty/data_conversion.rb +20 -28
- data/lib/marty/data_exporter.rb +25 -28
- data/lib/marty/data_importer.rb +25 -27
- data/lib/marty/engine.rb +1 -2
- data/lib/marty/json_schema.rb +22 -24
- data/lib/marty/logger.rb +6 -9
- data/lib/marty/mcfly_model.rb +20 -24
- data/lib/marty/migrations.rb +37 -35
- data/lib/marty/monkey.rb +33 -33
- data/lib/marty/permissions.rb +18 -18
- data/lib/marty/promise_job.rb +17 -17
- data/lib/marty/promise_proxy.rb +6 -6
- data/lib/marty/relation.rb +6 -7
- data/lib/marty/rpc_call.rb +13 -12
- data/lib/marty/rule_script_set.rb +32 -28
- data/lib/marty/schema_helper.rb +37 -51
- data/lib/marty/util.rb +25 -24
- data/lib/marty/version.rb +1 -1
- data/lib/marty/xl.rb +121 -115
- data/make-dummy.mk +3 -0
- data/marty.gemspec +21 -21
- data/other/marty/api/base.rb +34 -35
- data/other/marty/diagnostic/aws/ec2_instance.rb +8 -8
- data/other/marty/diagnostic/base.rb +13 -14
- data/other/marty/diagnostic/collection.rb +2 -1
- data/other/marty/diagnostic/connections.rb +8 -6
- data/other/marty/diagnostic/database.rb +1 -0
- data/other/marty/diagnostic/delayed_job_version.rb +7 -9
- data/other/marty/diagnostic/delayed_job_worker_total_count.rb +1 -1
- data/other/marty/diagnostic/delayed_job_workers.rb +1 -1
- data/other/marty/diagnostic/environment_variables.rb +17 -15
- data/other/marty/diagnostic/fatal.rb +1 -1
- data/other/marty/diagnostic/node.rb +5 -9
- data/other/marty/diagnostic/nodes.rb +7 -5
- data/other/marty/diagnostic/packer.rb +7 -7
- data/other/marty/diagnostic/reporter.rb +24 -27
- data/other/marty/diagnostic/version.rb +3 -5
- data/script/rails +2 -1
- data/spec/controllers/application_controller_spec.rb +6 -6
- data/spec/controllers/delayed_job_controller_spec.rb +4 -4
- data/spec/controllers/diagnostic/controller_spec.rb +59 -60
- data/spec/controllers/job_controller_spec.rb +68 -69
- data/spec/controllers/rpc_controller_spec.rb +353 -359
- data/spec/controllers/rpc_import_spec.rb +15 -16
- data/spec/dummy/delorean/blame_report.dl +110 -15
- data/spec/dummy/delorean/data_report.dl +4 -4
- data/spec/dummy/delorean/marty_fields.dl +63 -0
- data/spec/dummy/delorean/table_report.dl +34 -0
- data/spec/features/auth_app_spec.rb +1 -2
- data/spec/features/data_import_spec.rb +2 -3
- data/spec/features/enum_spec.rb +42 -46
- data/spec/features/jobs_dashboard_spec.rb +14 -8
- data/spec/features/log_view_spec.rb +40 -43
- data/spec/features/reporting_spec.rb +15 -15
- data/spec/features/rule_spec.rb +195 -190
- data/spec/features/scripting_spec.rb +17 -20
- data/spec/features/scripting_test_spec.rb +32 -33
- data/spec/features/user_view_spec.rb +15 -17
- data/spec/job_helper.rb +11 -11
- data/spec/lib/data_blame_spec.rb +82 -0
- data/spec/lib/data_exporter_spec.rb +31 -32
- data/spec/lib/data_importer_spec.rb +382 -395
- data/spec/lib/delorean_query_spec.rb +117 -119
- data/spec/lib/json_schema_spec.rb +382 -392
- data/spec/lib/logger_spec.rb +23 -24
- data/spec/lib/mcfly_model_spec.rb +112 -109
- data/spec/lib/migrations_spec.rb +10 -10
- data/spec/lib/struct_compare_spec.rb +6 -6
- data/spec/lib/table_report_spec.rb +90 -0
- data/spec/lib/xl_spec.rb +63 -65
- data/spec/lib/xl_styles_spec.rb +16 -19
- data/spec/models/api_auth_spec.rb +30 -30
- data/spec/models/config_spec.rb +32 -32
- data/spec/models/data_grid_spec.rb +642 -655
- data/spec/models/event_spec.rb +96 -88
- data/spec/models/import_type_spec.rb +20 -20
- data/spec/models/posting_spec.rb +35 -35
- data/spec/models/promise_spec.rb +5 -5
- data/spec/models/rule_spec.rb +280 -269
- data/spec/models/script_spec.rb +27 -18
- data/spec/models/user_spec.rb +9 -9
- data/spec/other/diagnostic/base_spec.rb +20 -19
- data/spec/other/diagnostic/collection_spec.rb +6 -5
- data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
- data/spec/other/diagnostic/delayed_job_workers_spec.rb +8 -8
- data/spec/other/diagnostic/reporter_spec.rb +31 -33
- data/spec/spec_helper.rb +5 -5
- data/spec/support/chromedriver.rb +3 -5
- data/spec/support/components/netzke_combobox.rb +1 -1
- data/spec/support/components/netzke_grid.rb +17 -17
- data/spec/support/custom_matchers.rb +2 -2
- data/spec/support/download_helper.rb +1 -1
- data/spec/support/helper.rb +1 -2
- data/spec/support/netzke.rb +31 -31
- data/spec/support/performance_helper.rb +8 -8
- data/spec/support/post_run_logger.rb +1 -2
- data/spec/support/setup.rb +1 -4
- data/spec/support/shared_connection.rb +2 -2
- data/spec/support/structure_compare.rb +21 -22
- data/spec/support/suite.rb +1 -2
- data/spec/support/users.rb +5 -6
- metadata +32 -26
data/lib/marty/schema_helper.rb
CHANGED
|
@@ -1,53 +1,43 @@
|
|
|
1
1
|
class Marty::SchemaHelper
|
|
2
2
|
include Delorean::Model
|
|
3
3
|
|
|
4
|
-
delorean_fn :enum_is, sig: 2 do
|
|
5
|
-
|
|
6
|
-
{"properties"=> {var => { "enum"=> value}}}
|
|
4
|
+
delorean_fn :enum_is, sig: 2 do |var, value|
|
|
5
|
+
{ 'properties' => { var => { 'enum' => value } } }
|
|
7
6
|
end
|
|
8
7
|
|
|
9
|
-
delorean_fn :bool_is, sig: 2 do
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{"properties"=> {var => { "type"=> "boolean", "enum"=> [value]}}}]}
|
|
8
|
+
delorean_fn :bool_is, sig: 2 do |var, value|
|
|
9
|
+
{ 'allOf' => [{ 'required' => [var] },
|
|
10
|
+
{ 'properties' => { var => { 'type' => 'boolean', 'enum' => [value] } } }] }
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
delorean_fn :or, sig: [1, 20] do
|
|
16
|
-
|
|
17
|
-
{"anyOf"=>args}
|
|
13
|
+
delorean_fn :or, sig: [1, 20] do |*args|
|
|
14
|
+
{ 'anyOf' => args }
|
|
18
15
|
end
|
|
19
16
|
|
|
20
|
-
delorean_fn :and, sig: [1, 20] do
|
|
21
|
-
|
|
22
|
-
{"allOf"=>args}
|
|
17
|
+
delorean_fn :and, sig: [1, 20] do |*args|
|
|
18
|
+
{ 'allOf' => args }
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
delorean_fn :not, sig: 1 do
|
|
26
|
-
|
|
27
|
-
{"not"=>{"allOf"=>[arg]}}
|
|
21
|
+
delorean_fn :not, sig: 1 do |arg|
|
|
22
|
+
{ 'not' => { 'allOf' => [arg] } }
|
|
28
23
|
end
|
|
29
24
|
|
|
30
25
|
# if conds is true, var_array columns we be required
|
|
31
|
-
delorean_fn :disallow_if_conds, sig: [2, 20] do
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
}]}
|
|
26
|
+
delorean_fn :disallow_if_conds, sig: [2, 20] do |var_array, *conds_array|
|
|
27
|
+
{ 'anyOf' => [{ 'not' => { 'allOf' => conds_array } },
|
|
28
|
+
{ 'properties' => var_array.each_with_object({}) do |v, h|
|
|
29
|
+
h[v] = { 'not' => {} }
|
|
30
|
+
end
|
|
31
|
+
}] }
|
|
39
32
|
end
|
|
40
33
|
|
|
41
|
-
|
|
42
34
|
# if param is present, disallow cols
|
|
43
|
-
delorean_fn :disallow_if_present, sig: [2, 20] do
|
|
44
|
-
|dep_column, *var_array|
|
|
35
|
+
delorean_fn :disallow_if_present, sig: [2, 20] do |dep_column, *var_array|
|
|
45
36
|
dep_check(dep_column,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
37
|
+
'properties' => var_array.each_with_object({}) do |v, h|
|
|
38
|
+
h[v] = { 'not' => {} }
|
|
39
|
+
end
|
|
40
|
+
)
|
|
51
41
|
end
|
|
52
42
|
|
|
53
43
|
# if param is not present, disallow cols
|
|
@@ -57,30 +47,26 @@ class Marty::SchemaHelper
|
|
|
57
47
|
# it will report both the required clause and the not clauses
|
|
58
48
|
# as failed. so the caller will see a message that a
|
|
59
49
|
# required field is missing (which is not really a required field)
|
|
60
|
-
delorean_fn :disallow_if_not_present, sig: [2, 20] do
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}]}
|
|
50
|
+
delorean_fn :disallow_if_not_present, sig: [2, 20] do |dep_column, *var_array|
|
|
51
|
+
{ 'anyOf' => [
|
|
52
|
+
{ 'required' => [dep_column] },
|
|
53
|
+
{ 'properties' => var_array.each_with_object({}) do |v, h|
|
|
54
|
+
h[v] = { 'not' => {} }
|
|
55
|
+
end
|
|
56
|
+
}
|
|
57
|
+
] }
|
|
69
58
|
end
|
|
70
59
|
|
|
71
60
|
# if conds is true, var_array columns are not allowed
|
|
72
|
-
delorean_fn :required_if, sig: [2, 20] do
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{"required"=>var_array}]}
|
|
61
|
+
delorean_fn :required_if, sig: [2, 20] do |var_array, *conds_array|
|
|
62
|
+
{ 'anyOf' => [{ 'not' => { 'allOf' => conds_array } },
|
|
63
|
+
{ 'required' => var_array }] }
|
|
76
64
|
end
|
|
77
65
|
|
|
78
66
|
# if dep_column is present, checks must pass
|
|
79
|
-
delorean_fn :dep_check, sig: [2, 20] do
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"allOf"=> checks}}}
|
|
67
|
+
delorean_fn :dep_check, sig: [2, 20] do |dep_column, *checks|
|
|
68
|
+
{ 'dependencies' => { dep_column =>
|
|
69
|
+
{ 'type' => 'object',
|
|
70
|
+
'allOf' => checks } } }
|
|
84
71
|
end
|
|
85
|
-
|
|
86
72
|
end
|
data/lib/marty/util.rb
CHANGED
|
@@ -8,16 +8,17 @@ module Marty::Util
|
|
|
8
8
|
def self.get_posting
|
|
9
9
|
sid = Netzke::Base.session && Netzke::Base.session[:posting]
|
|
10
10
|
return unless sid.is_a? Integer
|
|
11
|
+
|
|
11
12
|
sid && Marty::Posting.find_by_id(sid)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def self.get_posting_time
|
|
15
|
-
snap =
|
|
16
|
+
snap = get_posting
|
|
16
17
|
snap ? snap.created_dt : Float::INFINITY
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def self.warped?
|
|
20
|
-
|
|
21
|
+
get_posting_time != Float::INFINITY
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def self.logger
|
|
@@ -34,21 +35,21 @@ module Marty::Util
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def self.pg_range_to_human(r)
|
|
37
|
-
return r if r ==
|
|
38
|
+
return r if r == 'empty' || r.nil?
|
|
38
39
|
|
|
39
40
|
m = pg_range_match(r)
|
|
40
41
|
|
|
41
42
|
raise "bad PG range #{r}" unless m
|
|
42
43
|
|
|
43
|
-
if m[:start] ==
|
|
44
|
-
res =
|
|
44
|
+
if m[:start] == ''
|
|
45
|
+
res = ''
|
|
45
46
|
else
|
|
46
|
-
op = m[:open] ==
|
|
47
|
+
op = m[:open] == '(' ? '>' : '>='
|
|
47
48
|
res = "#{op}#{m[:start]}"
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
if m[:end] !=
|
|
51
|
-
op = m[:close] ==
|
|
51
|
+
if m[:end] != ''
|
|
52
|
+
op = m[:close] == ')' ? '<' : '<='
|
|
52
53
|
res += "#{op}#{m[:end]}"
|
|
53
54
|
end
|
|
54
55
|
|
|
@@ -56,7 +57,7 @@ module Marty::Util
|
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def self.human_to_pg_range(r)
|
|
59
|
-
return r if r ==
|
|
60
|
+
return r if r == 'empty'
|
|
60
61
|
|
|
61
62
|
m = /\A
|
|
62
63
|
((?<op0>\>|\>=)(?<start>[^\<\>\=]*?))?
|
|
@@ -66,17 +67,17 @@ module Marty::Util
|
|
|
66
67
|
raise "bad range #{r}" unless m
|
|
67
68
|
|
|
68
69
|
if m[:op0]
|
|
69
|
-
open = m[:op0] ==
|
|
70
|
+
open = m[:op0] == '>' ? '(' : '['
|
|
70
71
|
start = "#{open}#{m[:start]}"
|
|
71
72
|
else
|
|
72
|
-
start =
|
|
73
|
+
start = '['
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
if m[:op1]
|
|
76
|
-
close = m[:op1] ==
|
|
77
|
+
close = m[:op1] == '<' ? ')' : ']'
|
|
77
78
|
ends = "#{m[:end]}#{close}"
|
|
78
79
|
else
|
|
79
|
-
ends =
|
|
80
|
+
ends = ']'
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
"#{start},#{ends}"
|
|
@@ -88,7 +89,7 @@ module Marty::Util
|
|
|
88
89
|
sql = 'select pg_is_in_recovery();'
|
|
89
90
|
result = ActiveRecord::Base.connection.execute(sql)
|
|
90
91
|
status = result[0]['pg_is_in_recovery'] == 't' if result && result[0]
|
|
91
|
-
rescue => e
|
|
92
|
+
rescue StandardError => e
|
|
92
93
|
Marty::Util.logger.error 'unable to determine recovery status'
|
|
93
94
|
end
|
|
94
95
|
status
|
|
@@ -97,14 +98,14 @@ module Marty::Util
|
|
|
97
98
|
def self.deep_round(obj, digits)
|
|
98
99
|
case obj
|
|
99
100
|
when Array
|
|
100
|
-
obj.map {|o| deep_round(o, digits)}
|
|
101
|
+
obj.map { |o| deep_round(o, digits) }
|
|
101
102
|
when Hash
|
|
102
|
-
obj.inject({})
|
|
103
|
+
obj.inject({}) do |result, (key, value)|
|
|
103
104
|
result[key] = deep_round(value, digits)
|
|
104
105
|
result
|
|
105
|
-
|
|
106
|
+
end
|
|
106
107
|
else
|
|
107
|
-
obj.is_a?(Float)? obj.round(digits) : obj
|
|
108
|
+
obj.is_a?(Float) ? obj.round(digits) : obj
|
|
108
109
|
end
|
|
109
110
|
end
|
|
110
111
|
|
|
@@ -113,7 +114,7 @@ module Marty::Util
|
|
|
113
114
|
engine = Marty::ScriptSet.new.get_engine(script_name)
|
|
114
115
|
res = engine.background_eval(node_name,
|
|
115
116
|
params,
|
|
116
|
-
[
|
|
117
|
+
['result', 'title', 'format'],
|
|
117
118
|
)
|
|
118
119
|
|
|
119
120
|
promise_id = res.__promise__.id
|
|
@@ -127,18 +128,18 @@ module Marty::Util
|
|
|
127
128
|
engine = Marty::ScriptSet.new.get_engine(script)
|
|
128
129
|
format = engine.evaluate(node, 'format')
|
|
129
130
|
title = params.delete(:title) || engine.evaluate(node, 'title')
|
|
130
|
-
data = ({selected_script_name: script,
|
|
131
|
-
selected_node: node} + params).to_json
|
|
131
|
+
data = ({ selected_script_name: script,
|
|
132
|
+
selected_node: node } + params).to_json
|
|
132
133
|
URI.encode("#{Marty::Util.marty_path}/report?data=#{data}"\
|
|
133
134
|
"&reptitle=#{title}&format=#{format}")
|
|
134
135
|
end
|
|
135
136
|
|
|
136
137
|
def self.scrub_obj(obj)
|
|
137
|
-
trav = lambda {|o|
|
|
138
|
+
trav = lambda { |o|
|
|
138
139
|
if o.is_a?(Hash)
|
|
139
|
-
return o.each_with_object({}) {|(k, v), h| h[k] = trav.call(v)}
|
|
140
|
+
return o.each_with_object({}) { |(k, v), h| h[k] = trav.call(v) }
|
|
140
141
|
elsif o.is_a?(Array)
|
|
141
|
-
return o.map {|v| trav.call(v)}
|
|
142
|
+
return o.map { |v| trav.call(v) }
|
|
142
143
|
elsif o.to_s.length > 10000
|
|
143
144
|
o.class.to_s
|
|
144
145
|
else
|
data/lib/marty/version.rb
CHANGED
data/lib/marty/xl.rb
CHANGED
|
@@ -12,10 +12,10 @@ class Marty::Xl
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def deep_copy(value)
|
|
15
|
-
return value.each_with_object({}){|(k, v), h| h[k] = deep_copy(v)} if
|
|
15
|
+
return value.each_with_object({}) { |(k, v), h| h[k] = deep_copy(v) } if
|
|
16
16
|
value.is_a?(Hash)
|
|
17
17
|
|
|
18
|
-
value.is_a?(Array) ? value.map{|v| deep_copy(v)} : value
|
|
18
|
+
value.is_a?(Array) ? value.map { |v| deep_copy(v) } : value
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def merge_cell_edges(a, b)
|
|
@@ -25,10 +25,9 @@ class Marty::Xl
|
|
|
25
25
|
|
|
26
26
|
return b unless a_border.is_a?(Hash) && a_border[:edges].is_a?(Array)
|
|
27
27
|
|
|
28
|
-
non_match = a_border.detect
|
|
29
|
-
|key, value|
|
|
28
|
+
non_match = a_border.detect do |key, value|
|
|
30
29
|
key != :edges && b_border[key] != value
|
|
31
|
-
|
|
30
|
+
end
|
|
32
31
|
|
|
33
32
|
a_border[:edges].each do |edge|
|
|
34
33
|
unless b_border[:edges].include? edge
|
|
@@ -37,10 +36,9 @@ class Marty::Xl
|
|
|
37
36
|
|
|
38
37
|
# add new style/color for the new edge if there is no style
|
|
39
38
|
# match with the old edges:
|
|
40
|
-
b["border_#{edge}".to_sym] = a_border.each_with_object({})
|
|
41
|
-
|(key, value), h|
|
|
39
|
+
b["border_#{edge}".to_sym] = a_border.each_with_object({}) do |(key, value), h|
|
|
42
40
|
h[key] = value unless key == :edges
|
|
43
|
-
|
|
41
|
+
end if non_match
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
44
|
b
|
|
@@ -48,6 +46,7 @@ class Marty::Xl
|
|
|
48
46
|
|
|
49
47
|
def merge_row_edges(a, b)
|
|
50
48
|
return b unless a.count > 0
|
|
49
|
+
|
|
51
50
|
a.each_index do |ind|
|
|
52
51
|
b[ind] = merge_cell_edges(a[ind], deep_copy(b[ind]))
|
|
53
52
|
end
|
|
@@ -70,32 +69,33 @@ class Marty::Xl
|
|
|
70
69
|
new_row, new_style = rows[r_number], styles[r_number]
|
|
71
70
|
|
|
72
71
|
(0...column_offset).each do |t|
|
|
73
|
-
new_row[t] ||=
|
|
72
|
+
new_row[t] ||= ''
|
|
74
73
|
new_style[t] ||= {}
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
d[1].each_index do |c_index|
|
|
78
|
-
new_row[c_index+column_offset] = d[1][c_index]
|
|
77
|
+
new_row[c_index + column_offset] = d[1][c_index]
|
|
79
78
|
end if d[1].kind_of?(Array)
|
|
80
79
|
|
|
81
|
-
if (d.length > 2) && d[2].kind_of?(Hash) && d[2][
|
|
82
|
-
d[2][
|
|
83
|
-
new_style[c_index+column_offset] = d[2][
|
|
80
|
+
if (d.length > 2) && d[2].kind_of?(Hash) && d[2]['style'].kind_of?(Array)
|
|
81
|
+
d[2]['style'].each_index do |c_index|
|
|
82
|
+
new_style[c_index + column_offset] = d[2]['style'][c_index]
|
|
84
83
|
end
|
|
85
84
|
end
|
|
86
85
|
|
|
87
86
|
# apply style for the row as a whole:
|
|
88
87
|
if (d.length > 2) && d[2].kind_of?(Hash)
|
|
89
88
|
d[2].each do |key, value|
|
|
90
|
-
|
|
91
|
-
row_styles[r_number][key] = value
|
|
92
|
-
else
|
|
89
|
+
if key == :style.to_s
|
|
93
90
|
# skip if the style is an array: /style as an array is
|
|
94
91
|
# handled by the 'apply a style to each cell' section/
|
|
95
92
|
next unless value.kind_of?(Hash)
|
|
93
|
+
|
|
96
94
|
d[1].length.times do |t|
|
|
97
|
-
new_style[t+column_offset] = value
|
|
95
|
+
new_style[t + column_offset] = value
|
|
98
96
|
end
|
|
97
|
+
else
|
|
98
|
+
row_styles[r_number][key] = value
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -107,43 +107,44 @@ class Marty::Xl
|
|
|
107
107
|
x1, y1, x2, y2, w, h = d[1]
|
|
108
108
|
column_offset, row_offset = offset
|
|
109
109
|
|
|
110
|
-
y_coords = y2.is_a?(Integer) || d[0] !=
|
|
111
|
-
x_coords = x2.is_a?(Integer) || d[0] !=
|
|
110
|
+
y_coords = y2.is_a?(Integer) || d[0] != 'image' ? [y1, y2] : [y1]
|
|
111
|
+
x_coords = x2.is_a?(Integer) || d[0] != 'image' ? [x1, x2] : [x1]
|
|
112
112
|
|
|
113
113
|
# add the row offset:
|
|
114
|
-
y1, y2 = y_coords.map
|
|
114
|
+
y1, y2 = y_coords.map do |y|
|
|
115
115
|
if y.is_a?(Integer)
|
|
116
116
|
row_offset + y
|
|
117
|
-
elsif y.is_a?(Hash) && y[
|
|
118
|
-
last_row + y[
|
|
117
|
+
elsif y.is_a?(Hash) && y['off'].is_a?(Integer)
|
|
118
|
+
last_row + y['off']
|
|
119
119
|
else
|
|
120
120
|
raise "bad offset #{y}"
|
|
121
121
|
end
|
|
122
|
-
|
|
122
|
+
end
|
|
123
123
|
|
|
124
124
|
# add the column offset:
|
|
125
|
-
x1, x2 = x_coords.map
|
|
125
|
+
x1, x2 = x_coords.map do |x|
|
|
126
126
|
raise "bad range point #{x}" unless x.is_a? Integer
|
|
127
|
+
|
|
127
128
|
column_offset + x
|
|
128
|
-
|
|
129
|
+
end
|
|
129
130
|
|
|
130
131
|
el[last_row] = [] unless
|
|
131
|
-
el[last_row] || [
|
|
132
|
+
el[last_row] || ['border', 'image'].member?(d[0])
|
|
132
133
|
|
|
133
134
|
case d[0]
|
|
134
|
-
when
|
|
135
|
+
when 'conditional_formatting'
|
|
135
136
|
el[last_row] << [d[0], [x1, y1, x2, y2], d[2]]
|
|
136
|
-
when
|
|
137
|
+
when 'merge'
|
|
137
138
|
el[last_row] << [d[0], [x1, y1, x2, y2]]
|
|
138
|
-
when
|
|
139
|
+
when 'border'
|
|
139
140
|
el << [d[0], [x1, y1, x2, y2], d[2]]
|
|
140
|
-
when
|
|
141
|
+
when 'image'
|
|
141
142
|
el << [d[0], [x1, y1, x2, y2, w, h], d[2]]
|
|
142
143
|
end
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
def position_borders(borders)
|
|
146
|
-
b_styles
|
|
147
|
+
b_styles = []
|
|
147
148
|
borders.each do |b|
|
|
148
149
|
top_row, middle_row, bottom_row, edge_h = [], [], [], {}
|
|
149
150
|
br, range, defaults = b
|
|
@@ -156,7 +157,7 @@ class Marty::Xl
|
|
|
156
157
|
|
|
157
158
|
boxborders = Hash.new do |hash, key|
|
|
158
159
|
hash[key] = {
|
|
159
|
-
border: defaults.merge(
|
|
160
|
+
border: defaults.merge(edges: key.to_s.split('_').map(&:to_sym))
|
|
160
161
|
}
|
|
161
162
|
end
|
|
162
163
|
|
|
@@ -168,16 +169,16 @@ class Marty::Xl
|
|
|
168
169
|
if col0 == colw
|
|
169
170
|
# vertical line
|
|
170
171
|
edge_h[tro], edge_h[mro], edge_h[bro] =
|
|
171
|
-
[
|
|
172
|
+
['left'], ['left'], ['left']
|
|
172
173
|
elsif row0 == rowh
|
|
173
174
|
# horizontal line
|
|
174
175
|
edge_h[tro], edge_h[mro], edge_h[bro] =
|
|
175
|
-
[
|
|
176
|
+
['top'] * 3, ['top'] * 3, ['top'] * 3
|
|
176
177
|
else
|
|
177
178
|
# box
|
|
178
|
-
edge_h[tro] = [
|
|
179
|
-
edge_h[mro] = [
|
|
180
|
-
edge_h[bro] = [
|
|
179
|
+
edge_h[tro] = ['top_left', 'top_right', 'top']
|
|
180
|
+
edge_h[mro] = ['left', 'right', 'nil']
|
|
181
|
+
edge_h[bro] = ['bottom_left', 'bottom_right', 'bottom']
|
|
181
182
|
end
|
|
182
183
|
|
|
183
184
|
[top_row, middle_row, bottom_row].each do |r|
|
|
@@ -189,7 +190,7 @@ class Marty::Xl
|
|
|
189
190
|
|
|
190
191
|
# counter == col0 == (colw - 1) => merge the edges:
|
|
191
192
|
a = boxborders[edge_h[r.object_id][1].to_sym] =
|
|
192
|
-
|
|
193
|
+
merge_cell_edges(a, deep_copy(boxborders[edge_h[r.object_id][1].to_sym])) if
|
|
193
194
|
counter == (colw - 1)
|
|
194
195
|
|
|
195
196
|
a = boxborders[edge_h[r.object_id][2].to_sym] unless
|
|
@@ -209,7 +210,7 @@ class Marty::Xl
|
|
|
209
210
|
|
|
210
211
|
a = i == 0 ? top_row : []
|
|
211
212
|
|
|
212
|
-
a = merge_row_edges(a,bottom_row) if
|
|
213
|
+
a = merge_row_edges(a, bottom_row) if
|
|
213
214
|
i == (rowh - row0 - 1)
|
|
214
215
|
|
|
215
216
|
a = middle_row unless
|
|
@@ -234,43 +235,42 @@ class Marty::Xl
|
|
|
234
235
|
|
|
235
236
|
if rlen < rlenmax
|
|
236
237
|
rows[index] ||= []
|
|
237
|
-
rows[index] += [
|
|
238
|
+
rows[index] += [''] * (rlenmax - rlen)
|
|
238
239
|
end
|
|
239
240
|
|
|
240
241
|
row_styles[index] ||= {}
|
|
241
242
|
|
|
242
243
|
rsi = row_styles[index]
|
|
243
244
|
|
|
244
|
-
rsi[
|
|
245
|
+
rsi['style'] = styles[index].kind_of?(Array) ? styles[index] : []
|
|
245
246
|
|
|
246
247
|
if b_styles[index].kind_of?(Array) && b_styles[index].count > 0
|
|
247
|
-
len = [rsi[
|
|
248
|
+
len = [rsi['style'].count, b_styles[index].count].max
|
|
248
249
|
|
|
249
250
|
len.times do |ind|
|
|
250
251
|
b_styles[index][ind] ||= {}
|
|
251
|
-
rsi[
|
|
252
|
+
rsi['style'][ind] ||= {}
|
|
252
253
|
|
|
253
|
-
rsi[
|
|
254
|
-
rsi[
|
|
254
|
+
rsi['style'][ind] =
|
|
255
|
+
rsi['style'][ind].merge(b_styles[index][ind])
|
|
255
256
|
end
|
|
256
257
|
|
|
257
|
-
rsi[
|
|
258
|
+
rsi['style'] = rsi['style'].map { |x| x || {} }
|
|
258
259
|
end
|
|
259
260
|
|
|
260
|
-
wsrows << [
|
|
261
|
+
wsrows << ['row', rows[index], rsi]
|
|
261
262
|
|
|
262
263
|
if format[index] && format[index].kind_of?(Array)
|
|
263
264
|
format[index].each do |f|
|
|
264
265
|
raise "wrong number of arguments for #{f[0]}" unless
|
|
265
266
|
[
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
['conditional_formatting', 3],
|
|
268
|
+
['merge', 2]
|
|
268
269
|
].member?([f[0], f.length])
|
|
269
270
|
|
|
270
271
|
wsrows << f
|
|
271
272
|
end
|
|
272
273
|
end
|
|
273
|
-
|
|
274
274
|
end
|
|
275
275
|
|
|
276
276
|
apply_relative_worksheet_ops(ws, wsrows + images)
|
|
@@ -285,18 +285,18 @@ class Marty::Xl
|
|
|
285
285
|
|
|
286
286
|
# We got some sort of error if the worksheets is an array
|
|
287
287
|
if worksheets.is_a? Hash
|
|
288
|
-
ws = wb.add_worksheet(name:
|
|
289
|
-
ws.add_row [
|
|
290
|
-
ws.add_row [
|
|
288
|
+
ws = wb.add_worksheet(name: 'EXCEPTION')
|
|
289
|
+
ws.add_row ['error', worksheets['error']]
|
|
290
|
+
ws.add_row ['backtrace', worksheets['backtrace']]
|
|
291
291
|
return
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
raise "expected worksheets array, got: #{worksheets}" unless
|
|
295
295
|
worksheets.is_a?(Array)
|
|
296
296
|
|
|
297
|
-
worksheets << [
|
|
297
|
+
worksheets << ['No data', []] if worksheets.count == 0
|
|
298
298
|
|
|
299
|
-
worksheets.each
|
|
299
|
+
worksheets.each do |opl|
|
|
300
300
|
name, ops, opts = opl
|
|
301
301
|
|
|
302
302
|
raise "bad worksheet name: #{name}" unless name.is_a?(String)
|
|
@@ -318,17 +318,17 @@ class Marty::Xl
|
|
|
318
318
|
ws.sheet_view.show_grid_lines = gridlines
|
|
319
319
|
|
|
320
320
|
apply_relative_worksheet_ops(ws, ops)
|
|
321
|
-
|
|
321
|
+
end
|
|
322
322
|
@package.use_shared_strings = true
|
|
323
323
|
end
|
|
324
324
|
|
|
325
325
|
def add_style(style)
|
|
326
|
-
raise
|
|
326
|
+
raise 'bad style' unless style.is_a?(Hash) || style.is_a?(Array)
|
|
327
327
|
|
|
328
328
|
if style.is_a?(Array)
|
|
329
|
-
style.map
|
|
329
|
+
style.map do |s|
|
|
330
330
|
styles[s] ||= package.workbook.styles.add_style(s)
|
|
331
|
-
|
|
331
|
+
end
|
|
332
332
|
else
|
|
333
333
|
styles[style] ||= package.workbook.styles.add_style(style)
|
|
334
334
|
end
|
|
@@ -336,71 +336,74 @@ class Marty::Xl
|
|
|
336
336
|
|
|
337
337
|
def intern_range(ws, range)
|
|
338
338
|
return range if range.is_a? String
|
|
339
|
-
raise "bad range #{range}" unless range.is_a?(Array) && range.length==4
|
|
339
|
+
raise "bad range #{range}" unless range.is_a?(Array) && range.length == 4
|
|
340
|
+
|
|
340
341
|
x1, y1, x2, y2 = range
|
|
341
342
|
|
|
342
|
-
y1, y2 = [y1, y2].map
|
|
343
|
+
y1, y2 = [y1, y2].map do |y|
|
|
343
344
|
next y unless y.is_a?(Hash)
|
|
344
|
-
raise "bad offset #{y}" unless y[
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
raise "bad offset #{y}" unless y['off'].is_a?(Integer)
|
|
346
|
+
|
|
347
|
+
ws.rows.last.row_index + y['off']
|
|
348
|
+
end
|
|
347
349
|
|
|
348
|
-
[x1, y1, x2, y2].each
|
|
350
|
+
[x1, y1, x2, y2].each do |x|
|
|
349
351
|
raise "bad range point #{x}" unless x.is_a? Integer
|
|
350
|
-
|
|
351
|
-
Axlsx.cell_r(x1, y1) +
|
|
352
|
+
end
|
|
353
|
+
Axlsx.cell_r(x1, y1) + ':' + Axlsx.cell_r(x2, y2)
|
|
352
354
|
end
|
|
353
355
|
|
|
354
356
|
def recalc_offsets(ops_pos)
|
|
355
357
|
new_ops1, new_ops2, new_ops = [], [], []
|
|
356
358
|
# precalculate the offsets of pos options embedded in another pos opt:
|
|
357
|
-
ops_pos.each
|
|
358
|
-
new_ops1 += d[2].select
|
|
359
|
-
inner_ops if inner_ops[0] ==
|
|
360
|
-
|
|
361
|
-
[inner[0], d[1].zip(inner[1]).map { |x,y| x+y }, inner[2]
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
ops_pos.each do |d|
|
|
360
|
+
new_ops1 += d[2].select do |inner_ops|
|
|
361
|
+
inner_ops if inner_ops[0] == 'pos'
|
|
362
|
+
end.map do |inner|
|
|
363
|
+
[inner[0], d[1].zip(inner[1]).map { |x, y| x + y }, inner[2]]
|
|
364
|
+
end
|
|
365
|
+
end
|
|
364
366
|
# keep the offsets of non-pos options embedded in pos opt:
|
|
365
|
-
new_ops2 = ops_pos.map
|
|
366
|
-
[
|
|
367
|
-
|
|
367
|
+
new_ops2 = ops_pos.map do |d|
|
|
368
|
+
[d[0], d[1], d[2].select { |inner| inner if inner[0] != 'pos' }]
|
|
369
|
+
end
|
|
368
370
|
new_ops = new_ops1 + new_ops2
|
|
369
|
-
count = new_ops.select
|
|
370
|
-
d[2].select
|
|
371
|
-
inner_ops if inner_ops[0] ==
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
count = new_ops.select do |d|
|
|
372
|
+
d[2].select do |inner_ops|
|
|
373
|
+
inner_ops if inner_ops[0] == 'pos'
|
|
374
|
+
end.count > 0
|
|
375
|
+
end.count
|
|
374
376
|
|
|
375
377
|
count == 0 ? new_ops.sort : recalc_offsets(new_ops)
|
|
376
378
|
end
|
|
377
379
|
|
|
378
380
|
def apply_relative_worksheet_ops(ws, ops)
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
ops_brd = ops.select {|opl| opl[0] == "border" }
|
|
381
|
+
non_pos = ops.select { |opl| opl[0] != 'pos' }
|
|
382
|
+
ops_pos = ops.select { |opl| opl[0] == 'pos' }
|
|
383
|
+
ops_brd = ops.select { |opl| opl[0] == 'border' }
|
|
383
384
|
|
|
384
385
|
if (ops_pos.count > 0)
|
|
385
386
|
# Wrap all non-pos options in a pos option with offset 0, 0:
|
|
386
|
-
pos_00_ops = non_pos.count > 0 ? [
|
|
387
|
+
pos_00_ops = non_pos.count > 0 ? [['pos', [0, 0], non_pos]] : []
|
|
387
388
|
# Recalculate the offsets of embedded pos opts:
|
|
388
|
-
ops =
|
|
389
|
+
ops = pos_00_ops + recalc_offsets(ops_pos)
|
|
389
390
|
elsif (ops_brd.count > 0)
|
|
390
391
|
# Wrap the non-pos options in a pos opt with offset 0, 0:
|
|
391
|
-
pos_00_ops = [
|
|
392
|
+
pos_00_ops = [['pos', [0, 0], non_pos]]
|
|
392
393
|
ops = pos_00_ops
|
|
393
394
|
end
|
|
394
395
|
|
|
395
396
|
rows, styles, row_styles, format, borders, images = [], [], [], [], [], []
|
|
396
|
-
ops.each
|
|
397
|
+
ops.each do |opl|
|
|
397
398
|
raise "bad op #{opl}" unless opl.length > 1
|
|
399
|
+
|
|
398
400
|
case opl[0]
|
|
399
|
-
when
|
|
401
|
+
when 'pos'
|
|
400
402
|
op, offset, data = opl
|
|
401
403
|
raise "bad offset #{offset}" unless
|
|
402
404
|
offset.is_a?(Array) && offset.length == 2 &&
|
|
403
|
-
offset.all? {|x| x.is_a? Integer}
|
|
405
|
+
offset.all? { |x| x.is_a? Integer }
|
|
406
|
+
|
|
404
407
|
# column offset, row offset:
|
|
405
408
|
column_offset, row_offset = offset
|
|
406
409
|
r_number, last_row = row_offset, row_offset
|
|
@@ -409,23 +412,24 @@ class Marty::Xl
|
|
|
409
412
|
raise "non array data #{d[1]}" unless d[1].is_a?(Array)
|
|
410
413
|
raise "non hash data options #{d[2]}" unless
|
|
411
414
|
[NilClass, Hash, String, Array].member? d[2].class
|
|
415
|
+
|
|
412
416
|
case d[0]
|
|
413
|
-
when
|
|
417
|
+
when 'row'
|
|
414
418
|
position_row(d, column_offset, r_number, rows, styles, row_styles)
|
|
415
419
|
last_row = r_number
|
|
416
420
|
r_number += 1
|
|
417
|
-
when
|
|
421
|
+
when 'conditional_formatting', 'merge'
|
|
418
422
|
position_elem(d, offset, last_row, format)
|
|
419
|
-
when
|
|
423
|
+
when 'border'
|
|
420
424
|
position_elem(d, offset, last_row, borders)
|
|
421
|
-
when
|
|
425
|
+
when 'image'
|
|
422
426
|
position_elem(d, offset, last_row, images)
|
|
423
427
|
else
|
|
424
428
|
raise "unknown op #{d[0]} embedded in 'position' option"
|
|
425
429
|
end
|
|
426
430
|
end
|
|
427
431
|
|
|
428
|
-
when
|
|
432
|
+
when 'row'
|
|
429
433
|
op, data, options = opl
|
|
430
434
|
|
|
431
435
|
raise "bad row op #{opl}" unless data.is_a?(Array) || opl.length > 3
|
|
@@ -438,11 +442,11 @@ class Marty::Xl
|
|
|
438
442
|
|
|
439
443
|
ws.add_row data, options
|
|
440
444
|
|
|
441
|
-
when
|
|
445
|
+
when 'row_style'
|
|
442
446
|
op, row_num, style = opl
|
|
443
447
|
|
|
444
448
|
# FIXME: need to handle Array?
|
|
445
|
-
raise
|
|
449
|
+
raise 'non hash arg for row_style' unless style.is_a?(Hash)
|
|
446
450
|
raise "bad row num #{opl}" unless row_num.is_a?(Integer)
|
|
447
451
|
|
|
448
452
|
style = self.class.symbolize_keys(style, ':')
|
|
@@ -452,17 +456,18 @@ class Marty::Xl
|
|
|
452
456
|
|
|
453
457
|
row.style = style_id
|
|
454
458
|
|
|
455
|
-
when
|
|
459
|
+
when 'merge'
|
|
456
460
|
op, range = opl
|
|
457
461
|
|
|
458
462
|
raise "bad merge op #{opl}" unless opl.length == 2
|
|
463
|
+
|
|
459
464
|
range = intern_range(ws, range)
|
|
460
465
|
|
|
461
466
|
ws.merge_cells range
|
|
462
|
-
when
|
|
467
|
+
when 'conditional_formatting'
|
|
463
468
|
op, range, format = opl
|
|
464
469
|
|
|
465
|
-
raise
|
|
470
|
+
raise 'non hash arg for format' unless format.is_a?(Hash)
|
|
466
471
|
|
|
467
472
|
range = intern_range(ws, range)
|
|
468
473
|
|
|
@@ -471,11 +476,11 @@ class Marty::Xl
|
|
|
471
476
|
color_scale_a = format[:color_scale]
|
|
472
477
|
|
|
473
478
|
if color_scale_a
|
|
474
|
-
raise
|
|
479
|
+
raise 'color_scale must be an array' unless
|
|
475
480
|
color_scale_a.is_a?(Array)
|
|
476
481
|
|
|
477
|
-
raise
|
|
478
|
-
color_scale_a.all? {|x| x.is_a?(Hash)}
|
|
482
|
+
raise 'non-hash color_scale element' unless
|
|
483
|
+
color_scale_a.all? { |x| x.is_a?(Hash) }
|
|
479
484
|
|
|
480
485
|
format[:color_scale] = Axlsx::ColorScale.new(*color_scale_a)
|
|
481
486
|
end
|
|
@@ -484,13 +489,15 @@ class Marty::Xl
|
|
|
484
489
|
format[:dxfId] = add_style(dxfid) if dxfid.is_a?(Hash)
|
|
485
490
|
|
|
486
491
|
ws.add_conditional_formatting(range, format)
|
|
487
|
-
when
|
|
492
|
+
when 'image'
|
|
488
493
|
op, range, img = opl
|
|
489
494
|
raise "bad image params #{range}" unless
|
|
490
495
|
range.is_a?(Array) && range.length == 6
|
|
496
|
+
|
|
491
497
|
x1, y1, x2, y2, w, h = range
|
|
492
498
|
raise "bad image range, width or height #{range}" unless
|
|
493
|
-
[
|
|
499
|
+
[x1, y1, w, h].all? { |x| x.is_a? Integer }
|
|
500
|
+
|
|
494
501
|
ws.add_image(image_src: "#{Rails.public_path}/images/#{img}",
|
|
495
502
|
noSelect: true,
|
|
496
503
|
noMove: true) do |image|
|
|
@@ -502,28 +509,27 @@ class Marty::Xl
|
|
|
502
509
|
else
|
|
503
510
|
raise "unknown op #{opl[0]}"
|
|
504
511
|
end
|
|
505
|
-
|
|
506
|
-
worksheet_rows(ws,rows,styles,row_styles,format,borders,images) unless
|
|
507
|
-
[ops_pos.count, ops_brd.count].all?{ |a| a == 0 }
|
|
512
|
+
end
|
|
513
|
+
worksheet_rows(ws, rows, styles, row_styles, format, borders, images) unless
|
|
514
|
+
[ops_pos.count, ops_brd.count].all? { |a| a == 0 }
|
|
508
515
|
end
|
|
509
516
|
|
|
510
517
|
# recursive symbolize_keys. FIXME: this belongs in a generic
|
|
511
518
|
# library somewhere.
|
|
512
|
-
def self.symbolize_keys(obj, sym_str=nil)
|
|
519
|
+
def self.symbolize_keys(obj, sym_str = nil)
|
|
513
520
|
case obj
|
|
514
521
|
when Array
|
|
515
|
-
obj.map {|x| symbolize_keys(x, sym_str)}
|
|
522
|
+
obj.map { |x| symbolize_keys(x, sym_str) }
|
|
516
523
|
when Hash
|
|
517
|
-
obj.inject({})
|
|
524
|
+
obj.inject({}) do |result, (key, value)|
|
|
518
525
|
key = key.to_sym if key.is_a?(String)
|
|
519
526
|
result[key] = symbolize_keys(value, sym_str)
|
|
520
527
|
result
|
|
521
|
-
|
|
528
|
+
end
|
|
522
529
|
when String
|
|
523
530
|
(sym_str && obj.starts_with?(sym_str)) ? obj[sym_str.length..-1].to_sym : obj
|
|
524
531
|
else
|
|
525
532
|
obj
|
|
526
533
|
end
|
|
527
534
|
end
|
|
528
|
-
|
|
529
535
|
end
|