lca 0.2

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +115 -0
  5. data/LICENSE +21 -0
  6. data/README.md +110 -0
  7. data/Rakefile +8 -0
  8. data/bin/console +15 -0
  9. data/bin/setup +8 -0
  10. data/lib/generators/lca/install_generator.rb +26 -0
  11. data/lib/generators/lca/templates/config.yml.tt +14 -0
  12. data/lib/generators/lca/templates/initializer.rb.tt +16 -0
  13. data/lib/generators/lca/templates/migration.rb.tt +61 -0
  14. data/lib/lca/active_record.rb +6 -0
  15. data/lib/lca/builders/builder.rb +3 -0
  16. data/lib/lca/models/concerns/lcable.rb +84 -0
  17. data/lib/lca/models/concerns/statusable.rb +44 -0
  18. data/lib/lca/models/cycle/activity.rb +3 -0
  19. data/lib/lca/models/cycle/product.rb +6 -0
  20. data/lib/lca/models/cycle/service.rb +4 -0
  21. data/lib/lca/models/cycle.rb +13 -0
  22. data/lib/lca/models/exchange.rb +4 -0
  23. data/lib/lca/models/impact/ecosphere/fauna.rb +3 -0
  24. data/lib/lca/models/impact/ecosphere/flora.rb +3 -0
  25. data/lib/lca/models/impact/ecosphere.rb +3 -0
  26. data/lib/lca/models/impact/human_health/cancer.rb +3 -0
  27. data/lib/lca/models/impact/human_health.rb +3 -0
  28. data/lib/lca/models/impact/technosphere/resource_availability.rb +3 -0
  29. data/lib/lca/models/impact/technosphere.rb +3 -0
  30. data/lib/lca/models/impact.rb +8 -0
  31. data/lib/lca/models/model.rb +69 -0
  32. data/lib/lca/models/process/raw_resource/extraction.rb +3 -0
  33. data/lib/lca/models/process/raw_resource.rb +2 -0
  34. data/lib/lca/models/process/transport/by_air.rb +3 -0
  35. data/lib/lca/models/process/transport/by_pigeon.rb +2 -0
  36. data/lib/lca/models/process/transport/by_sea.rb +2 -0
  37. data/lib/lca/models/process/transport/by_truck.rb +2 -0
  38. data/lib/lca/models/process/transport.rb +2 -0
  39. data/lib/lca/models/process.rb +10 -0
  40. data/lib/lca/models/stage/end_of_life.rb +3 -0
  41. data/lib/lca/models/stage/manufacturing.rb +3 -0
  42. data/lib/lca/models/stage/retail.rb +3 -0
  43. data/lib/lca/models/stage/use.rb +3 -0
  44. data/lib/lca/models/stage.rb +6 -0
  45. data/lib/lca/queries/cycle/generic.rb +3 -0
  46. data/lib/lca/queries/impact/generic.rb +3 -0
  47. data/lib/lca/queries/process/generic.rb +3 -0
  48. data/lib/lca/queries/query.rb +8 -0
  49. data/lib/lca/version.rb +5 -0
  50. data/lib/lca.rb +75 -0
  51. metadata +166 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4ff437267879f30b1fdf073964e93eaf6be5c580a8baefa2fd308b1b7d38de1f
