lca 0.2.3 → 0.2.8

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: 771b69813b99e5d0f92e8b69acd1a6c4b30014c8b1e6b6d253d60870bcf8b719
4
- data.tar.gz: fe2d5c4335ea33f83d42d2997725b543c3c7fcb9d8e7c9057f023be92dea9264
3
+ metadata.gz: a16c5260b03c32b7338e2afc954b3b7e81cdac0d70281b381572cef31c4e2100
4
+ data.tar.gz: fb743e4d069c4768b3cb75fd8bd38774a5600148078e1d84bd7dc26e6686c351
5
5
  SHA512:
6
- metadata.gz: 0b96260e9b0e79ca1b6add7b838adeb138d7ba0809271dd913f06759132a21145993cff3d30c55866f271160006d613b51d812417f8697d0550cd38a96f74009
7
- data.tar.gz: e555dc61a62267987635e46bba1758134a8177d3fbf74ff769f746cc27d7b8ec175a8adc34b901d024a9cf8106c6051d4709fab09dfb81f423604f184372d259
6
+ metadata.gz: d0cb142ada744da2836415053fd8e5aa246463d2921dea9ad3b0f92aa1a6c88fe5e50fefdae34189ca6cee87a0bd664fdad47447737d364b1f39018518c3c517
7
+ data.tar.gz: 28bfb9f87f16d36ed059db06a4aa91de7d8b3390b2f49c3f69e74519958d093b64fbdfebb753e26fead95f8fe49aec7352742545f17559bed53e652ffe311316
data/README.md CHANGED
@@ -4,10 +4,12 @@
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
@@ -42,42 +44,42 @@ 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.lca_cycles
49
+ User.find_by(name: "Acme").lca_cycles.impacts.select("impact_unit, sum(impact_amount) as total_impact").group(:impact_unit)
50
+ User.last.lca_cycles.where(...)
51
+ User.last.lca_cycles.where(...).group(...)
52
+ User.last.lca_cycles.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.lca_cycles.disabled.match_path("*.CustomProcess.*")
56
+ User.last.lca_cycles.match_path("*{5,10}.CustomProcess.*.Ecosphere.*")
57
+ User.last.lca_cycles.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.lca_cycles.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.lca_cycles(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:
@@ -88,14 +90,30 @@ The LCA gem is designed to be compatible with PostgREST. PostgREST is an amazing
88
90
 
89
91
  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.
90
92
 
93
+ ## Caveats
94
+
95
+ `pg_ltree` .child / .parent queries do not work across different models due to an ActiveRecord limitation that requires results to be related via inheritance.
96
+
97
+ The following behaviors have been observed:
98
+
99
+ ```ruby
100
+
101
+ Lca::Process.last.children.impacts.where(impact_amount: 50)
102
+ # => nil
103
+
104
+ Lca::Process.last.children.unscope(where: :type).impacts.where(impact_amount: 50)
105
+ # => ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: Lca::Impact is not a subclass of Lca::Process)
106
+ ```
107
+
91
108
  ## Roadmap
92
109
 
93
- * more models for various LCA cycles, processes and impacts
110
+ * .child / .parent query support across models unrelated through inheritance
111
+ * more model templates for various LCA cycles, processes and impacts
94
112
  * model validations
95
- * query objects, including postgres RECURSIVE queries
113
+ * query objects
114
+ * postgres RECURSIVE queries
96
115
  * builders
97
116
  * seeds/fixtures
98
- * automated tests
99
117
 
100
118
 
101
119
  ## Contributing
@@ -3,42 +3,53 @@ 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
 
11
- # LCA_OPTIONS[:table_name]
12
+ # create the main table as set up within LCA_OPTIONS[:table_name]
12
13
  execute <<-SQL
13
14
  create table #{ LCA_OPTIONS[:table_name] } (
14
15
  id serial,
15
16
 
16
17
  owner_id integer,
17
- owner_type text,
18
+ owner_type character varying,
19
+
20
+ status integer,
18
21
 
19
- data_external_id text,
20
- data_provider text,
22
+ data_external_id character varying,
23
+ data_provider character varying,
21
24
 
22
- type text,
25
+ type character varying,
23
26
  name text,
24
27
 
25
28
  parent_entity_id integer,
26
- parent_entity_type text,
29
+ parent_entity_type character varying,
27
30
 
28
31
  path ltree,
29
32
  path_slug text,
33
+
34
+ metadata_inline jsonb,
30
35
 
31
36
  impact_amount decimal,
37
+ impact_amount_previous decimal,
32
38
  impact_amount_unit text,
33
39
  impact_factor decimal,
34
40
  impact_precision decimal,
41
+
42
+ created_at timestamp(6) without time zone not null,
43
+ updated_at timestamp(6) without time zone not null,
44
+
35
45
 
36
46
  primary key (id, owner_id)
37
47
  ) partition by list(owner_id)
