lca 0.2.4 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc53882ad2b8c5c34b869be0546546973dd829b25aa39f354e9be9da63a44fae
4
- data.tar.gz: 57c65eeadf05e7e6f160ea1c2ca3db8a025614578ecb6242d9f1404002651766
3
+ metadata.gz: ae08a160b647985c85ef3181992f091ac978411c2409bc23de69daf97a95e1a3
4
+ data.tar.gz: 735cbda1c8bbde69c6b015d5d09f97fee07d7830de21978c40086910810e7d1f
5
5
  SHA512:
6
- metadata.gz: 2a1542ddf863b9461fb3ceb67e45f59599608c40e98994203918b7aeb4fb87ef2e4b1f73bd7fa3a837a0cd9cbd691cf72648505f38f156c64b84b6ee097d0dd6
7
- data.tar.gz: e7989620c9b0a81249e741b41bcbe8fd1854b24650d66e4963ce940ab593d8ea2340403f06f467d8e280cea3dfed99d1c5067407771557403bf2d049cf792b0c
6
+ metadata.gz: 794b1b5deb3ed8ed9e44d0bf0252ddb7144e9dab733ec0ae6c28bb39d933317b1c28463c74d4a556dda9d91fbcc77ae1466a020bda7ba09aa15eb96a3336a451
7
+ data.tar.gz: b7aa19cf60dfe5c510b206a9e00f4afbaee46168fe1e82860d3c82b39e966dc02613e4a49c9a2f2356a19c432e01eedd99ed82fa054ec5c771bc1402426a3725
data/Gemfile CHANGED
@@ -8,8 +8,6 @@ gem "rake", "~> 13.0"
8
8
  gem "rspec", "~> 3.0"
9
9
 
10
10
  gem "activerecord", "~> 6.0"
11
- gem "activestorage", "~> 6.0"
12
- gem "actiontext", "~> 6.0"
13
-
11
+ gem "active_tree", "~> 0.2.8"
14
12
  gem "jwt"
15
13
  gem "pg_ltree"
data/Gemfile.lock CHANGED
@@ -1,87 +1,40 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lca (0.1.18)
5
- actiontext (~> 6.0)
4
+ lca (0.2.8)
5
+ active_tree (~> 0.2.8)
6
6
  activerecord (~> 6.0)
7
- activestorage (~> 6.0)
8
7
  jwt (~> 2.2.3)
9
8
  pg_ltree (~> 1.1.8)
10
9
 
11
10
  GEM
12
11
  remote: https://rubygems.org/
13
12
  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)
13
+ active_tree (0.2.9)
14
+ activerecord (~> 6.0)
15
+ jwt (~> 2.2.3)
16
+ pg_ltree (~> 1.1.8)
36
17
  activemodel (6.1.4.1)
37
18
  activesupport (= 6.1.4.1)
38
19
  activerecord (6.1.4.1)
39
20
  activemodel (= 6.1.4.1)
40
21
  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
22
  activesupport (6.1.4.1)
49
23
  concurrent-ruby (~> 1.0, >= 1.0.2)
50
24
  i18n (>= 1.6, < 2)
51
25
  minitest (>= 5.1)
52
26
  tzinfo (~> 2.0)
53
27
  zeitwerk (~> 2.3)
54
- builder (3.2.4)
55
28
  concurrent-ruby (1.1.9)
56
- crass (1.0.6)
57
29
  diff-lcs (1.4.4)
58
- erubi (1.10.0)
59
- globalid (0.5.2)
60
- activesupport (>= 5.0)
61
30
  i18n (1.8.10)
62
31
  concurrent-ruby (~> 1.0)
63
32
  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
33
  minitest (5.14.4)
70
- nokogiri (1.12.4-x86_64-linux)
71
- racc (~> 1.4)
72
34
  pg (1.2.3)
73
35
  pg_ltree (1.1.8)