4
+ data.tar.gz: 6ffcf161a6bd74b41f3ca07ae7ce78e41e8e9fa68f1ff1c5350a1bab5193c9d0
5
+ SHA512:
6
+ metadata.gz: a2133b1c9680cb1cfb78b26335d449a86b04871fac6357f2a1741dc57434398c40156af1613a691485b168d0cfb62ee41f0c8299829b5594ba6eebfe218499c4
7
+ data.tar.gz: 65a355dd57bdaf6deffcdffbb567247b6d28a6ac1dd7100631205e4d1337cf123c110aeeb517372ade19f13f166c85c9ee837d25c7441a692942d988cd946b8e
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
8
+ gem "rspec", "~> 3.0"
9
+
10
+ gem "activerecord", "~> 6.0"
11
+ gem "activestorage", "~> 6.0"
12
+ gem "actiontext", "~> 6.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,115 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lca (0.1.18)
5
+ actiontext (~> 6.0)
6
+ activerecord (~> 6.0)
7
+ activestorage (~> 6.0)
8
+ jwt (~> 2.2.3)
9
+ pg_ltree (~> 1.1.8)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ actionpack (6.1.4.1)
15
+ actionview (= 6.1.4.1)
16
+ activesupport (= 6.1.4.1)
17
+ rack (~> 2.0, >= 2.0.9)
18
+ rack-test (>= 0.6.3)
19
+ rails-dom-testing (~> 2.0)
20
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
21
+ actiontext (6.1.4.1)
22
+ actionpack (= 6.1.4.1)
23
+ activerecord (= 6.1.4.1)
24
+ activestorage (= 6.1.4.1)
25
+ activesupport (= 6.1.4.1)
26
+ nokogiri (>= 1.8.5)
27
+ actionview (6.1.4.1)
28
+ activesupport (= 6.1.4.1)
29
+ builder (~> 3.1)
30
+ erubi (~> 1.4)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
33
+ activejob (6.1.4.1)
34
+ activesupport (= 6.1.4.1)
35
+ globalid (>= 0.3.6)
36
+ activemodel (6.1.4.1)
37
+ activesupport (= 6.1.4.1)
38
+ activerecord (6.1.4.1)
39
+ activemodel (= 6.1.4.1)
40
+ activesupport (= 6.1.4.1)
41
+ activestorage (6.1.4.1)
42
+ actionpack (= 6.1.4.1)
43
+ activejob (= 6.1.4.1)
44
+ activerecord (= 6.1.4.1)
45
+ activesupport (= 6.1.4.1)
46
+ marcel (~> 1.0.0)
47
+ mini_mime (>= 1.1.0)
48
+ activesupport (6.1.4.1)
49
+ concurrent-ruby (~> 1.0, >= 1.0.2)
50
+ i18n (>= 1.6, < 2)
51
+ minitest (>= 5.1)
52
+ tzinfo (~> 2.0)
53
+ zeitwerk (~> 2.3)
54
+ builder (3.2.4)
55
+ concurrent-ruby (1.1.9)
56
+ crass (1.0.6)
57
+ diff-lcs (1.4.4)
58
+ erubi (1.10.0)
59
+ globalid (0.5.2)
60
+ activesupport (>= 5.0)
61
+ i18n (1.8.10)
62
+ concurrent-ruby (~> 1.0)
63
+ jwt (2.2.3)
64
+ loofah (2.12.0)
65
+ crass (~> 1.0.2)
66
+ nokogiri (>= 1.5.9)
67
+ marcel (1.0.1)
68
+ mini_mime (1.1.1)
69
+ minitest (5.14.4)
70
+ nokogiri (1.12.4-x86_64-linux)
71
+ racc (~> 1.4)
72
+ pg (1.2.3)
73
+ pg_ltree (1.1.8)
74
+ activerecord (>= 4.0.0, <= 7.0.0.rc1)
75
+ pg (>= 0.17.0, < 2)
76
+ racc (1.5.2)
77
+ rack (2.2.3)
78
+ rack-test (1.1.0)
79
+ rack (>= 1.0, < 3)
80
+ rails-dom-testing (2.0.3)
81
+ activesupport (>= 4.2.0)
82
+ nokogiri (>= 1.6)
83
+ rails-html-sanitizer (1.4.2)
84
+ loofah (~> 2.3)
85
+ rake (13.0.6)
86
+ rspec (3.10.0)
87
+ rspec-core (~> 3.10.0)
88
+ rspec-expectations (~> 3.10.0)
89
+ rspec-mocks (~> 3.10.0)
90
+ rspec-core (3.10.1)
91
+ rspec-support (~> 3.10.0)
92
+ rspec-expectations (3.10.1)
93
+ diff-lcs (>= 1.2.0, < 2.0)
94
+ rspec-support (~> 3.10.0)
95
+ rspec-mocks (3.10.2)
96
+ diff-lcs (>= 1.2.0, < 2.0)
97
+ rspec-support (~> 3.10.0)
98
+ rspec-support (3.10.2)
99
+ tzinfo (2.0.4)
100
+ concurrent-ruby (~> 1.0)
101
+ zeitwerk (2.4.2)
102
+
103
+ PLATFORMS
104
+ x86_64-linux
105
+
106
+ DEPENDENCIES
107
+ actiontext (~> 6.0)
108
+ activerecord (~> 6.0)
109
+ activestorage (~> 6.0)
110
+ lca!
111
+ rake (~> 13.0)
112
+ rspec (~> 3.0)
113
+
114
+ BUNDLED WITH
115
+ 2.2.27
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 CheckThisOut
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # LCA
2
+
3
+ Storing, processing and working with life-cycle assessment data has always been challenging. A multitude of data models and implementations exist already but every one of them makes huge compromises or lacks functionality.
4
+
5
+ This gem implements a denormalized database model for life-cycle assessment data as well as several innovative query interfaces.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'lca'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install lca
22
+
23
+ Upon installing the gem you must run the install process in your Rails application's root:
24
+
25
+ $ rails g lca:install
26
+
27
+ This will generate a `config/lca.yml` which you may customize to your needs, an initializer and a migration file which you can also customize to add any database columns your models will require.
28
+
29
+ ## Usage
30
+
31
+ Include the LCA concern into one of your models which will own lifecycle trees (owner model):
32
+
33
+ ```ruby
34
+ class User < ApplicationRecord
35
+ include Lca::Lcable
36
+ # ...
37
+ end
38
+ ```
39
+
40
+ This will extend your model and enable the following functionality:
41
+
42
+ ```ruby
43
+ # query with ActiveRecord syntax
44
+ User.last.lca_cycles
45
+ User.find_by(name: "Acme").lca_cycles.impacts.select("impact_unit, sum(impact_amount) as total_impact").group(:impact_unit)
46
+ User.last.lca_cycles.where(...)
47
+ User.last.lca_cycles.where(...).group(...)
48
+ User.last.lca_cycles.where(...).limit(...).offset(...)
49
+
50
+ # AR query with ltree syntax
51
+ User.last.lca_cycles.disabled.match_path("*.CustomProcess.*")
52
+ User.last.lca_cycles.match_path("*{5,10}.CustomProcess.*.Ecosphere.*")
53
+ User.last.lca_cycles.active.match_path("*.WhateverProcess.*.Ecosphere.*.CO2Emission.*").where( impact_amount: [100..150]).sum(:impact_amount)
54
+
55
+ Lca::Cycle.match_path("Top.Electric Vehicle.*").impacts.match_path("*.CO2*").where( impact_amount: [ 1000..10000 ]).average(:impact_amount)
56
+ Lca::Cycle::Product.where(name: "Electric Vehicle Battery").impacts.match_path("*.Cobalt.*").sum(:impact_amount)
57
+ Lca::Process.where(owner: User.last).match_path("*{10,20}.*Assembly, automated.*")
58
+ Lca::Impact.match_path("*.Transport by truck.*")
59
+ Lca::Exchange.match_path("*.Oil.*.Ecosphere.*").impacts.sum(:impact_amount)
60
+ Lca::Product.match_path("*.ElectricVehicle.*").processes.match_path("*.Processing.*").where(location: "EU").impacts.match_path("*.Lithium.*").where(location: ["CN", "Africa"]).sum(:impact_amount)
61
+
62
+ # pg_ltree queries
63
+ User.last.lca_cycles.last.parent
64
+ Lca::Process.match_path("*.Manual assembly.*").children
65
+
66
+ # pg_ltree combined with AR syntax
67
+ User.last.lca_cycles(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
68
+
69
+ # all queries can be directed to a specific partition:
70
+ Lca::Process.owned_by( owner_id ).match_path("*.Recycling.*").where(impact_unit: "tons CO2/year").impacts.sum(:impact_amount)
71
+
72
+ ```
73
+
74
+ The gem also creates some default models:
75
+
76
+ ```ruby
77
+ Lca::Process::Transport::ByAir.match_path("*.CO2Emission.*").impacts.sum(:impact_amount)
78
+ Lca::Impact::Ecosphere::Fauna.match_path("*.ResourceExtraction.*").where(impact_unit: "Species killed/year").sum(:impact_amount)
79
+ ```
80
+
81
+ To see what syntax to use for path traversal please check out the following resources:
82
+ * pg_ltree gem https://github.com/sjke/pg_ltree
83
+ * Postgres ltree extension documentation https://www.postgresql.org/docs/9.1/ltree.html
84
+
85
+ The LCA gem is designed to be compatible with PostgREST. PostgREST is an amazing tool that generates a CRUD REST API for your Postgres database, read more about it here: https://postgrest.org
86
+
87
+ If the `create_postgrest_roles` setting is on each new owner will be assigned a Postgres role allowing them to access data within their partition using PostgREST. Your owner model will be extended with a `.generate_jwt` method you can use to generate the PostgREST authentication token.
88
+
89
+ ## Roadmap
90
+
91
+ * more models for various LCA cycles, processes and impacts
92
+ * model validations
93
+ * query objects, including postgres RECURSIVE queries
94
+ * builders
95
+ * seeds/fixtures
96
+ * automated tests
97
+
98
+
99
+ ## Contributing
100
+
101
+ Bug reports, pull requests and feature suggestions are welcome on GitHub at https://github.com/nicksterious/lca
102
+
103
+ ## Sponsors
104
+
105
+ We welcome any sponsorship through Github Sponsors. All corporate sponsors' logo and URL of choice shall show up within this section.
106
+
107
+ ## License
108
+
109
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
110
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "lca"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ require "rails/generators/active_record"
2
+
3
+ module Lca
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.join(__dir__, "templates")
8
+
9
+ def copy_migration
10
+ migration_template "migration.rb.tt", "db/migrate/install_lca.rb", migration_version: migration_version
11
+ end
12
+
13
+ def copy_initializer
14
+ copy_file 'initializer.rb.tt', 'config/initializers/lca.rb'
15
+ end
16
+
17
+ def copy_config
18
+ copy_file "config.yml.tt", "config/lca.yml"
19
+ end
20
+
21
+ def migration_version
22
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # the main table will be called "lca_models"
2
+ # partitions will be called "lca_models_X" where X is the owner object ID
3
+ table_name: "lca_models"
4
+
5
+ # create PG roles for postgrest: anon and owner-specific roles
6
+ create_postgrest_roles: true
7
+
8
+ # on owner removal, detach the partition but preserve the table and data, or destroy the partition and data
9
+ # if you choose to detach and preserve, in order to avoid table name collisions, make sure owner IDs are not reused
10
+ destroy_partition_on_owner_destroy: true
11
+
12
+ # jwt secret required for postgrest role switching
13
+ jwt_secret: "supersecret"
14
+ jwt_encryption: "HS256"
@@ -0,0 +1,16 @@
1
+ LCA_OPTIONS ||= begin
2
+ path = Rails.root.join("config", "lca.yml").to_s
3
+ if File.exist?(path)
4
+ YAML.load( ERB.new(File.read(path)).result ).deep_symbolize_keys
5
+ else
6
+ {
7
+ table_name: "lca_models",
8
+ create_postgrest_roles: true,
9
+ jwt_secret: "supersecret",
10
+ jwt_encryption: "HS256",
11
+ destroy_partition_on_owner_destroy: true
12
+ }
13
+ end
14
+ end.merge({
15
+ database_user: Rails.application.config.database_configuration[ Rails.env ].deep_symbolize_keys[:username]
16
+ })
@@ -0,0 +1,61 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+
4
+ # enable ltree extension
5
+ begin
6
+ execute "create extension ltree"
7
+ rescue
8
+ p "LTREE was already enabled"
9
+ end
10
+
11
+ # LCA_OPTIONS[:table_name]
12
+ execute <<-SQL
13
+ create table #{ LCA_OPTIONS[:table_name] } (
14
+ id serial,
15
+
16
+ owner_id integer,
17
+ owner_type text,
18
+
19
+ data_external_id text,
20
+ data_provider text,
21
+
22
+ type text,
23
+ name text,
24
+
25
+ parent_id integer,
26
+ parent_type text,
27
+
28
+ path ltree,
29
+ path_slug text,
30
+
31
+ impact_amount decimal,
32
+ impact_amount_unit text,
33
+ impact_factor decimal,
34
+ impact_precision decimal,
35
+
36
+ primary key (id, owner_id)
37
+ ) partition by list(owner_id)
38
+ SQL
39
+
40
+ # TODO do we want to also create an "others" partition?
41
+ # "CREATE TABLE #{LCA_OPTIONS[:table_name]}_others PARTITION OF #{LCA_OPTIONS[:table_name]} DEFAULT"
42
+
43
+ # add indexes
44
+ add_index LCA_OPTIONS[:table_name], :id
45
+ add_index LCA_OPTIONS[:table_name], :owner_id
46
+ add_index LCA_OPTIONS[:table_name], :type
47
+ add_index LCA_OPTIONS[:table_name], :parent_id
48
+
49
+ # next two indexes unfortunately can't be unique since a cycle can appear several times under an owner
50
+ add_index LCA_OPTIONS[:table_name], :path, using: :gist
51
+ add_index LCA_OPTIONS[:table_name], [:data_provider, :data_external_id]
52
+
53
+ if LCA_OPTIONS[:create_postgrest_roles]
54
+ # create postgrest anon user with no privs
55
+ # postgrest may pass an user's role using JWT
56
+ execute "drop role if exists postgrest_anon"
57
+ execute "create role postgrest_anon nologin"
58
+ execute "grant postgrest_anon to #{ LCA_OPTIONS[:database_user] }"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,6 @@
1
+ # this still needed?
2
+ # we include the concern manually so there should be no need for this anymore
3
+ ActiveSupport.on_load(:active_record) do
4
+ puts "tf is AR still being extended?"
5
+ extend Lca::Lcable
6
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Builder
2
+
3
+ end
@@ -0,0 +1,84 @@
1
+ require "jwt"
2
+
3
+ module Lca
4
+ module Lcable
5
+ extend ActiveSupport::Concern
6
+
7
+ # def self.included(base)
8
+ # ::Lca::Model.class_exec do
9
+ # @@owner_class = base.name
10
+ # end
11
+ # end
12
+
13
+ included do
14
+
15
+ has_many :lca_cycles, class_name: "::Lca::Cycle", foreign_key: :owner_id, as: :owner
16
+ after_create :lca_create_storage
17
+ before_destroy :lca_delete_storage
18
+
19
+ # instance methods
20
+ def lca_role
21
+ "lca_owner_#{id}"
22
+ end # role
23
+
24
+ # Generates a JWT token the client (SPA) can pass to PostgREST for privilege escalation
25
+ def generate_jwt
26
+ payload = { role: self.lca_role }
27
+ ::JWT.encode payload, LCA_OPTIONS[:jwt_secret], LCA_OPTIONS[:jwt_encryption]
28
+ end # .generate_jwt
29
+
30
+
31
+ # Returns the LCA table name as configured within config/lca.yml
32
+ def lca_table_name
33
+ LCA_OPTIONS[:table_name]
34
+ end # .lca_table_name
35
+
36
+
37
+ # Creates LCA table partition and role for owner
38
+ def lca_create_storage
39
+
40
+ # create data partition
41
+ lca_sql "create table if not exists #{lca_table_name}_#{id} partition of #{lca_table_name} for values in (#{id})"
42
+ # TODO create partition indexes
43
+
44
+ if LCA_OPTIONS[:create_postgrest_roles]
45
+ # drop role if it exists
46
+ lca_sql "drop role if exists lca_owner_#{id}"
47
+
48
+ # create role
49
+ lca_sql "create role lca_owner_#{id}"
50
+
51
+ # grant privs
52
+ lca_sql "grant all privileges on #{lca_table_name}_#{id} to lca_owner_#{id}"
53
+ end
54
+
55
+ end # create_storage
56
+
57
+
58
+ # Deletes or detaches the partition and removes the role for this owner
59
+ def lca_delete_storage
60
+
61
+ if LCA_OPTIONS[:create_postgrest_roles]
62
+ # revoke privs
63
+ lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_#{id} FROM lca_owner_#{id}"
64
+
65
+ # delete role
66
+ lca_sql "drop role lca_owner_#{id}"
67
+ end
68
+
69
+ if LCA_OPTIONS[:destroy_partition_on_owner_destroy]
70
+ # delete partition
71
+ lca_sql "drop table if exists #{lca_table_name}_#{id}"
72
+ else
73
+ # detach and forget about it
74
+ lca_sql "alter table #{lca_table_name} detach partition lca_owner_#{id}"
75
+ end
76
+ end # delete_storage
77
+
78
+ def lca_sql sql
79
+ ActiveRecord::Base.connection.execute sql
80
+ end # lca_sql
81
+
82
+ end # ClassMethods
83
+ end
84
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ module Lca::Statusable
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ scope :active, -> { where(status: 1) }
8
+ scope :inactive, -> { where(status: 0) }
9
+ alias_method :enabled?, :active?
10
+ alias_method :enable!, :active!
11
+ alias_method :on!, :active!
12
+ alias_method :disabled?, :inactive?
13
+ alias_method :disable!, :inactive!
14
+ alias_method :off!, :inactive!
15
+ alias_method :toggle?, :toggle_status!
16
+ end
17
+
18
+
19
+ def toggle_status!
20
+ if active?
21
+ inactive!
22
+ else
23
+ active!
24
+ end
25
+ end
26
+
27
+ def status?
28
+ [:inactive, :active][ status ]
29
+ end
30
+
31
+ def active?
32
+ status == 1
33
+ end
34
+ def inactive?
35
+ status == 0
36
+ end
37
+
38
+ def active!
39
+ self.update(status: 1)
40
+ end
41
+ def inactive!
42
+ self.update(status: 0)
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Cycle::Activity < Lca::Cycle
2
+
3
+ end
@@ -0,0 +1,6 @@
1
+ class Lca::Cycle::Product < Lca::Cycle
2
+ # price?
3
+ # quantity?
4
+ # life time?
5
+ # life time unit?
6
+ end
@@ -0,0 +1,4 @@
1
+ class Lca::Cycle::Service < Lca::Cycle
2
+ # duration?
3
+ # price?
4
+ end
@@ -0,0 +1,13 @@
1
+ class Lca::Cycle < Lca::Model
2
+
3
+ has_many :stages, class_name: "::Lca::Stage", foreign_key: :parent_id, dependent: :destroy
4
+
5
+ def subcycles
6
+ ::Lca::Cycle.match_path("#{path}.*")
7
+ end
8
+
9
+ def processes
10
+ ::Lca::Process.match_path("#{path}.*")
11
+ end
12
+
13
+ end
@@ -0,0 +1,4 @@
1
+ class Lca::Exchange < Lca::Model
2
+ belongs_to :process, class_name: "::Lca::Process", foreign_key: :parent_id
3
+ has_many :impacts, class_name: "::Lca::Impact", foreign_key: :parent_id, dependent: :destroy
4
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::Ecosphere::Fauna < Lca::Impact::Ecosphere
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::Ecosphere::Flora < Lca::Impact::Ecosphere
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::Ecosphere < Lca::Impact
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::HumanHealth::Cancer < Lca::Impact::HumanHealth
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::HumanHealth < Lca::Impact
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::Technosphere::ResourceAvailability < Lca::Impact::Technosphere
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Impact::Technosphere < Lca::Impact
2
+
3
+ end
@@ -0,0 +1,8 @@
1
+ class Lca::Impact < Lca::Model
2
+ belongs_to :exchange, class_name: "::Lca::Exchange", foreign_key: :parent_id
3
+
4
+ validates_presence_of :impact_amount
5
+ validates_presence_of :impact_amount_unit, allow_blank: false
6
+ validates_presence_of :impact_factor
7
+ validates_presence_of :impact_precision
8
+ end
@@ -0,0 +1,69 @@
1
+ class Lca::Model < ActiveRecord::Base
2
+
3
+ include Lca::Statusable
4
+
5
+ self.primary_key = :id
6
+
7
+ def self.table_name
8
+ return ::LCA_OPTIONS[:table_name] if defined? ::LCA_OPTIONS
9
+ return "lca_models"
10
+ end
11
+
12
+ belongs_to :owner, polymorphic: :true
13
+
14
+ scope :match_path, -> (some_path) { where("path ~ ?", "#{some_path}") }
15
+
16
+ #has_one_attached :picture
17
+ #has_rich_text :description
18
+
19
+ validates_presence_of :name, allow_blank: false
20
+ validates_presence_of :path, allow_blank: false
21
+
22
+ before_save :set_defaults
23
+ def set_defaults
24
+ self.path ||= name.parameterize.gsub("-", ".")
25
+ self.path_slug = path.parameterize
26
+ end
27
+
28
+
29
+ # Scoping by owner in order to select the partition
30
+ #
31
+ # @param owner_id [Integer] the partition owner
32
+ def self.owned_by(owner_id)
33
+ # if we're looking for anything else but an integer, revert to the base class
34
+ return self if !owner_id.is_a? Integer
35
+
36
+ partition_suffix = "_#{owner_id}"
37
+
38
+ table = "#{ self.table_name }#{ partition_suffix }"
39
+
40
+ ApplicationRecord.connection.schema_cache.clear!
41
+ return self if !ApplicationRecord.connection.schema_cache.data_source_exists? table
42
+
43
+ # duplicate the class
44
+ model_class = Class.new self
45
+ original_class_name = self.name
46
+
47
+ # ...for this owner
48
+ class_name = "#{name}#{partition_suffix}"
49
+
50
+ # specify the table
51
+ model_class.define_singleton_method(:table_name) do
52
+ table
53
+ end
54
+
55
+ # specify the name
56
+ model_class.define_singleton_method(:name) do
57
+ class_name
58
+ end
59
+
60
+ # override the STI name lmfao
61
+ model_class.define_singleton_method(:find_sti_class) do |p|
62
+ original_class_name.constantize
63
+ end
64
+
65
+ # proceed
66
+ model_class
67
+ end # .owned_by
68
+
69
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Process::RawResource::Extraction < Lca::Process::RawResource
2
+
3
+ end
@@ -0,0 +1,2 @@
1
+ class Lca::Process::RawResource < Lca::Process
2
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Process::Transport::ByAir < Lca::Process::Transport
2
+
3
+ end
@@ -0,0 +1,2 @@
1
+ class Lca::Process::Transport::ByPigeon < Lca::Process::Transport
2
+ end
@@ -0,0 +1,2 @@
1
+ class Lca::Process::Transport::BySea < Lca::Process::Transport
2
+ end
@@ -0,0 +1,2 @@
1
+ class Lca::Process::Transport::ByTruck < Lca::Process::Transport
2
+ end
@@ -0,0 +1,2 @@
1
+ class Lca::Process::Transport < Lca::Process
2
+ end
@@ -0,0 +1,10 @@
1
+ class Lca::Process < Lca::Model
2
+
3
+ has_many :exchanges, class_name: "::Lca::Exchange", foreign_key: :parent_id, dependent: :destroy
4
+ has_many :impacts, through: :exchanges
5
+ belongs_to :stage, class_name: "::Lca::Stage", foreign_key: :parent_id
6
+
7
+ def subprocesses
8
+ ::Lca::Process.where("path ~ ?", "#{path}.*")
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Stage::EndOfLife < Lca::Stage
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Stage::Manufacturing < Lca::Stage
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Stage::Retail < Lca::Stage
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Stage::Use < Lca::Stage
2
+
3
+ end
@@ -0,0 +1,6 @@
1
+ class Lca::Stage < Lca::Model
2
+
3
+ belongs_to :cycle, class_name: "::Lca::Cycle", foreign_key: :parent_id
4
+ has_many :processes, class_name: "::Lca::Process", foreign_key: :parent_id
5
+
6
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Queries::Cycle::Generic < Lca::Queries::Query
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Queries::Impact::Generic < Lca::Queries::Query
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class Lca::Queries::Process::Generic < Lca::Queries::Query
2
+
3
+ end
@@ -0,0 +1,8 @@
1
+ class Lca::Queries::Query
2
+
3
+ def valid_input?(v)
4
+ return false if [ [], "", "0", 0, nil, [""], [0], ["0"], [nil], "all" ].include?(v)
5
+ return true
6
+ end
7
+
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lca
4
+ VERSION = "0.2"
5
+ end
data/lib/lca.rb ADDED
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+ require "active_support/all"
3
+
4
+ require "active_record"
5
+
6
+ require_relative "lca/version"
7
+
8
+ require_relative "lca/models/concerns/statusable"
9
+ require_relative "lca/models/model"
10
+ require_relative "lca/models/cycle"
11
+ require_relative "lca/models/stage"
12
+ require_relative "lca/models/process"
13
+ require_relative "lca/models/exchange"
14
+ require_relative "lca/models/impact"
15
+ require_relative "lca/models/cycle/product"
16
+ require_relative "lca/models/cycle/service"
17
+ require_relative "lca/models/cycle/activity"
18
+ require_relative "lca/models/stage/manufacturing"
19
+ require_relative "lca/models/stage/retail"
20
+ require_relative "lca/models/stage/use"
21
+ require_relative "lca/models/stage/end_of_life"
22
+ require_relative "lca/models/process/raw_resource"
23
+ require_relative "lca/models/process/raw_resource/extraction"
24
+ require_relative "lca/models/process/transport"
25
+ require_relative "lca/models/process/transport/by_air"
26
+ require_relative "lca/models/process/transport/by_sea"
27
+ require_relative "lca/models/process/transport/by_pigeon"
28
+ require_relative "lca/models/process/transport/by_truck"
29
+ require_relative "lca/models/exchange"
30
+ require_relative "lca/models/impact/ecosphere"
31
+ require_relative "lca/models/impact/human_health"
32
+ require_relative "lca/models/impact/technosphere"
33
+ require_relative "lca/models/impact/ecosphere/fauna"
34
+ require_relative "lca/models/impact/ecosphere/flora"
35
+ require_relative "lca/models/impact/human_health/cancer"
36
+ require_relative "lca/models/impact/technosphere/resource_availability"
37
+
38
+ require_relative "lca/models/concerns/lcable"
39
+ require_relative "lca/active_record"
40
+
41
+ # TODO add query objects and builders
42
+ #require "lca/queries/"
43
+ #require "lca/builders/"
44
+
45
+ module Lca
46
+ class Error < StandardError; end
47
+
48
+ # Your code goes here...
49
+
50
+ class << self
51
+ attr_accessor :lca_models
52
+ attr_accessor :options
53
+ end
54
+
55
+ self.lca_models = []
56
+
57
+ def self.lca_options
58
+ @options ||= begin
59
+ path = Rails.root.join("config", "lca.yml").to_s
60
+ if File.exist?(path)
61
+ YAML.load(ERB.new(File.read(path)).result)
62
+ else
63
+ {
64
+ table_name: "lca_models",
65
+ jwt_secret: "",
66
+ jwt_encryption: "HS256"
67
+ }
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.env
73
+ @env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lca
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Nick @ Earthster
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-09-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activestorage
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actiontext
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '6.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '6.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pg_ltree
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.8
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.1.8
69
+ - !ruby/object:Gem::Dependency
70
+ name: jwt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.2.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.2.3
83
+ description: This gem allows storing and querying lifecycle assessment data in a meaningful,
84
+ comprehensive way using a Postgres ltree structure
85
+ email:
86
+ - nick@earthster.org
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".rspec"
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/generators/lca/install_generator.rb
100
+ - lib/generators/lca/templates/config.yml.tt
101
+ - lib/generators/lca/templates/initializer.rb.tt
102
+ - lib/generators/lca/templates/migration.rb.tt
103
+ - lib/lca.rb
104
+ - lib/lca/active_record.rb
105
+ - lib/lca/builders/builder.rb
106
+ - lib/lca/models/concerns/lcable.rb
107
+ - lib/lca/models/concerns/statusable.rb
108
+ - lib/lca/models/cycle.rb
109
+ - lib/lca/models/cycle/activity.rb
110
+ - lib/lca/models/cycle/product.rb
111
+ - lib/lca/models/cycle/service.rb
112
+ - lib/lca/models/exchange.rb
113
+ - lib/lca/models/impact.rb
114
+ - lib/lca/models/impact/ecosphere.rb
115
+ - lib/lca/models/impact/ecosphere/fauna.rb
116
+ - lib/lca/models/impact/ecosphere/flora.rb
117
+ - lib/lca/models/impact/human_health.rb
118
+ - lib/lca/models/impact/human_health/cancer.rb
119
+ - lib/lca/models/impact/technosphere.rb
120
+ - lib/lca/models/impact/technosphere/resource_availability.rb
121
+ - lib/lca/models/model.rb
122
+ - lib/lca/models/process.rb
123
+ - lib/lca/models/process/raw_resource.rb
124
+ - lib/lca/models/process/raw_resource/extraction.rb
125
+ - lib/lca/models/process/transport.rb
126
+ - lib/lca/models/process/transport/by_air.rb
127
+ - lib/lca/models/process/transport/by_pigeon.rb
128
+ - lib/lca/models/process/transport/by_sea.rb
129
+ - lib/lca/models/process/transport/by_truck.rb
130
+ - lib/lca/models/stage.rb
131
+ - lib/lca/models/stage/end_of_life.rb
132
+ - lib/lca/models/stage/manufacturing.rb
133
+ - lib/lca/models/stage/retail.rb
134
+ - lib/lca/models/stage/use.rb
135
+ - lib/lca/queries/cycle/generic.rb
136
+ - lib/lca/queries/impact/generic.rb
137
+ - lib/lca/queries/process/generic.rb
138
+ - lib/lca/queries/query.rb
139
+ - lib/lca/version.rb
140
+ homepage: https://github.com/nicksterious/lca
141
+ licenses:
142
+ - MIT
143
+ metadata:
144
+ homepage_uri: https://github.com/nicksterious/lca
145
+ source_code_uri: https://github.com/nicksterious/lca
146
+ changelog_uri: https://github.com/nicksterious/lca/blob/master/CHANGELOG.md
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 2.4.0
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubygems_version: 3.0.3.1
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: A storage backend for LCA data
166
+ test_files: []