38
48
  SQL
39
49
 
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"
50
+ # create an "others" partition for when the owner is undefined/unknown? just in case / may help in some edge cases
51
+ execute "CREATE TABLE #{LCA_OPTIONS[:table_name]}_others PARTITION OF #{LCA_OPTIONS[:table_name]} DEFAULT"
52
+
42
53
 
43
54
  # add indexes
44
55
  add_index LCA_OPTIONS[:table_name], :id
@@ -50,6 +61,8 @@ SQL
50
61
  add_index LCA_OPTIONS[:table_name], :path, using: :gist
51
62
  add_index LCA_OPTIONS[:table_name], [:data_provider, :data_external_id]
52
63
 
64
+
65
+ # when postgrest is enabled...
53
66
  if LCA_OPTIONS[:create_postgrest_roles]
54
67
  # create postgrest anon user with no privs
55
68
  # postgrest may pass an user's role using JWT
@@ -57,5 +70,49 @@ SQL
57
70
  execute "create role postgrest_anon nologin"
58
71
  execute "grant postgrest_anon to #{ LCA_OPTIONS[:database_user] }"
59
72
  end
73
+
74
+
75
+ # snapshots table
76
+ # maybe this should only store the impacts?
77
+ execute <<-SQL
78
+ create table #{ LCA_OPTIONS[:table_name] }_snapshots (
79
+ id serial,
80
+
81
+ impact_id integer,
82
+
83
+ owner_id integer,
84
+ owner_type character varying,
85
+
86
+ impact_amount decimal,
87
+ impact_amount_previous decimal,
88
+ impact_amount_unit text,
89
+ impact_factor decimal,
90
+ impact_precision decimal,
91
+
92
+ created_at timestamp(6) without time zone not null,
93
+ updated_at timestamp(6) without time zone not null,
94
+
95
+ primary key (id, owner_id)
96
+ ) partition by list(owner_id)
97
+ SQL
98
+
99
+ # index the snapshots table
100
+ add_index "#{LCA_OPTIONS[:table_name]}_snapshots", :id
101
+ add_index "#{LCA_OPTIONS[:table_name]}_snapshots", :owner_id
102
+ add_index "#{LCA_OPTIONS[:table_name]}_snapshots", :impact_id
103
+ add_index "#{LCA_OPTIONS[:table_name]}_snapshots", :created_at
104
+
105
+
106
+ # metadata table
107
+ create_table "#{ LCA_OPTIONS[:table_name] }_metadata" do |t|
108
+ t.string :model_type
109
+ t.integer :model_id
110
+ t.string :key
111
+ t.text :value
112
+
113
+ t.timestamps
114
+ end
115
+ add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :model_type, :model_id ]
116
+ add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :key ]
60
117
  end
61
118
  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,18 +1,14 @@
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
14
10
 
15
- has_many :lca_cycles, class_name: "::Lca::Cycle", foreign_key: :owner_id, as: :owner
11
+ has_many :lca_cycles, class_name: "::Lca::Model", foreign_key: :owner_id, as: :owner
16
12
  after_create :lca_create_storage
17
13
  before_destroy :lca_delete_storage
18
14
 
@@ -41,16 +37,21 @@ module Lca
41
37
  lca_sql "create table if not exists #{lca_table_name}_#{id} partition of #{lca_table_name} for values in (#{id})"
42
38
  # TODO create partition indexes
43
39
 
44
- if LCA_OPTIONS[:create_postgrest_roles]
45
- # drop role if it exists
46
- lca_sql "drop role if exists #{ lca_role }"
40
+ # create snapshots partition
41
+ lca_sql "create table if not exists #{lca_table_name}_snapshots_#{id} partition of #{lca_table_name}_snapshots 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_role }"
47
47
 
48
- # create role
49
- lca_sql "create role #{ lca_role }"
48
+ # create role
49
+ lca_sql "create role #{ lca_role }"
50
50
 
51
- # grant privs
52
- lca_sql "grant all privileges on #{lca_table_name}_#{id} to #{ lca_role }"
53
- end
51
+ # grant privs
52
+ lca_sql "grant all privileges on #{lca_table_name}_#{id} to #{ lca_role }"
53
+ lca_sql "grant all privileges on #{lca_table_name}_snapshots_#{id} to #{ lca_role }"
54
+ end
54
55
 
55
56
  end # create_storage
56
57
 
@@ -58,21 +59,24 @@ module Lca
58
59
  # Deletes or detaches the partition and removes the role for this owner
59
60
  def lca_delete_storage
60
61
 