74
36
  activerecord (>= 4.0.0, <= 7.0.0.rc1)
75
37
  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
38
  rake (13.0.6)
86
39
  rspec (3.10.0)
87
40
  rspec-core (~> 3.10.0)
@@ -104,10 +57,11 @@ PLATFORMS
104
57
  x86_64-linux
105
58
 
106
59
  DEPENDENCIES
107
- actiontext (~> 6.0)
60
+ active_tree (~> 0.2.8)
108
61
  activerecord (~> 6.0)
109
- activestorage (~> 6.0)
62
+ jwt
110
63
  lca!
64
+ pg_ltree
111
65
  rake (~> 13.0)
112
66
  rspec (~> 3.0)
113
67
 
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
- [![Gem Version](https://badge.fury.io/rb/lca.svg)](https://badge.fury.io/rb/lca)
1
+ [![Tests](https://github.com/nicksterious/lca/actions/workflows/ci.yml/badge.svg)](https://github.com/nicksterious/lca/actions/workflows/ci.yml) [![Ruby Gem](https://github.com/nicksterious/lca/actions/workflows/rubygems.yml/badge.svg)](https://github.com/nicksterious/lca/actions/workflows/rubygems.yml) [![Gem Version](https://badge.fury.io/rb/lca.svg)](https://badge.fury.io/rb/lca)
2
2
 
3
3
  # LCA
4
4
 
5
5
  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.
6
6
 
7
- This gem implements a denormalized database model for life-cycle assessment data as well as several innovative query interfaces.
7
+ This gem implements a denormalized database model for life-cycle assessment data as well as several vectors for convenient querying.
8
8
 
9
9
  ## Installation
10
10
 
11
+ This gem is at home within a Rails 6+ console-only, API or full fledged application on top of a Postgres database.
12
+
11
13
  Add this line to your application's Gemfile:
12
14
 
13
15
  ```ruby
@@ -22,15 +24,15 @@ Or install it yourself as:
22
24
 
23
25
  $ gem install lca
24
26
 
25
- Upon installing the gem you must run the install process in your Rails application's root:
27
+ Upon installing the gem you must run the ActiveTree install process in your Rails application's root:
26
28
 
27
- $ rails g lca:install
29
+ $ rails g active_tree:install
28
30
 
29
- 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.
31
+ This will generate a `config/active_tree.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.
30
32
 
31
33
  ## Usage
32
34
 
33
- Include the LCA concern into one of your models which will own lifecycle trees (owner model):
35
+ Include the ActiveTree concern into one of your models which will own lifecycle trees (owner model):
34
36
 
35
37
  ```ruby
36
38
  class User < ApplicationRecord
@@ -42,58 +44,69 @@ Include the LCA concern into one of your models which will own lifecycle trees (
42
44
  This will extend your model and enable the following functionality:
43
45
 
44
46
  ```ruby
45
- # query with ActiveRecord syntax
46
- User.last.lca_cycles
47
- User.find_by(name: "Acme").lca_cycles.impacts.select("impact_unit, sum(impact_amount) as total_impact").group(:impact_unit)
48
- User.last.lca_cycles.where(...)
49
- User.last.lca_cycles.where(...).group(...)
50
- User.last.lca_cycles.where(...).limit(...).offset(...)
47
+ # query with ActiveRecord syntax
48
+ User.last.active_trees
49
+ User.find_by(name: "Acme").lca_cycles.impacts.select("impact_unit, sum(impact_amount) as total_impact").group(:impact_unit)
50
+ User.last.active_trees.where(...)
51
+ User.last.active_trees.where(...).group(...)
52
+ User.last.active_trees.where(...).limit(...).offset(...)
51
53
 
52
- # AR query with ltree syntax
53
- User.last.lca_cycles.disabled.match_path("*.CustomProcess.*")
54
- User.last.lca_cycles.match_path("*{5,10}.CustomProcess.*.Ecosphere.*")
55
- User.last.lca_cycles.active.match_path("*.WhateverProcess.*.Ecosphere.*.CO2Emission.*").where( impact_amount: [100..150]).sum(:impact_amount)
56
-
57
- Lca::Cycle.match_path("Top.Electric Vehicle.*").impacts.match_path("*.CO2*").where( impact_amount: [ 1000..10000 ]).average(:impact_amount)
58
- Lca::Cycle::Product.where(name: "Electric Vehicle Battery").impacts.match_path("*.Cobalt.*").sum(:impact_amount)
59
- Lca::Process.where(owner: User.last).match_path("*{10,20}.*Assembly, automated.*")
60
- Lca::Impact.match_path("*.Transport by truck.*")
61
- Lca::Exchange.match_path("*.Oil.*.Ecosphere.*").impacts.sum(:impact_amount)
62
- Lca::Product.match_path("*.ElectricVehicle.*").processes.match_path("*.Processing.*").where(location: "EU").impacts.match_path("*.Lithium.*").where(location: ["CN", "Africa"]).sum(:impact_amount)
54
+ # AR query with ltree syntax
55
+ User.last.active_trees.disabled.match_path("*.CustomProcess.*")
56
+ User.last.active_trees.match_path("*{5,10}.CustomProcess.*.Ecosphere.*")
57
+ User.last.active_trees.active.match_path("*.WhateverProcess.*.Ecosphere.*.CO2Emission.*").where( impact_amount: [100..150]).sum(:impact_amount)
58
+
59
+ Lca::Cycle.match_path("Top.Electric Vehicle.*").impacts.match_path("*.CO2*").where( impact_amount: [ 1000..10000 ]).average(:impact_amount)
60
+ Lca::Cycle::Product.where(name: "Electric Vehicle Battery").impacts.match_path("*.Cobalt.*").sum(:impact_amount)
61
+ Lca::Process.where(owner: User.last).match_path("*{10,20}.*Assembly, automated.*")
62
+ Lca::Impact.match_path("*.Transport by truck.*")
63
+ Lca::Exchange.match_path("*.Oil.*.Ecosphere.*").impacts.sum(:impact_amount)
64
+ Lca::Product.match_path("*.ElectricVehicle.*").processes.match_path("*.Processing.*").where(location: "EU").impacts.match_path("*.Lithium.*").where(location: ["CN", "Africa"]).sum(:impact_amount)
63
65
 
64
- # pg_ltree queries
65
- User.last.lca_cycles.last.parent
66
- Lca::Process.match_path("*.Manual assembly.*").children
66
+ # pg_ltree queries
67
+ User.last.active_trees.last.parent
68
+ Lca::Process.match_path("*.Manual assembly.*").children
67
69
 
68
- # pg_ltree combined with AR syntax
69
- User.last.lca_cycles(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
70
+ # pg_ltree combined with AR syntax
71
+ User.last.active_trees(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
70
72
 
71
- # all queries can be directed to a specific partition:
72
- Lca::Process.owned_by( owner_id ).match_path("*.Recycling.*").where(impact_unit: "tons CO2/year").impacts.sum(:impact_amount)
73
+ # all queries can be directed to a specific partition:
74
+ Lca::Process.owned_by( owner_id ).match_path("*.Recycling.*").where(impact_unit: "tons CO2/year").impacts.sum(:impact_amount)
73
75
 
74
76
  ```
75
77
 
76
78
  The gem also creates some default models:
77
79
 
78
80
  ```ruby
79
- Lca::Process::Transport::ByAir.match_path("*.CO2Emission.*").impacts.sum(:impact_amount)
80
- Lca::Impact::Ecosphere::Fauna.match_path("*.ResourceExtraction.*").where(impact_unit: "Species killed/year").sum(:impact_amount)
81
+ Lca::Process::Transport::ByAir.match_path("*.CO2Emission.*").impacts.sum(:impact_amount)
82
+ Lca::Impact::Ecosphere::Fauna.match_path("*.ResourceExtraction.*").where(impact_unit: "Species killed/year").sum(:impact_amount)
81
83
  ```
82
84
 
83
85
  To see what syntax to use for path traversal please check out the following resources:
86
+ * active_tree gem https://github.com/nicksterious/active_tree
84
87
  * pg_ltree gem https://github.com/sjke/pg_ltree
85
88
  * Postgres ltree extension documentation https://www.postgresql.org/docs/9.1/ltree.html
86
89
 
87
- 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
90
+ ## Caveats
91
+
92
+ `pg_ltree` .child / .parent queries do not work across different models due to an ActiveRecord limitation that requires results to be related via inheritance.
93
+
94
+ The following behaviors have been observed:
95
+
96
+ ```ruby
88
97
 
89
- 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.
98
+ Lca::Process.last.children.impacts.where(impact_amount: 50)
99
+ # => nil
100
+
101
+ Lca::Process.last.children.unscope(where: :type).impacts.where(impact_amount: 50)
102
+ # => ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: Lca::Impact is not a subclass of Lca::Process)
103
+ ```
90
104
 
91
105
  ## Roadmap
92
106
 
93
107
  * .child / .parent query support across models unrelated through inheritance
94
108
  * more model templates for various LCA cycles, processes and impacts
95
109
  * model validations
96
- * query objects
97
110
  * postgres RECURSIVE queries
98
111
  * builders
99
112
  * seeds/fixtures
@@ -103,10 +116,6 @@ If the `create_postgrest_roles` setting is on each new owner will be assigned a
103
116
 
104
117
  Bug reports, pull requests and feature suggestions are welcome on GitHub at https://github.com/nicksterious/lca
105
118
 
106
- ## Sponsors
107
-
108
- We welcome any sponsorship through Github Sponsors. All corporate sponsors' logo and URL of choice shall show up within this section.
109
-
110
119
  ## License
111
120
 
112
121
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -10,17 +10,6 @@ module Lca
10
10
  migration_template "migration.rb.tt", "db/migrate/install_lca.rb", migration_version: migration_version
11
11
  end
12
12
 
13
- def copy_initializer
14
- copy_file 'initializer.rb.tt', 'config/initializers/lca.rb'
15
- end
16
-
17
- def copy_config
18
- conf_file = "config/lca.yml"
19
- copy_file "config.yml.tt", conf_file
20
- contents = File.read( conf_file ).gsub("changeme", ('a'..'z').to_a.shuffle.first(4).join )
21
- File.open(conf_file, 'wb') { |file| file.write(contents) }
22
- end
23
-
24
13
  def migration_version
25
14
  "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
26
15
  end
@@ -3,59 +3,48 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
3
3
 
4
4
  # enable ltree extension
5
5
  begin
6
- execute "create extension ltree"
6
+ execute "create extension ltree"
7
7
  rescue
8
- p "LTREE was already enabled"
8
+ p "LTREE was already enabled"
9
9
  end
10
10
 
11
- # LCA_OPTIONS[:table_name]
11
+ # add any columns required by your business model
12
+ add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount, :decimal
13
+ add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount_previous, :decimal
14
+ add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount_unit, :text
15
+ add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_factor, :decimal
16
+ add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_precision, :decimal
17
+
18
+
19
+ # snapshots table
20
+ # maybe this should only store the impacts?
12
21
  execute <<-SQL
13
- create table #{ LCA_OPTIONS[:table_name] } (
22
+ create table #{ ACTIVE_TREE_OPTIONS[:table_name] }_snapshots (
14
23
  id serial,
15
24
 
25
+ impact_id integer,
26
+
16
27
  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_entity_id integer,
26
- parent_entity_type text,
27
-
28
- path ltree,
29
- path_slug text,
28
+ owner_type character varying,
30
29
 
31
30
  impact_amount decimal,
31
+ impact_amount_previous decimal,
32
32
  impact_amount_unit text,
33
33
  impact_factor decimal,
34
34
  impact_precision decimal,
35
35
 
36
+ created_at timestamp(6) without time zone not null,
37
+ updated_at timestamp(6) without time zone not null,
38
+
36
39
  primary key (id, owner_id)
37
40
  ) partition by list(owner_id)
38
41
  SQL
39
42
 
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_entity_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
43
+ # index the snapshots table
44
+ add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :id
45
+ add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :owner_id
46
+ add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :impact_id
47
+ add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :created_at
48
+
60
49
  end
61
50
  end
@@ -1,6 +1,6 @@
1
- # this still needed?
2
1
  # 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
2
+ # besides, no need to polute other classes with our stuff.
3
+
4
+ # ActiveSupport.on_load(:active_record) do
5
+ # extend Lca::Lcable
6
+ # end
@@ -1,84 +1,60 @@
1
1
  require "jwt"
2
2
 
3
+ # read https://www.postgresqltutorial.com/postgresql-schema/
4
+
3
5
  module Lca
4
6
  module Lcable
5
7
  extend ActiveSupport::Concern
6
8
 
7
- # def self.included(base)
8
- # ::Lca::Model.class_exec do
9
- # @@owner_class = base.name
10
- # end
11
- # end
12
-
13
9
  included do
10
+ before_destroy :lca_delete_storage
14
11
 
15
- has_many :lca_cycles, class_name: "::Lca::Model", 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}_#{ LCA_OPTIONS[:owner_role_suffix] }"
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
12
+ #
13
+ include ActiveTree::ActiveTreeAble
14
+ after_create :lca_create_storage
39
15
 
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
16
+ has_many :lca_cycles, class_name: "::Lca::Model", foreign_key: :owner_id, as: :owner
43
17
 
44
- if LCA_OPTIONS[:create_postgrest_roles]
45
- # drop role if it exists
46
- lca_sql "drop role if exists #{ lca_role }"
47
18
 
48
- # create role
49
- lca_sql "create role #{ lca_role }"
19
+ def lca_role
20
+ "active_tree_owner_#{id}_#{ ACTIVE_TREE_OPTIONS[:owner_role_suffix] }"
21
+ end # role
50
22
 
51
- # grant privs
52
- lca_sql "grant all privileges on #{lca_table_name}_#{id} to #{ lca_role }"
53
- end
54
23
 
55
- end # create_storage
24
+ def lca_table_name
25
+ ACTIVE_TREE_OPTIONS[:table_name]
26
+ end
56
27
 
57
28
 
58
- # Deletes or detaches the partition and removes the role for this owner
59
- def lca_delete_storage
29
+ def lca_create_storage
30
+ # create snapshots partition
31
+ lca_sql "create table if not exists #{lca_table_name}_snapshots_#{id} partition of #{lca_table_name}_snapshots for values in (#{id})"
32
+
33
+ # if roles are enabled, grant privileges
34
+ if ACTIVE_TREE_OPTIONS[:create_postgrest_roles]
35
+ lca_sql "grant all privileges on #{lca_table_name}_snapshots_#{id} to #{ lca_role }"
36
+ end
37
+ end
60
38
 
61
- if LCA_OPTIONS[:create_postgrest_roles]
62
- # revoke privs
63
- lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_#{id} FROM #{ lca_role }"
64
39
 
65
- # delete role
66
- lca_sql "drop role #{ lca_role }"
67
- end
40
+ def lca_delete_storage
41
+ # remove privs
42
+ if ACTIVE_TREE_OPTIONS[:create_postgrest_roles]
43
+ lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_snapshots_#{id} FROM #{ lca_role }"
44
+ end
68
45
 
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_role }"
75
- end
76
- end # delete_storage
46
+ if ACTIVE_TREE_OPTIONS[:destroy_partition_on_owner_destroy]
47
+ lca_sql "drop table if exists #{lca_table_name}_snapshots_#{id}"
48
+ else
49
+ lca_sql "alter table #{lca_table_name}_snapshots detach partition #{ lca_role }"
50
+ end
51
+
52
+ end
77
53
 
78
- def lca_sql sql
79
- ActiveRecord::Base.connection.execute sql
80
- end # lca_sql
81
54
 
55
+ def lca_sql(q)
56
+ ActiveRecord::Base.connection.execute q
57
+ end
82
58
  end # ClassMethods
83
59
  end
84
60
  end
@@ -1,6 +1,7 @@
1
1
  class Lca::Cycle::Product < Lca::Cycle
2
2
  # price?
3
3
  # quantity?
4
+ # quantity unit?
4
5
  # life time?
5
6
  # life time unit?
6
7
  end
@@ -1,4 +1,5 @@
1
1
  class Lca::Cycle::Service < Lca::Cycle
2
2
  # duration?
3
+ # duration unit?
3
4
  # price?
4
5
  end
@@ -1,8 +1,18 @@
1
1
  class Lca::Impact < Lca::Model
2
2
  belongs_to :exchange, class_name: "::Lca::Exchange", foreign_key: :parent_entity_id, required: true
3
3
 
4
+ has_many :snapshots, class_name: "::Lca::ImpactSnapshot", foreign_key: :impact_id, dependent: :destroy
5
+
4
6
  validates_presence_of :impact_amount
5
7
  validates_presence_of :impact_amount_unit, allow_blank: false
6
8
  validates_presence_of :impact_factor
7
9
  validates_presence_of :impact_precision
10
+
11
+ # lol?
12
+ before_update :snapshot!
13
+
14
+
15
+ def take_snapshot!
16
+ return ::Lca::ImpactSnapshot.take! self
17
+ end # .snapshot!
8
18
  end
@@ -0,0 +1,55 @@
1
+ class Lca::ImpactSnapshot < ActiveRecord::Base
2
+
3
+ self.primary_key = :id
4
+
5
+ def self.table_name
6
+ return "#{::ACTIVE_TREE_OPTIONS[:table_name]}_snapshots" if defined? ::ACTIVE_TREE_OPTIONS
7
+ return "active_tree_snapshots"
8
+ end
9
+
10
+
11
+ belongs_to :impact, class_name: "::Lca::Impact", foreign_key: :impact_id, required: true
12
+ belongs_to :owner, polymorphic: true, required: true
13
+
14
+ validates_presence_of :impact_amount
15
+ validates_presence_of :impact_amount_unit, allow_blank: false
16
+ validates_presence_of :impact_factor
17
+ validates_presence_of :impact_precision
18
+
19
+
20
+
21
+ # pull data from owner's partition
22
+ def self.owned_by(owner_id)
23
+ return self if !owner_id.is_a? Integer
24
+ partition_suffix = "_#{owner_id}"
25
+ table = "#{ self.table_name }#{ partition_suffix }"
26
+
27
+ ApplicationRecord.connection.schema_cache.clear!
28
+ return self if !ApplicationRecord.connection.schema_cache.data_source_exists? table
29
+
30
+ model_class = Class.new self
31
+
32
+ original_class_name = self.name
33
+ class_name = "#{name}#{partition_suffix}"
34
+
35
+ model_class.define_singleton_method(:table_name) do
36
+ table
37
+ end
38
+
39
+ model_class.define_singleton_method(:name) do
40
+ class_name
41
+ end
42
+
43
+ model_class
44
+ end # owned_by
45
+
46
+
47
+ def self.take!(impact)
48
+ return create(
49
+ impact.attributes
50
+ .deep_symbolize_keys
51
+ .slice(:impact_amount, :impact_amount_previous, :impact_amount_unit, :impact_factor, :impact_precision, :owner_id, :owner_type)
52
+ .merge( impact_id: impact.id )
53
+ )
54
+ end # .take!
55
+ end
@@ -1,75 +1,5 @@
1
- class Lca::Model < ActiveRecord::Base
1
+ class Lca::Model < ActiveTree::Model
2
2
 
3
- include Lca::Statusable
4
3
 
5
- self.primary_key = :id
6
-
7
- ltree :path
8
-
9
- def self.table_name
10
- return ::LCA_OPTIONS[:table_name] if defined? ::LCA_OPTIONS
11
- return "lca_models"
12
- end
13
-
14
- belongs_to :owner, polymorphic: :true, required: true
15
-
16
- scope :match_path, -> (some_path) { where("path ~ ?", "#{some_path}") }
17
-
18
- #has_one_attached :picture
19
- #has_rich_text :description
20
-
21
- validates_presence_of :name, allow_blank: false
22
- validates_presence_of :path, allow_blank: false
23
-
24
- before_validation :set_defaults
25
- def set_defaults
26
- self.path ||= name.delete(" ").gsub(/[^0-9a-z ]/i, '') if name
27
- self.path_slug = path.parameterize if path
28
- end
29
-
30
-
31
- # Scoping by owner in order to select the partition
32
- #
33
- # @param owner_id [Integer] the partition owner
34
- def self.owned_by(owner_id)
35
- # if we're looking for anything else but an integer, revert to the base class
36
- return self if !owner_id.is_a? Integer
37
-
38
- partition_suffix = "_#{owner_id}"
39
-
40
- table = "#{ self.table_name }#{ partition_suffix }"
41
-
42
- ApplicationRecord.connection.schema_cache.clear!
43
- return self if !ApplicationRecord.connection.schema_cache.data_source_exists? table
44
-
45
- # duplicate the class
46
- model_class = Class.new self
47
- original_class_name = self.name
48
-
49
- # ...for this owner
50
- class_name = "#{name}#{partition_suffix}"
51
-
52
- # specify the table
53
- model_class.define_singleton_method(:table_name) do
54
- table
55
- end
56
-
57
- # specify the name
58
- model_class.define_singleton_method(:name) do
59
- class_name
60
- end
61
-
62
- model_class.define_singleton_method(:sti_name) do
63
- original_class_name
64
- end
65
-
66
- # override the STI name lmfao
67
- model_class.define_singleton_method(:find_sti_class) do |p|
68
- original_class_name.constantize
69
- end
70
-
71
- # proceed
72
- model_class
73
- end # .owned_by
74
4
 
75
5
  end
@@ -1,2 +1,6 @@
1
1
  class Lca::Process::Transport < Lca::Process
2
- end
2
+ # distance?
3
+ # distance unit?
4
+ # duration?
5
+ # duration unit?
6
+ end
@@ -3,4 +3,9 @@ class Lca::Stage < Lca::Model
3
3
  belongs_to :cycle, class_name: "::Lca::Cycle", foreign_key: :parent_entity_id, required: true
4
4
  has_many :processes, class_name: "::Lca::Process", foreign_key: :parent_entity_id
5
5
 
6
+ # duration?
7
+ # duration unit?
8
+
9
+ # quantity?
10
+ # quantity unit?
6
11
  end
@@ -1,8 +1,3 @@
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
1
+ class Lca::Queries::Query < ActiveTree::Query
7
2
 
8
3
  end
data/lib/lca/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lca
4
- VERSION = "0.2.4"
4
+ VERSION = "0.2.9"
5
5
  end
data/lib/lca.rb CHANGED
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require "active_support/all"
3
2
 
3
+ require "active_support/all"
4
4
  require "active_record"
5
-
6
5
  require "pg_ltree"
6
+ require "active_tree"
7
7
 
8
8
  require_relative "lca/version"
9
9
 
10
- require_relative "lca/models/concerns/statusable"
10
+ require_relative "lca/models/concerns/lcable"
11
11
  require_relative "lca/models/model"
12
12
  require_relative "lca/models/cycle"
13
13
  require_relative "lca/models/stage"
14
14
  require_relative "lca/models/process"
15
15
  require_relative "lca/models/exchange"
16
16
  require_relative "lca/models/impact"
17
+ require_relative "lca/models/impact_snapshot"
17
18
  require_relative "lca/models/cycle/product"
18
19
  require_relative "lca/models/cycle/service"
19
20
  require_relative "lca/models/cycle/activity"
@@ -37,39 +38,12 @@ require_relative "lca/models/impact/ecosphere/flora"
37
38
  require_relative "lca/models/impact/human_health/cancer"
38
39
  require_relative "lca/models/impact/technosphere/resource_availability"
39
40
 
40
- require_relative "lca/models/concerns/lcable"
41
41
  require_relative "lca/active_record"
42
42
 
43
- # TODO add query objects and builders
44
- #require "lca/queries/"
45
- #require "lca/builders/"
46
43
 
47
44
  module Lca
48
45
  class Error < StandardError; end
49
46
 
50
- # Your code goes here...
51
-
52
- class << self
53
- attr_accessor :lca_models
54
- attr_accessor :options
55
- end
56
-
57
- self.lca_models = []
58
-
59
- def self.lca_options
60
- @options ||= begin
61
- path = Rails.root.join("config", "lca.yml").to_s
62
- if File.exist?(path)
63
- YAML.load(ERB.new(File.read(path)).result)
64
- else
65
- {
66
- table_name: "lca_models",
67
- jwt_secret: "",
68
- jwt_encryption: "HS256"
69
- }
70
- end
71
- end
72
- end
73
47
 
74
48
  def self.env
75
49
  @env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick @ Earthster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-25 00:00:00.000000000 Z
11
+ date: 2021-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -25,33 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: activestorage
28
+ name: active_tree
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '6.0'
33
+ version: 0.2.8
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
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'
40
+ version: 0.2.8
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: pg_ltree
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -97,14 +83,10 @@ files:
97
83
  - bin/console
98
84
  - bin/setup
99
85
  - lib/generators/lca/install_generator.rb
100
- - lib/generators/lca/templates/config.yml.tt
101
- - lib/generators/lca/templates/initializer.rb.tt
102
86
  - lib/generators/lca/templates/migration.rb.tt
103
87
  - lib/lca.rb
104
88
  - lib/lca/active_record.rb
105
- - lib/lca/builders/builder.rb
106
89
  - lib/lca/models/concerns/lcable.rb
107
- - lib/lca/models/concerns/statusable.rb
108
90
  - lib/lca/models/cycle.rb
109
91
  - lib/lca/models/cycle/activity.rb
110
92
  - lib/lca/models/cycle/product.rb
@@ -118,6 +100,7 @@ files:
118
100
  - lib/lca/models/impact/human_health/cancer.rb
119
101
  - lib/lca/models/impact/technosphere.rb
120
102
  - lib/lca/models/impact/technosphere/resource_availability.rb
103
+ - lib/lca/models/impact_snapshot.rb
121
104
  - lib/lca/models/model.rb
122
105
  - lib/lca/models/process.rb
123
106
  - lib/lca/models/process/raw_resource.rb
@@ -1,18 +0,0 @@
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"
15
-
16
- # suffix postgres roles with a random string
17
- # to avoid collisions between other LCA installations in other apps using same db server
18
- owner_role_suffix: "changeme"
@@ -1,17 +0,0 @@
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
- owner_role_suffix: "changeme"
13
- }
14
- end
15
- end.merge({
16
- database_user: Rails.application.config.database_configuration[ Rails.env ].deep_symbolize_keys[:username]
17
- })
@@ -1,3 +0,0 @@
1
- class Lca::Builder
2
-
3
- end
@@ -1,44 +0,0 @@
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