lca 0.2.3 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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