61
- if LCA_OPTIONS[:create_postgrest_roles]
62
+ if LCA_OPTIONS[:create_postgrest_roles]
62
63
  # revoke privs
63
64
  lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_#{id} FROM #{ lca_role }"
65
+ lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_snapshots_#{id} FROM #{ lca_role }"
64
66
 
65
67
  # delete role
66
68
  lca_sql "drop role #{ lca_role }"
67
- end
69
+ end
68
70
 
69
- if LCA_OPTIONS[:destroy_partition_on_owner_destroy]
71
+ if LCA_OPTIONS[:destroy_partition_on_owner_destroy]
70
72
  # delete partition
71
73
  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
74
+ lca_sql "drop table if exists #{lca_table_name}_snapshots_#{id}"
75
+ else
76
+ # detach and forget about it
77
+ lca_sql "alter table #{lca_table_name} detach partition #{ lca_role }"
78
+ lca_sql "alter table #{lca_table_name}_snapshots detach partition #{ lca_role }"
79
+ end
76
80
  end # delete_storage
77
81
 
78
82
  def lca_sql sql
@@ -13,8 +13,13 @@ module Lca::Statusable
13
13
  alias_method :disable!, :inactive!
14
14
  alias_method :off!, :inactive!
15
15
  alias_method :toggle?, :toggle_status!
16
+
17
+ before_create :set_default_status
16
18
  end
17
19
 
20
+ def set_default_status
21
+ self.status ||= 1
22
+ end
18
23
 
19
24
  def toggle_status!
20
25
  if active?
@@ -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 "#{::LCA_OPTIONS[:table_name]}_snapshots" if defined? ::LCA_OPTIONS
7
+ return "lca_models_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
@@ -0,0 +1,12 @@
1
+ class Lca::Metadata < ActiveRecord::Base
2
+
3
+ belongs_to :model, polymorphic: true, required: true
4
+
5
+ def self.table_name
6
+ return "#{ ::LCA_OPTIONS[:table_name] }_metadata" if defined? ::LCA_OPTIONS
7
+ return "lca_models_metadata"
8
+ end
9
+
10
+ validates_presence_of :key
11
+
12
+ end
@@ -4,6 +4,8 @@ class Lca::Model < ActiveRecord::Base
4
4
 
5
5
  self.primary_key = :id
6
6
 
7
+ ltree :path
8
+
7
9
  def self.table_name
8
10
  return ::LCA_OPTIONS[:table_name] if defined? ::LCA_OPTIONS
9
11
  return "lca_models"
@@ -19,10 +21,13 @@ class Lca::Model < ActiveRecord::Base
19
21
  validates_presence_of :name, allow_blank: false
20
22
  validates_presence_of :path, allow_blank: false
21
23
 
24
+ has_many :metadata, class_name: "::Lca::Metadata", dependent: :destroy, as: :model
25
+
22
26
  before_validation :set_defaults
23
27
  def set_defaults
24
- self.path ||= name.parameterize.gsub("-", ".") if name
28
+ self.path ||= name.delete(" ").gsub(/[^0-9a-z ]/i, '') if name
25
29
  self.path_slug = path.parameterize if path
30
+ self.metadata_inline ||= {}
26
31
  end
27
32
 
28
33
 
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.3"
4
+ VERSION = "0.2.8"
5
5
  end
data/lib/lca.rb CHANGED
@@ -3,15 +3,19 @@ require "active_support/all"
3
3
 
4
4
  require "active_record"
5
5
 
6
+ require "pg_ltree"
7
+
6
8
  require_relative "lca/version"
7
9
 
8
10
  require_relative "lca/models/concerns/statusable"
11
+ require_relative "lca/models/metadata"
9
12
  require_relative "lca/models/model"
10
13
  require_relative "lca/models/cycle"
11
14
  require_relative "lca/models/stage"
12
15
  require_relative "lca/models/process"
13
16
  require_relative "lca/models/exchange"
14
17
  require_relative "lca/models/impact"
18
+ require_relative "lca/models/impact_snapshot"
15
19
  require_relative "lca/models/cycle/product"
16
20
  require_relative "lca/models/cycle/service"
17
21
  require_relative "lca/models/cycle/activity"
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.3
4
+ version: 0.2.8
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-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -118,6 +118,8 @@ files:
118
118
  - lib/lca/models/impact/human_health/cancer.rb
119
119
  - lib/lca/models/impact/technosphere.rb
120
120
  - lib/lca/models/impact/technosphere/resource_availability.rb
121
+ - lib/lca/models/impact_snapshot.rb
122
+ - lib/lca/models/metadata.rb
121
123
  - lib/lca/models/model.rb
122
124
  - lib/lca/models/process.rb
123
125
  - lib/lca/models/process/raw_resource.rb