lca 0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []