acts_as_calculator 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +38 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/Rakefile +12 -0
- data/app/controllers/acts_as_calculator/application_controller.rb +76 -0
- data/app/controllers/acts_as_calculator/formula_versions_controller.rb +50 -0
- data/app/controllers/acts_as_calculator/formulas_controller.rb +58 -0
- data/app/controllers/acts_as_calculator/imports_controller.rb +33 -0
- data/app/controllers/acts_as_calculator/templates_controller.rb +66 -0
- data/app/models/acts_as_calculator/formula.rb +21 -0
- data/app/models/acts_as_calculator/formula_version.rb +88 -0
- data/app/models/acts_as_calculator/lookup_table.rb +25 -0
- data/app/models/acts_as_calculator/lookup_table_entry.rb +26 -0
- data/app/models/acts_as_calculator/record.rb +12 -0
- data/app/models/acts_as_calculator/run.rb +24 -0
- data/app/models/acts_as_calculator/template.rb +56 -0
- data/app/models/acts_as_calculator/variable.rb +20 -0
- data/config/routes.rb +36 -0
- data/lib/acts_as_calculator/aggregate_results.rb +57 -0
- data/lib/acts_as_calculator/aggregation.rb +9 -0
- data/lib/acts_as_calculator/apportion_amount.rb +62 -0
- data/lib/acts_as_calculator/apportionment.rb +57 -0
- data/lib/acts_as_calculator/build_calculator.rb +24 -0
- data/lib/acts_as_calculator/build_lookups.rb +37 -0
- data/lib/acts_as_calculator/calculable.rb +84 -0
- data/lib/acts_as_calculator/calculator_cache.rb +36 -0
- data/lib/acts_as_calculator/cast_date.rb +16 -0
- data/lib/acts_as_calculator/cast_decimal.rb +15 -0
- data/lib/acts_as_calculator/cast_json_safe.rb +37 -0
- data/lib/acts_as_calculator/cast_liquid_value.rb +58 -0
- data/lib/acts_as_calculator/cast_variable_attributes.rb +17 -0
- data/lib/acts_as_calculator/configuration.rb +31 -0
- data/lib/acts_as_calculator/distribute_remainder.rb +49 -0
- data/lib/acts_as_calculator/divide_proportionally.rb +27 -0
- data/lib/acts_as_calculator/engine.rb +19 -0
- data/lib/acts_as_calculator/errors.rb +23 -0
- data/lib/acts_as_calculator/evaluate_expression.rb +49 -0
- data/lib/acts_as_calculator/evaluate_formula.rb +53 -0
- data/lib/acts_as_calculator/find_lookup_table_references.rb +68 -0
- data/lib/acts_as_calculator/find_owned_record.rb +38 -0
- data/lib/acts_as_calculator/find_tier.rb +25 -0
- data/lib/acts_as_calculator/formula_version_drop.rb +55 -0
- data/lib/acts_as_calculator/function_registry.rb +83 -0
- data/lib/acts_as_calculator/import_definitions.rb +70 -0
- data/lib/acts_as_calculator/import_formula.rb +102 -0
- data/lib/acts_as_calculator/import_lookup_table.rb +94 -0
- data/lib/acts_as_calculator/import_outcome.rb +20 -0
- data/lib/acts_as_calculator/import_summary.rb +29 -0
- data/lib/acts_as_calculator/import_template.rb +57 -0
- data/lib/acts_as_calculator/liquid_filters.rb +70 -0
- data/lib/acts_as_calculator/persist_run.rb +31 -0
- data/lib/acts_as_calculator/promote_template.rb +21 -0
- data/lib/acts_as_calculator/publish_formula_version.rb +54 -0
- data/lib/acts_as_calculator/publish_template.rb +18 -0
- data/lib/acts_as_calculator/read_import_file.rb +23 -0
- data/lib/acts_as_calculator/render_liquid.rb +113 -0
- data/lib/acts_as_calculator/render_template.rb +43 -0
- data/lib/acts_as_calculator/resolve_formula_version.rb +57 -0
- data/lib/acts_as_calculator/resolve_import_owner.rb +38 -0
- data/lib/acts_as_calculator/resolve_template.rb +41 -0
- data/lib/acts_as_calculator/resolve_variables.rb +105 -0
- data/lib/acts_as_calculator/result.rb +32 -0
- data/lib/acts_as_calculator/result_drop.rb +46 -0
- data/lib/acts_as_calculator/serialize_formula.rb +18 -0
- data/lib/acts_as_calculator/serialize_formula_version.rb +26 -0
- data/lib/acts_as_calculator/serialize_import_summary.rb +21 -0
- data/lib/acts_as_calculator/serialize_template.rb +13 -0
- data/lib/acts_as_calculator/supersede_formula_versions.rb +83 -0
- data/lib/acts_as_calculator/tier.rb +31 -0
- data/lib/acts_as_calculator/variable_spec.rb +33 -0
- data/lib/acts_as_calculator/version.rb +5 -0
- data/lib/acts_as_calculator.rb +78 -0
- data/lib/generators/acts_as_calculator/import/import_generator.rb +26 -0
- data/lib/generators/acts_as_calculator/install/install_generator.rb +31 -0
- data/lib/generators/acts_as_calculator/install/templates/create_acts_as_calculator_tables.rb.tt +133 -0
- data/lib/tasks/acts_as_calculator.rake +21 -0
- data/sig/acts_as_calculator.rbs +4 -0
- metadata +251 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActsAsCalculator
|
|
4
|
+
VariableSpec = Data.define(:name, :source_type, :source_config, :required) do
|
|
5
|
+
def self.build(source)
|
|
6
|
+
attributes = source.is_a?(Hash) ? source : read_from(source)
|
|
7
|
+
attributes = attributes.transform_keys(&:to_sym)
|
|
8
|
+
required = attributes[:required]
|
|
9
|
+
required = true if required.nil?
|
|
10
|
+
|
|
11
|
+
new(
|
|
12
|
+
name: attributes.fetch(:name).to_s,
|
|
13
|
+
source_type: (attributes[:source_type] || :context).to_sym,
|
|
14
|
+
source_config: (attributes[:source_config] || {}).transform_keys(&:to_sym),
|
|
15
|
+
required:
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.read_from(source)
|
|
20
|
+
{
|
|
21
|
+
name: source.name,
|
|
22
|
+
source_type: source.source_type,
|
|
23
|
+
source_config: source.source_config,
|
|
24
|
+
required: source.respond_to?(:required) ? source.required : true
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
private_class_method :read_from
|
|
28
|
+
|
|
29
|
+
def default
|
|
30
|
+
source_config[:default]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bigdecimal"
|
|
4
|
+
require "dentaku"
|
|
5
|
+
require "liquid"
|
|
6
|
+
|
|
7
|
+
require_relative "acts_as_calculator/version"
|
|
8
|
+
require_relative "acts_as_calculator/errors"
|
|
9
|
+
require_relative "acts_as_calculator/configuration"
|
|
10
|
+
require_relative "acts_as_calculator/cast_decimal"
|
|
11
|
+
require_relative "acts_as_calculator/result"
|
|
12
|
+
require_relative "acts_as_calculator/tier"
|
|
13
|
+
require_relative "acts_as_calculator/variable_spec"
|
|
14
|
+
require_relative "acts_as_calculator/find_tier"
|
|
15
|
+
require_relative "acts_as_calculator/function_registry"
|
|
16
|
+
require_relative "acts_as_calculator/build_calculator"
|
|
17
|
+
require_relative "acts_as_calculator/evaluate_expression"
|
|
18
|
+
require_relative "acts_as_calculator/resolve_variables"
|
|
19
|
+
require_relative "acts_as_calculator/divide_proportionally"
|
|
20
|
+
require_relative "acts_as_calculator/distribute_remainder"
|
|
21
|
+
require_relative "acts_as_calculator/apportionment"
|
|
22
|
+
require_relative "acts_as_calculator/apportion_amount"
|
|
23
|
+
require_relative "acts_as_calculator/aggregation"
|
|
24
|
+
require_relative "acts_as_calculator/aggregate_results"
|
|
25
|
+
require_relative "acts_as_calculator/formula_version_drop"
|
|
26
|
+
require_relative "acts_as_calculator/result_drop"
|
|
27
|
+
require_relative "acts_as_calculator/cast_liquid_value"
|
|
28
|
+
require_relative "acts_as_calculator/liquid_filters"
|
|
29
|
+
require_relative "acts_as_calculator/render_liquid"
|
|
30
|
+
|
|
31
|
+
module ActsAsCalculator
|
|
32
|
+
# The scope every formula, lookup table and template falls into when a host doesn't
|
|
33
|
+
# namespace one. A non-null column beats a nullable one here because uniqueness is
|
|
34
|
+
# scoped on `[key, scope, owner_type, owner_id]` and NULLs never compare equal.
|
|
35
|
+
DEFAULT_SCOPE = "default"
|
|
36
|
+
|
|
37
|
+
# Defined here rather than in engine.rb because Engine.isolate_namespace only leaves
|
|
38
|
+
# this alone if the module already answers to it — otherwise it installs an
|
|
39
|
+
# `on_load(:active_record)` hook that redefines it to "acts_as_calculator_" long after
|
|
40
|
+
# the engine class body has run. The tables are named for what they hold.
|
|
41
|
+
def self.table_name_prefix
|
|
42
|
+
"calculator_"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
require_relative "acts_as_calculator/cast_date"
|
|
47
|
+
require_relative "acts_as_calculator/cast_json_safe"
|
|
48
|
+
require_relative "acts_as_calculator/calculator_cache"
|
|
49
|
+
require_relative "acts_as_calculator/find_owned_record"
|
|
50
|
+
require_relative "acts_as_calculator/build_lookups"
|
|
51
|
+
require_relative "acts_as_calculator/resolve_formula_version"
|
|
52
|
+
require_relative "acts_as_calculator/persist_run"
|
|
53
|
+
require_relative "acts_as_calculator/evaluate_formula"
|
|
54
|
+
require_relative "acts_as_calculator/resolve_template"
|
|
55
|
+
require_relative "acts_as_calculator/promote_template"
|
|
56
|
+
require_relative "acts_as_calculator/render_template"
|
|
57
|
+
require_relative "acts_as_calculator/import_outcome"
|
|
58
|
+
require_relative "acts_as_calculator/import_summary"
|
|
59
|
+
require_relative "acts_as_calculator/read_import_file"
|
|
60
|
+
require_relative "acts_as_calculator/resolve_import_owner"
|
|
61
|
+
require_relative "acts_as_calculator/find_lookup_table_references"
|
|
62
|
+
require_relative "acts_as_calculator/supersede_formula_versions"
|
|
63
|
+
require_relative "acts_as_calculator/cast_variable_attributes"
|
|
64
|
+
require_relative "acts_as_calculator/publish_formula_version"
|
|
65
|
+
require_relative "acts_as_calculator/publish_template"
|
|
66
|
+
require_relative "acts_as_calculator/import_lookup_table"
|
|
67
|
+
require_relative "acts_as_calculator/import_formula"
|
|
68
|
+
require_relative "acts_as_calculator/import_template"
|
|
69
|
+
require_relative "acts_as_calculator/import_definitions"
|
|
70
|
+
require_relative "acts_as_calculator/serialize_formula_version"
|
|
71
|
+
require_relative "acts_as_calculator/serialize_formula"
|
|
72
|
+
require_relative "acts_as_calculator/serialize_template"
|
|
73
|
+
require_relative "acts_as_calculator/serialize_import_summary"
|
|
74
|
+
|
|
75
|
+
# Everything above is plain Ruby and stays loadable without Rails; the two below are the
|
|
76
|
+
# only files that touch a framework, so they load only when one is already present.
|
|
77
|
+
require_relative "acts_as_calculator/calculable" if defined?(::ActiveSupport)
|
|
78
|
+
require_relative "acts_as_calculator/engine" if defined?(::Rails::Engine)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module ActsAsCalculator
|
|
6
|
+
module Generators
|
|
7
|
+
# A thin wrapper: read the path, hand it to ImportDefinitions, print what happened.
|
|
8
|
+
# Deliberately no `source_root` and no templates — this generator writes rows, not files.
|
|
9
|
+
class ImportGenerator < ::Rails::Generators::Base
|
|
10
|
+
desc "Imports formulas, lookup tables and templates from a JSON file. Idempotent: " \
|
|
11
|
+
"re-importing an unchanged file changes nothing, and changed content becomes a " \
|
|
12
|
+
"new version rather than an edit to an existing one."
|
|
13
|
+
|
|
14
|
+
argument :path, type: :string, desc: "Path to the JSON import file"
|
|
15
|
+
|
|
16
|
+
def import
|
|
17
|
+
summary = ImportDefinitions.(path: File.expand_path(path, destination_root))
|
|
18
|
+
say summary.to_s
|
|
19
|
+
|
|
20
|
+
raise Thor::Error, "acts_as_calculator: #{summary.failures.size} import error(s)" unless summary.success?
|
|
21
|
+
rescue ActsAsCalculator::Error => e
|
|
22
|
+
raise Thor::Error, "acts_as_calculator: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module ActsAsCalculator
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
9
|
+
include ::Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
desc "Copies the acts_as_calculator migration for the six calculator_* tables."
|
|
14
|
+
|
|
15
|
+
def self.next_migration_number(dirname)
|
|
16
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_migration_file
|
|
20
|
+
migration_template "create_acts_as_calculator_tables.rb.tt",
|
|
21
|
+
"db/migrate/create_acts_as_calculator_tables.rb"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def migration_version
|
|
27
|
+
"[#{::ActiveRecord::Migration.current_version}]"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/generators/acts_as_calculator/install/templates/create_acts_as_calculator_tables.rb.tt
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateActsAsCalculatorTables < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
create_table :calculator_formulas do |t|
|
|
6
|
+
t.string :key, null: false
|
|
7
|
+
t.string :scope, null: false, default: "default"
|
|
8
|
+
t.references :owner, polymorphic: true, null: true, index: false
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
add_index :calculator_formulas, %i[key scope owner_type owner_id],
|
|
12
|
+
unique: true, name: "idx_calc_formulas_on_key_scope_owner"
|
|
13
|
+
add_index :calculator_formulas, %i[owner_type owner_id], name: "idx_calc_formulas_on_owner"
|
|
14
|
+
|
|
15
|
+
create_table :calculator_formula_versions do |t|
|
|
16
|
+
t.bigint :formula_id, null: false
|
|
17
|
+
t.integer :version_number, null: false
|
|
18
|
+
t.text :expression, null: false
|
|
19
|
+
t.date :effective_from, null: false
|
|
20
|
+
t.date :effective_to
|
|
21
|
+
t.string :status, null: false, default: "draft"
|
|
22
|
+
t.text :change_note
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
add_index :calculator_formula_versions, %i[formula_id version_number],
|
|
26
|
+
unique: true, name: "idx_calc_versions_on_formula_and_number"
|
|
27
|
+
add_index :calculator_formula_versions, %i[formula_id status effective_from effective_to],
|
|
28
|
+
name: "idx_calc_versions_on_effective_range"
|
|
29
|
+
add_foreign_key :calculator_formula_versions, :calculator_formulas, column: :formula_id
|
|
30
|
+
|
|
31
|
+
create_table :calculator_variables do |t|
|
|
32
|
+
t.bigint :formula_version_id, null: false
|
|
33
|
+
t.string :name, null: false
|
|
34
|
+
t.string :source_type, null: false
|
|
35
|
+
t.column :source_config, json_type, null: false, default: {}
|
|
36
|
+
t.boolean :required, null: false, default: true
|
|
37
|
+
t.timestamps
|
|
38
|
+
end
|
|
39
|
+
add_index :calculator_variables, %i[formula_version_id name],
|
|
40
|
+
unique: true, name: "idx_calc_variables_on_version_and_name"
|
|
41
|
+
add_foreign_key :calculator_variables, :calculator_formula_versions, column: :formula_version_id
|
|
42
|
+
|
|
43
|
+
create_table :calculator_lookup_tables do |t|
|
|
44
|
+
t.string :key, null: false
|
|
45
|
+
t.string :scope, null: false, default: "default"
|
|
46
|
+
t.references :owner, polymorphic: true, null: true, index: false
|
|
47
|
+
t.timestamps
|
|
48
|
+
end
|
|
49
|
+
add_index :calculator_lookup_tables, %i[key scope owner_type owner_id],
|
|
50
|
+
unique: true, name: "idx_calc_lookup_tables_on_key_scope_owner"
|
|
51
|
+
add_index :calculator_lookup_tables, %i[owner_type owner_id], name: "idx_calc_lookup_tables_on_owner"
|
|
52
|
+
|
|
53
|
+
create_table :calculator_lookup_table_entries do |t|
|
|
54
|
+
t.bigint :lookup_table_id, null: false
|
|
55
|
+
t.integer :position, null: false, default: 0
|
|
56
|
+
t.decimal :from, precision: 20, scale: 6
|
|
57
|
+
t.decimal :to, precision: 20, scale: 6
|
|
58
|
+
t.decimal :value, precision: 20, scale: 6, null: false
|
|
59
|
+
t.timestamps
|
|
60
|
+
end
|
|
61
|
+
add_index :calculator_lookup_table_entries, %i[lookup_table_id position],
|
|
62
|
+
name: "idx_calc_lookup_entries_on_table_and_position"
|
|
63
|
+
add_foreign_key :calculator_lookup_table_entries, :calculator_lookup_tables, column: :lookup_table_id
|
|
64
|
+
|
|
65
|
+
create_table :calculator_templates do |t|
|
|
66
|
+
t.string :key, null: false
|
|
67
|
+
t.string :scope, null: false, default: "default"
|
|
68
|
+
t.references :owner, polymorphic: true, null: true, index: false
|
|
69
|
+
t.text :body, null: false
|
|
70
|
+
t.string :format, null: false, default: "html"
|
|
71
|
+
t.integer :version_number, null: false, default: 1
|
|
72
|
+
# Defaults to true so authoring a new version publishes it; rollback is pointing
|
|
73
|
+
# `current` back at an older row, which keeps the newer one in the history.
|
|
74
|
+
t.boolean :current, null: false, default: true
|
|
75
|
+
t.timestamps
|
|
76
|
+
end
|
|
77
|
+
add_index :calculator_templates, %i[key scope owner_type owner_id version_number],
|
|
78
|
+
unique: true, name: "idx_calc_templates_on_key_scope_owner_version"
|
|
79
|
+
add_index :calculator_templates, %i[owner_type owner_id], name: "idx_calc_templates_on_owner"
|
|
80
|
+
|
|
81
|
+
create_table :calculator_runs do |t|
|
|
82
|
+
t.references :calculable, polymorphic: true, null: false, index: false
|
|
83
|
+
t.bigint :formula_version_id, null: false
|
|
84
|
+
t.date :as_of_date, null: false
|
|
85
|
+
t.column :inputs, json_type, null: false, default: {}
|
|
86
|
+
t.column :breakdown, json_type, null: false, default: {}
|
|
87
|
+
t.decimal :result, precision: 30, scale: 10, null: false
|
|
88
|
+
t.timestamps
|
|
89
|
+
end
|
|
90
|
+
add_index :calculator_runs, %i[calculable_type calculable_id as_of_date],
|
|
91
|
+
name: "idx_calc_runs_on_calculable_and_date"
|
|
92
|
+
add_index :calculator_runs, :formula_version_id, name: "idx_calc_runs_on_formula_version"
|
|
93
|
+
add_foreign_key :calculator_runs, :calculator_formula_versions, column: :formula_version_id
|
|
94
|
+
|
|
95
|
+
add_global_uniqueness :calculator_formulas, %i[key scope], "idx_calc_formulas_global_key_scope"
|
|
96
|
+
add_global_uniqueness :calculator_lookup_tables, %i[key scope], "idx_calc_lookup_tables_global_key_scope"
|
|
97
|
+
add_global_uniqueness :calculator_templates, %i[key scope version_number],
|
|
98
|
+
"idx_calc_templates_global_key_scope_version"
|
|
99
|
+
|
|
100
|
+
add_single_current_template
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# Exactly one version of a template may be current per [key, scope, owner]. Both halves
|
|
106
|
+
# are needed for the same reason as add_global_uniqueness: the composite index cannot
|
|
107
|
+
# constrain rows whose owner_id is NULL, so globally-owned templates get their own.
|
|
108
|
+
def add_single_current_template
|
|
109
|
+
return unless connection.supports_partial_index?
|
|
110
|
+
|
|
111
|
+
add_index :calculator_templates, %i[key scope owner_type owner_id],
|
|
112
|
+
unique: true, where: "current = TRUE", name: "idx_calc_templates_one_current"
|
|
113
|
+
add_index :calculator_templates, %i[key scope],
|
|
114
|
+
unique: true, where: "current = TRUE AND owner_id IS NULL",
|
|
115
|
+
name: "idx_calc_templates_one_current_global"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# A composite unique index over a nullable owner does not constrain globally-owned rows
|
|
119
|
+
# at all: NULLs never compare equal, so two `owner_id IS NULL` rows with the same key
|
|
120
|
+
# both slip past it. A partial index closes that on every adapter that has one; the
|
|
121
|
+
# model-level uniqueness validation is the only guard on the rest.
|
|
122
|
+
def add_global_uniqueness(table, columns, name)
|
|
123
|
+
return unless connection.supports_partial_index?
|
|
124
|
+
|
|
125
|
+
add_index table, columns, unique: true, where: "owner_id IS NULL", name: name
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# jsonb is Postgres-only; every other adapter Rails ships gets its own json type, which
|
|
129
|
+
# for sqlite3 and older MySQL is text with JSON casting on top.
|
|
130
|
+
def json_type
|
|
131
|
+
connection.adapter_name.match?(/postgres/i) ? :jsonb : :json
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :acts_as_calculator do
|
|
4
|
+
desc "Imports formulas, lookup tables and templates from a JSON file " \
|
|
5
|
+
"(rake acts_as_calculator:import[config/calculator/payroll.json])"
|
|
6
|
+
task :import, [:path] => :environment do |_task, args|
|
|
7
|
+
path = args[:path]
|
|
8
|
+
abort("usage: rake acts_as_calculator:import[path/to/file.json]") if path.nil? || path.empty?
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
summary = ActsAsCalculator::ImportDefinitions.(path:)
|
|
12
|
+
rescue ActsAsCalculator::Error => e
|
|
13
|
+
abort("acts_as_calculator: #{e.message}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
puts summary
|
|
17
|
+
# Non-zero on any failed entry so a deploy or CI step stops here rather than shipping a
|
|
18
|
+
# half-imported rule set.
|
|
19
|
+
abort("acts_as_calculator: #{summary.failures.size} import error(s)") unless summary.success?
|
|
20
|
+
end
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: acts_as_calculator
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- lautarograc
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bigdecimal
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: dentaku
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '4.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '4.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: liquid
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.4'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.4'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: actionpack
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '7.1'
|
|
62
|
+
- - "<"
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '9'
|
|
65
|
+
type: :runtime
|
|
66
|
+
prerelease: false
|
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '7.1'
|
|
72
|
+
- - "<"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '9'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: activerecord
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '7.1'
|
|
82
|
+
- - "<"
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '9'
|
|
85
|
+
type: :runtime
|
|
86
|
+
prerelease: false
|
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '7.1'
|
|
92
|
+
- - "<"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '9'
|
|
95
|
+
- !ruby/object:Gem::Dependency
|
|
96
|
+
name: activesupport
|
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '7.1'
|
|
102
|
+
- - "<"
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '9'
|
|
105
|
+
type: :runtime
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '7.1'
|
|
112
|
+
- - "<"
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '9'
|
|
115
|
+
- !ruby/object:Gem::Dependency
|
|
116
|
+
name: railties
|
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '7.1'
|
|
122
|
+
- - "<"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '9'
|
|
125
|
+
type: :runtime
|
|
126
|
+
prerelease: false
|
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '7.1'
|
|
132
|
+
- - "<"
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '9'
|
|
135
|
+
description: Mix Calculable into any model to get effective-dated, versioned formulas,
|
|
136
|
+
apportionment and aggregation helpers, and Liquid-rendered output — reusable across
|
|
137
|
+
payroll, e-commerce, and insurance domains.
|
|
138
|
+
email:
|
|
139
|
+
- lautarograciani2106@gmail.com
|
|
140
|
+
executables: []
|
|
141
|
+
extensions: []
|
|
142
|
+
extra_rdoc_files: []
|
|
143
|
+
files:
|
|
144
|
+
- ".rspec"
|
|
145
|
+
- ".rubocop.yml"
|
|
146
|
+
- CODE_OF_CONDUCT.md
|
|
147
|
+
- LICENSE.txt
|
|
148
|
+
- README.md
|
|
149
|
+
- Rakefile
|
|
150
|
+
- app/controllers/acts_as_calculator/application_controller.rb
|
|
151
|
+
- app/controllers/acts_as_calculator/formula_versions_controller.rb
|
|
152
|
+
- app/controllers/acts_as_calculator/formulas_controller.rb
|
|
153
|
+
- app/controllers/acts_as_calculator/imports_controller.rb
|
|
154
|
+
- app/controllers/acts_as_calculator/templates_controller.rb
|
|
155
|
+
- app/models/acts_as_calculator/formula.rb
|
|
156
|
+
- app/models/acts_as_calculator/formula_version.rb
|
|
157
|
+
- app/models/acts_as_calculator/lookup_table.rb
|
|
158
|
+
- app/models/acts_as_calculator/lookup_table_entry.rb
|
|
159
|
+
- app/models/acts_as_calculator/record.rb
|
|
160
|
+
- app/models/acts_as_calculator/run.rb
|
|
161
|
+
- app/models/acts_as_calculator/template.rb
|
|
162
|
+
- app/models/acts_as_calculator/variable.rb
|
|
163
|
+
- config/routes.rb
|
|
164
|
+
- lib/acts_as_calculator.rb
|
|
165
|
+
- lib/acts_as_calculator/aggregate_results.rb
|
|
166
|
+
- lib/acts_as_calculator/aggregation.rb
|
|
167
|
+
- lib/acts_as_calculator/apportion_amount.rb
|
|
168
|
+
- lib/acts_as_calculator/apportionment.rb
|
|
169
|
+
- lib/acts_as_calculator/build_calculator.rb
|
|
170
|
+
- lib/acts_as_calculator/build_lookups.rb
|
|
171
|
+
- lib/acts_as_calculator/calculable.rb
|
|
172
|
+
- lib/acts_as_calculator/calculator_cache.rb
|
|
173
|
+
- lib/acts_as_calculator/cast_date.rb
|
|
174
|
+
- lib/acts_as_calculator/cast_decimal.rb
|
|
175
|
+
- lib/acts_as_calculator/cast_json_safe.rb
|
|
176
|
+
- lib/acts_as_calculator/cast_liquid_value.rb
|
|
177
|
+
- lib/acts_as_calculator/cast_variable_attributes.rb
|
|
178
|
+
- lib/acts_as_calculator/configuration.rb
|
|
179
|
+
- lib/acts_as_calculator/distribute_remainder.rb
|
|
180
|
+
- lib/acts_as_calculator/divide_proportionally.rb
|
|
181
|
+
- lib/acts_as_calculator/engine.rb
|
|
182
|
+
- lib/acts_as_calculator/errors.rb
|
|
183
|
+
- lib/acts_as_calculator/evaluate_expression.rb
|
|
184
|
+
- lib/acts_as_calculator/evaluate_formula.rb
|
|
185
|
+
- lib/acts_as_calculator/find_lookup_table_references.rb
|
|
186
|
+
- lib/acts_as_calculator/find_owned_record.rb
|
|
187
|
+
- lib/acts_as_calculator/find_tier.rb
|
|
188
|
+
- lib/acts_as_calculator/formula_version_drop.rb
|
|
189
|
+
- lib/acts_as_calculator/function_registry.rb
|
|
190
|
+
- lib/acts_as_calculator/import_definitions.rb
|
|
191
|
+
- lib/acts_as_calculator/import_formula.rb
|
|
192
|
+
- lib/acts_as_calculator/import_lookup_table.rb
|
|
193
|
+
- lib/acts_as_calculator/import_outcome.rb
|
|
194
|
+
- lib/acts_as_calculator/import_summary.rb
|
|
195
|
+
- lib/acts_as_calculator/import_template.rb
|
|
196
|
+
- lib/acts_as_calculator/liquid_filters.rb
|
|
197
|
+
- lib/acts_as_calculator/persist_run.rb
|
|
198
|
+
- lib/acts_as_calculator/promote_template.rb
|
|
199
|
+
- lib/acts_as_calculator/publish_formula_version.rb
|
|
200
|
+
- lib/acts_as_calculator/publish_template.rb
|
|
201
|
+
- lib/acts_as_calculator/read_import_file.rb
|
|
202
|
+
- lib/acts_as_calculator/render_liquid.rb
|
|
203
|
+
- lib/acts_as_calculator/render_template.rb
|
|
204
|
+
- lib/acts_as_calculator/resolve_formula_version.rb
|
|
205
|
+
- lib/acts_as_calculator/resolve_import_owner.rb
|
|
206
|
+
- lib/acts_as_calculator/resolve_template.rb
|
|
207
|
+
- lib/acts_as_calculator/resolve_variables.rb
|
|
208
|
+
- lib/acts_as_calculator/result.rb
|
|
209
|
+
- lib/acts_as_calculator/result_drop.rb
|
|
210
|
+
- lib/acts_as_calculator/serialize_formula.rb
|
|
211
|
+
- lib/acts_as_calculator/serialize_formula_version.rb
|
|
212
|
+
- lib/acts_as_calculator/serialize_import_summary.rb
|
|
213
|
+
- lib/acts_as_calculator/serialize_template.rb
|
|
214
|
+
- lib/acts_as_calculator/supersede_formula_versions.rb
|
|
215
|
+
- lib/acts_as_calculator/tier.rb
|
|
216
|
+
- lib/acts_as_calculator/variable_spec.rb
|
|
217
|
+
- lib/acts_as_calculator/version.rb
|
|
218
|
+
- lib/generators/acts_as_calculator/import/import_generator.rb
|
|
219
|
+
- lib/generators/acts_as_calculator/install/install_generator.rb
|
|
220
|
+
- lib/generators/acts_as_calculator/install/templates/create_acts_as_calculator_tables.rb.tt
|
|
221
|
+
- lib/tasks/acts_as_calculator.rake
|
|
222
|
+
- sig/acts_as_calculator.rbs
|
|
223
|
+
homepage: https://github.com/lautarograc/acts_as_calculator
|
|
224
|
+
licenses:
|
|
225
|
+
- MIT
|
|
226
|
+
metadata:
|
|
227
|
+
rubygems_mfa_required: 'true'
|
|
228
|
+
homepage_uri: https://github.com/lautarograc/acts_as_calculator
|
|
229
|
+
source_code_uri: https://github.com/lautarograc/acts_as_calculator
|
|
230
|
+
changelog_uri: https://github.com/lautarograc/acts_as_calculator/blob/main/CHANGELOG.md
|
|
231
|
+
bug_tracker_uri: https://github.com/lautarograc/acts_as_calculator/issues
|
|
232
|
+
post_install_message:
|
|
233
|
+
rdoc_options: []
|
|
234
|
+
require_paths:
|
|
235
|
+
- lib
|
|
236
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
237
|
+
requirements:
|
|
238
|
+
- - ">="
|
|
239
|
+
- !ruby/object:Gem::Version
|
|
240
|
+
version: 3.2.0
|
|
241
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
|
+
requirements:
|
|
243
|
+
- - ">="
|
|
244
|
+
- !ruby/object:Gem::Version
|
|
245
|
+
version: '0'
|
|
246
|
+
requirements: []
|
|
247
|
+
rubygems_version: 3.5.16
|
|
248
|
+
signing_key:
|
|
249
|
+
specification_version: 4
|
|
250
|
+
summary: A pricing and calculation engine on top of Dentaku.
|
|
251
|
+
test_files: []
|