sp-duh 2.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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +2 -0
  4. data/Rakefile +32 -0
  5. data/config/i18n/i18n.xlsx +0 -0
  6. data/config/initializers/active_record/connection_adapters_postgre_sql_adapter.rb +165 -0
  7. data/config/initializers/active_record/migration_without_transaction.rb +4 -0
  8. data/config/initializers/active_record/migrator.rb +34 -0
  9. data/config/initializers/rails/generators.rb +13 -0
  10. data/config/jsonapi/settings.yml +14 -0
  11. data/config/locales/pt.yml +15 -0
  12. data/lib/generators/accounting_migration/accounting_migration_generator.rb +10 -0
  13. data/lib/generators/accounting_migration/templates/migration.rb +42 -0
  14. data/lib/generators/accounting_payroll_migration/accounting_payroll_migration_generator.rb +10 -0
  15. data/lib/generators/accounting_payroll_migration/templates/migration.rb +73 -0
  16. data/lib/generators/sharded_migration/sharded_migration_generator.rb +10 -0
  17. data/lib/generators/sharded_migration/templates/migration.rb +45 -0
  18. data/lib/sp-duh.rb +32 -0
  19. data/lib/sp/duh.rb +180 -0
  20. data/lib/sp/duh/adapters/pg/text_decoder/json.rb +15 -0
  21. data/lib/sp/duh/adapters/pg/text_encoder/json.rb +15 -0
  22. data/lib/sp/duh/db/transfer/backup.rb +71 -0
  23. data/lib/sp/duh/db/transfer/restore.rb +89 -0
  24. data/lib/sp/duh/engine.rb +35 -0
  25. data/lib/sp/duh/exceptions.rb +70 -0
  26. data/lib/sp/duh/i18n/excel_loader.rb +26 -0
  27. data/lib/sp/duh/jsonapi/adapters/base.rb +168 -0
  28. data/lib/sp/duh/jsonapi/adapters/db.rb +36 -0
  29. data/lib/sp/duh/jsonapi/adapters/raw_db.rb +77 -0
  30. data/lib/sp/duh/jsonapi/configuration.rb +167 -0
  31. data/lib/sp/duh/jsonapi/doc/apidoc_documentation_format_generator.rb +286 -0
  32. data/lib/sp/duh/jsonapi/doc/generator.rb +32 -0
  33. data/lib/sp/duh/jsonapi/doc/schema_catalog_helper.rb +97 -0
  34. data/lib/sp/duh/jsonapi/doc/victor_pinus_metadata_format_parser.rb +374 -0
  35. data/lib/sp/duh/jsonapi/exceptions.rb +56 -0
  36. data/lib/sp/duh/jsonapi/model/base.rb +25 -0
  37. data/lib/sp/duh/jsonapi/model/concerns/attributes.rb +94 -0
  38. data/lib/sp/duh/jsonapi/model/concerns/model.rb +42 -0
  39. data/lib/sp/duh/jsonapi/model/concerns/persistence.rb +221 -0
  40. data/lib/sp/duh/jsonapi/model/concerns/serialization.rb +59 -0
  41. data/lib/sp/duh/jsonapi/parameters.rb +44 -0
  42. data/lib/sp/duh/jsonapi/resource_publisher.rb +28 -0
  43. data/lib/sp/duh/jsonapi/service.rb +110 -0
  44. data/lib/sp/duh/migrations.rb +47 -0
  45. data/lib/sp/duh/migrations/migrator.rb +41 -0
  46. data/lib/sp/duh/repl.rb +193 -0
  47. data/lib/sp/duh/version.rb +25 -0
  48. data/lib/tasks/db_utils.rake +98 -0
  49. data/lib/tasks/doc.rake +27 -0
  50. data/lib/tasks/i18n.rake +23 -0
  51. data/lib/tasks/oauth.rake +29 -0
  52. data/lib/tasks/transfer.rake +48 -0
  53. data/lib/tasks/xls2jrxml.rake +15 -0
  54. data/test/jsonapi/server.rb +67 -0
  55. data/test/tasks/test.rake +10 -0
  56. metadata +170 -0
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-duh.
5
+ #
6
+ # sp-duh is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-duh is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-duh. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ # encoding: utf-8
20
+ #
21
+ def require_silently(require_name)
22
+ begin
23
+ require require_name
24
+ rescue LoadError
25
+ end
26
+ end
27
+
28
+ require 'highline/import'
29
+ require_silently 'byebug'
30
+ require_silently 'awesome_print'
31
+
32
+ require File.expand_path(File.join(File.dirname(__FILE__), 'sp', 'duh'))
@@ -0,0 +1,180 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-duh.
5
+ #
6
+ # sp-duh is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-duh is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-duh. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ # encoding: utf-8
20
+ #
21
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'version'))
22
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'engine'))
23
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'exceptions'))
24
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'repl'))
25
+
26
+ # JSONAPI library classes
27
+
28
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'exceptions'))
29
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'resource_publisher'))
30
+ # Parameters class
31
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'parameters'))
32
+ # Service classes
33
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'service'))
34
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'configuration'))
35
+ # Adpater classes
36
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'adapters', 'base'))
37
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'adapters', 'raw_db'))
38
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'adapters', 'db'))
39
+ # PG Adapters
40
+ require 'pg'
41
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'adapters', 'pg', 'text_decoder', 'json'))
42
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'adapters', 'pg', 'text_encoder', 'json'))
43
+
44
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'model', 'base'))
45
+
46
+ # Migrations library classes
47
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'migrations'))
48
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'migrations', 'migrator'))
49
+
50
+ # Backup and restore (transfer) library classes
51
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'db', 'transfer', 'backup'))
52
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'db', 'transfer', 'restore'))
53
+
54
+ # API documentationlibrary classes
55
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'jsonapi', 'doc', 'generator'))
56
+
57
+ # i18n classes
58
+ require File.expand_path(File.join(File.dirname(__FILE__), 'duh', 'i18n', 'excel_loader'))
59
+
60
+ # SP helper lib
61
+ require 'sp-excel-loader'
62
+
63
+ module SP
64
+ module Duh
65
+ def self.root
66
+ File.expand_path '../../..', __FILE__
67
+ end
68
+
69
+ def self.initsee (a_pg_conn, a_recreate = false)
70
+ if a_recreate
71
+ a_pg_conn.exec(%Q[
72
+
73
+ DROP FUNCTION IF EXISTS see (
74
+ a_module text,
75
+ a_version text,
76
+ a_query_map text,
77
+ a_calc_parameters text
78
+ );
79
+
80
+ DROP FUNCTION IF EXISTS see (
81
+ a_module text,
82
+ a_version text,
83
+ a_query_map text,
84
+ a_calc_parameters text,
85
+ a_log text,
86
+ a_debug boolean
87
+ );
88
+
89
+ DROP FUNCTION IF EXISTS see_payroll (
90
+ a_module text,
91
+ a_version text,
92
+ a_query_map text,
93
+ a_calc_parameters text,
94
+ a_clones text,
95
+ a_log text,
96
+ a_debug boolean
97
+ );
98
+
99
+ DROP FUNCTION IF EXISTS see_evaluate_expression (
100
+ a_expression text
101
+ );
102
+ DROP TYPE IF EXISTS see_record;
103
+ DROP TABLE IF EXISTS pg_see_json_table;
104
+ ]);
105
+ end
106
+ begin
107
+ a_pg_conn.exec(%Q[
108
+ CREATE TYPE see_record AS (json text, status text);
109
+ ])
110
+ rescue Exception => e
111
+ if a_recreate
112
+ raise e
113
+ end
114
+ end
115
+ begin
116
+ a_pg_conn.exec(%Q[
117
+ CREATE OR REPLACE FUNCTION see (
118
+ a_module text,
119
+ a_version text,
120
+ a_query_map text,
121
+ a_calc_parameters text,
122
+ a_log text default null,
123
+ a_debug boolean default false
124
+ ) RETURNS see_record AS '$libdir/pg-see.so', 'see' LANGUAGE C STRICT;
125
+
126
+ CREATE OR REPLACE FUNCTION see_payroll (
127
+ a_module text,
128
+ a_version text,
129
+ a_query_map text,
130
+ a_calc_parameters text,
131
+ a_clones text,
132
+ a_log text default null,
133
+ a_debug boolean default false
134
+ ) RETURNS see_record AS '$libdir/pg-see.so', 'see_payroll' LANGUAGE C STRICT;
135
+
136
+ CREATE OR REPLACE FUNCTION see_evaluate_expression (
137
+ a_expression text
138
+ ) RETURNS see_record AS '$libdir/pg-see.so', 'see_evaluate_expression' LANGUAGE C STRICT;
139
+
140
+ ])
141
+ rescue Exception => e
142
+ if a_recreate
143
+ raise e
144
+ end
145
+ end
146
+ begin
147
+ a_pg_conn.exec(%Q[
148
+ CREATE TABLE pg_see_json_table (
149
+ namespace character varying(255),
150
+ table_name character varying(255),
151
+ version character varying(20),
152
+ is_model boolean NOT NULL DEFAULT FALSE,
153
+ json text,
154
+ commit_hash character varying(255),
155
+ CONSTRAINT pg_see_json_table_pkey PRIMARY KEY(namespace, table_name, version)
156
+ );
157
+ ])
158
+ rescue => e
159
+ if a_recreate
160
+ raise e
161
+ end
162
+ end
163
+
164
+ end
165
+ end
166
+ end
167
+
168
+ def _log(message, prefix = nil)
169
+ message = message.is_a?(String) ? message : message.inspect
170
+ prefix = "SP::Duh#{prefix.blank? ? '' : ' [' + prefix + ']'}: "
171
+ if Rails.logger && !defined?(Rails::Console)
172
+ Rails.logger.debug "#{prefix}#{message}"
173
+ else
174
+ puts "#{prefix}#{message}"
175
+ end
176
+ end
177
+
178
+ # Configure the I18n module for correct usage when outside a Rails app (tests)
179
+ I18n.load_path += Dir[File.join(SP::Duh.root, 'config', 'locales', '*.{rb,yml}')]
180
+ I18n.default_locale = :pt
@@ -0,0 +1,15 @@
1
+ module SP
2
+ module Duh
3
+ module Adapters
4
+ module PG
5
+ module TextDecoder
6
+ class Json < ::PG::SimpleDecoder
7
+ def decode(string, tuple=nil, field=nil)
8
+ JSON.parse string
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SP
2
+ module Duh
3
+ module Adapters
4
+ module PG
5
+ module TextEncoder
6
+ class Json < ::PG::SimpleEncoder
7
+ def decode(string, tuple=nil, field=nil)
8
+ JSON.parse string
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,71 @@
1
+ module SP
2
+ module Duh
3
+ module Db
4
+ module Transfer
5
+
6
+ def self.log_with_time(message)
7
+ puts "[#{Time.now.strftime('%d-%m-%Y %H:%M')}] #{message}"
8
+ end
9
+
10
+ class Backup
11
+
12
+ attr_reader :status
13
+
14
+ def initialize(source_pg_connection)
15
+ @connection = source_pg_connection
16
+ end
17
+
18
+ def execute(company_id, dump_file = nil)
19
+ self.status = :ok
20
+ @company_id = company_id
21
+ yield(:before_execute) if block_given?
22
+ before_execute
23
+ yield(:do_execute) if block_given?
24
+ do_execute(dump_file) if self.status == :ok
25
+ yield(:after_execute) if block_given?
26
+ after_execute if self.status == :ok
27
+ end
28
+
29
+ protected
30
+
31
+ def before_execute
32
+ SP::Duh::Db::Transfer.log_with_time "STARTED backing up company #{@company_id}"
33
+ SP::Duh::Db::Transfer.log_with_time "Preparing backup..."
34
+ @started_at = Time.now
35
+ @schemas = @connection.exec %Q[
36
+ SELECT * FROM transfer.backup_before_execute(#{@company_id});
37
+ ]
38
+ @schemas = @schemas.map { |result| result['schema_name'] }
39
+ end
40
+
41
+ def do_execute(dump_file = nil)
42
+ SP::Duh::Db::Transfer.log_with_time "Executing backup..."
43
+ dump_file = "#{Time.now.strftime('%Y%m%d%H%M')}_c#{@company_id}.dump" if dump_file.nil?
44
+ command = "pg_dump -Fc -O --quote-all-identifiers --data-only -n #{@schemas.join(' -n ')} -h #{@connection.host} -p #{@connection.port} -U #{@connection.user} #{@connection.db} > #{dump_file}"
45
+ SP::Duh::Db::Transfer.log_with_time command
46
+ %x[ #{command} ]
47
+ if $?.exitstatus != 0
48
+ File.delete dump_file
49
+ self.status = :error_dumping_company
50
+ end
51
+ end
52
+
53
+ def after_execute
54
+ @ended_at = Time.now
55
+ SP::Duh::Db::Transfer.log_with_time "FINISHED backing up company #{@company_id} in #{(@ended_at - @started_at).round(2)}s"
56
+ end
57
+
58
+ def status=(value)
59
+ @status = value
60
+ if value != :ok
61
+ @ended_at = Time.now
62
+ SP::Duh::Db::Transfer.log_with_time "CANCELED backing up company #{@company_id} in #{(@ended_at - @started_at).round(2)}s"
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,89 @@
1
+ module SP
2
+ module Duh
3
+ module Db
4
+ module Transfer
5
+
6
+ class Restore
7
+
8
+ def initialize(destination_pg_connection)
9
+ @connection = destination_pg_connection
10
+ end
11
+
12
+ def execute(company_id, dump_file, skip = false)
13
+
14
+ @company_id = company_id
15
+ @dump_file = dump_file
16
+
17
+ yield(:before, :before_execute) if block_given?
18
+ results = before_execute(skip)
19
+ yield(:after, :before_execute, results) if block_given?
20
+
21
+ return results if skip
22
+
23
+ yield(:before, :do_execute) if block_given?
24
+ results = do_execute
25
+ yield(:after, :do_execute, results) if block_given?
26
+
27
+ yield(:before, :after_execute) if block_given?
28
+ results = after_execute
29
+ yield(:after, :after_execute, results) if block_given?
30
+
31
+ return results
32
+
33
+ end
34
+
35
+ protected
36
+
37
+ def before_execute(skip = false)
38
+ services_configuration_file = File.join(Dir.pwd, "config", "cloudware_services.yml")
39
+ raise "Configuration file #{services_configuration_file} not found." if !File.exist?(services_configuration_file)
40
+ services_configuration = YAML.load_file(services_configuration_file)
41
+ raise "Invalid configuration in file #{services_configuration_file}." if services_configuration.nil?
42
+ @template_company_id = services_configuration[:company_id] || services_configuration['company_id']
43
+ raise "No template company id found in file #{services_configuration_file}." if @template_company_id.nil?
44
+ @template_company_id = @template_company_id.to_i
45
+ SP::Duh::Db::Transfer.log_with_time "STARTED restoring company #{@company_id} from dump #{@dump_file}"
46
+ SP::Duh::Db::Transfer.log_with_time "Preparing restore..."
47
+ @started_at = Time.now
48
+ meta_schema = @connection.exec %Q[
49
+ SELECT * FROM transfer.restore_before_before_execute(#{@company_id});
50
+ ]
51
+ meta_schema = meta_schema.first.values.first
52
+ raise "Backup file #{@dump_file} not found." if !File.exist?(@dump_file)
53
+ command = "pg_restore -Fc -n #{meta_schema} --data-only -h #{@connection.host} -p #{@connection.port} -U #{@connection.user} -d #{@connection.db} < #{@dump_file}"
54
+ SP::Duh::Db::Transfer.log_with_time "Restoring the backup metadata..."
55
+ SP::Duh::Db::Transfer.log_with_time command
56
+ result = %x[ #{command} ]
57
+ if skip
58
+ SP::Duh::Db::Transfer.log_with_time "Processing metadata..."
59
+ else
60
+ SP::Duh::Db::Transfer.log_with_time "Processing metadata and foreign records..."
61
+ end
62
+ @schemas = @connection.exec %Q[
63
+ SELECT * FROM transfer.restore_after_before_execute(#{@company_id}, #{@template_company_id}, #{skip});
64
+ ]
65
+ @schemas = @schemas.map { |result| result['schema_name'] }
66
+ end
67
+
68
+ def do_execute
69
+ SP::Duh::Db::Transfer.log_with_time "Executing restore..."
70
+ command = "pg_restore -Fc -n #{@schemas.join(' -n ')} -h #{@connection.host} -p #{@connection.port} -U #{@connection.user} -d #{@connection.db} < #{@dump_file}"
71
+ SP::Duh::Db::Transfer.log_with_time command
72
+ result = %x[ #{command} ]
73
+ end
74
+
75
+ def after_execute
76
+ SP::Duh::Db::Transfer.log_with_time "Finishing restore..."
77
+ @connection.exec %Q[
78
+ SELECT * FROM transfer.restore_after_execute(#{@company_id}, #{@template_company_id});
79
+ ]
80
+ @ended_at = Time.now
81
+ SP::Duh::Db::Transfer.log_with_time "FINISHED restoring company #{@company_id} in #{(@ended_at - @started_at).round(2)}s"
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-duh.
5
+ #
6
+ # sp-duh is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-duh is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-duh. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ # encoding: utf-8
20
+ #
21
+ require 'rails'
22
+
23
+ module SP
24
+ module Duh
25
+ class Engine < ::Rails::Engine
26
+ isolate_namespace SP::Duh
27
+
28
+ initializer :append_migrations do |app|
29
+ unless app.root.to_s.match root.to_s
30
+ app.config.paths["db/migrate"] += config.paths["db/migrate"].expanded
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-duh.
5
+ #
6
+ # sp-duh is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-duh is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-duh. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ # encoding: utf-8
20
+ #
21
+ module SP
22
+ module Duh
23
+ module Exceptions
24
+
25
+ # Generic errors
26
+
27
+ class GenericError < StandardError
28
+
29
+ attr_reader :nested
30
+ attr_reader :raw_backtrace
31
+
32
+ def initialize(message = nil, nested = $!)
33
+ if message.nil?
34
+ message = I18n.t("sp-duh.exceptions.#{type.underscore.gsub('/','.')}") if I18n.exists?("sp-duh.exceptions.#{type.underscore.gsub('/','.')}")
35
+ end
36
+ super(message)
37
+ @nested = nested
38
+ end
39
+
40
+ def set_backtrace(backtrace)
41
+ @raw_backtrace = backtrace
42
+ if nested
43
+ backtrace = backtrace - nested_raw_backtrace
44
+ backtrace += ["#{nested.backtrace.first}: #{nested.message} (#{nested.class.name})"]
45
+ backtrace += nested.backtrace[1..-1] || []
46
+ end
47
+ super(backtrace)
48
+ end
49
+
50
+ protected
51
+
52
+ def type ; self.class.name.sub("SP::Duh::", "").sub("Exceptions::", "") ; end
53
+
54
+ private
55
+
56
+ def nested_raw_backtrace
57
+ nested.respond_to?(:raw_backtrace) ? nested.raw_backtrace : nested.backtrace
58
+ end
59
+ end
60
+
61
+ class GenericDetailedError < GenericError
62
+ def initialize(details = {})
63
+ message = I18n.t("sp-duh.exceptions.#{type.underscore.gsub('/','.')}", details)
64
+ super(message)
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end