lca 0.2.1 → 0.2.5

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: 9484c550eb1523766573047c009f2081526636c4918aeb29609fbc40885990b1
4
- data.tar.gz: cd3266a10c9238189d327e7f92928c7c734094ae23d8dca999beb12342a13c75
3
+ metadata.gz: 62afb1561f978b69743ea334e439cc449375f0b4bc1cad29601b174a5d231a41
4
+ data.tar.gz: 1f18a6226d1fee18f3e2d226616c34dd3cae9ba99389ef9ffb17cf828c586e34
5
5
  SHA512:
6
- metadata.gz: 6858541887aae952fad81721253a4b51cf64d2a880dd8cc311d4afc1b672ae7938331fbd9e1d2bbcd77d8d860db2bb4dbf2e438271db4b8ce0843b7b5206ca55
7
- data.tar.gz: 9bf817a23784db18f4c60df38087c3a4409c938ee49d4634270a5aaaed0b50be0182a60ad03ddcba904e6964d65580b6d5ef4fe3c52de4698fdc85ac2297f511
6
+ metadata.gz: e190b6449f86a075d6582276747f6f17b1d617cf404f1db04ef7600f7ac9a84490d60535901a9b1e3fc212fb855b381fb80f78d583db391a159b27c75b63e9a3
7
+ data.tar.gz: b2fc67ead55300e549192c8f1b37e2971e11bc4da9ed08956ea46a601fc6aba1596a56106822f1b085e2c82d94560975bcc301ef8df3946121a331ddcd925a7f
data/Gemfile CHANGED
@@ -9,4 +9,7 @@ gem "rspec", "~> 3.0"
9
9
 
10
10
  gem "activerecord", "~> 6.0"
11
11
  gem "activestorage", "~> 6.0"
12
- gem "actiontext", "~> 6.0"
12
+ gem "actiontext", "~> 6.0"
13
+
14
+ gem "jwt"
15
+ gem "pg_ltree"
data/README.md CHANGED
@@ -4,7 +4,7 @@
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
 
@@ -42,42 +42,42 @@ Include the LCA concern into one of your models which will own lifecycle trees (
42
42
  This will extend your model and enable the following functionality:
43
43
 
44
44
  ```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(...)
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(...)
51
51
 
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)
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)
63
63
 
64
- # pg_ltree queries
65
- User.last.lca_cycles.last.parent
66
- Lca::Process.match_path("*.Manual assembly.*").children
64
+ # pg_ltree queries
65
+ User.last.lca_cycles.last.parent
66
+ Lca::Process.match_path("*.Manual assembly.*").children
67
67
 
68
- # pg_ltree combined with AR syntax
69
- User.last.lca_cycles(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
68
+ # pg_ltree combined with AR syntax
69
+ User.last.lca_cycles(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
70
70
 
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)
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
73
 
74
74
  ```
75
75
 
76
76
  The gem also creates some default models:
77
77
 
78
78
  ```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)
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
81
  ```
82
82
 
83
83
  To see what syntax to use for path traversal please check out the following resources:
@@ -88,14 +88,30 @@ The LCA gem is designed to be compatible with PostgREST. PostgREST is an amazing
88
88
 
89
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.
90
90
 
91
+ ## Caveats
92
+
93
+ `pg_ltree` .child / .parent queries do not work across different models due to an ActiveRecord limitation that requires results to be related via inheritance.
94
+
95
+ The following behaviors have been observed:
96
+
97
+ ```ruby
98
+
99
+ Lca::Process.last.children.impacts.where(impact_amount: 50)
100
+ # => nil
101
+
102
+ Lca::Process.last.children.unscope(where: :type).impacts.where(impact_amount: 50)
103
+ # => ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: Lca::Impact is not a subclass of Lca::Process)
104
+ ```
105
+
91
106
  ## Roadmap
92
107
 
93
- * more models for various LCA cycles, processes and impacts
108
+ * .child / .parent query support across models unrelated through inheritance
109
+ * more model templates for various LCA cycles, processes and impacts
94
110
  * model validations
95
- * query objects, including postgres RECURSIVE queries
111
+ * query objects
112
+ * postgres RECURSIVE queries
96
113
  * builders
97
114
  * seeds/fixtures
98
- * automated tests
99
115
 
100
116
 
101
117
  ## Contributing
@@ -14,19 +14,23 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
14
14
  id serial,
15
15
 
16
16
  owner_id integer,
17
- owner_type text,
17
+ owner_type character varying,
18
+
19
+ status integer,
18
20
 
19
- data_external_id text,
20
- data_provider text,
21
+ data_external_id character varying,
22
+ data_provider character varying,
21
23
 
22
- type text,
24
+ type character varying,
23
25
  name text,
24
26
 
25
- parent_id integer,
26
- parent_type text,
27
+ parent_entity_id integer,
28
+ parent_entity_type character varying,
27
29
 
28
30
  path ltree,
29
31
  path_slug text,
32
+
33
+ metadata_inline jsonb,
30
34
 
31
35
  impact_amount decimal,
32
36
  impact_amount_unit text,
@@ -44,7 +48,7 @@ SQL
44
48
  add_index LCA_OPTIONS[:table_name], :id
45
49
  add_index LCA_OPTIONS[:table_name], :owner_id
46
50
  add_index LCA_OPTIONS[:table_name], :type
47
- add_index LCA_OPTIONS[:table_name], :parent_id
51
+ add_index LCA_OPTIONS[:table_name], :parent_entity_id
48
52
 
49
53
  # next two indexes unfortunately can't be unique since a cycle can appear several times under an owner
50
54
  add_index LCA_OPTIONS[:table_name], :path, using: :gist
@@ -57,5 +61,19 @@ SQL
57
61
  execute "create role postgrest_anon nologin"
58
62
  execute "grant postgrest_anon to #{ LCA_OPTIONS[:database_user] }"
59
63
  end
64
+
65
+
66
+
67
+ #
68
+ create_table "#{ LCA_OPTIONS[:table_name] }_metadata" do |t|
69
+ t.string :model_type
70
+ t.integer :model_id
71
+ t.string :key
72
+ t.text :value
73
+
74
+ t.timestamps
75
+ end
76
+ add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :model_type, :model_id ]
77
+ add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :key ]
60
78
  end
61
79
  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
@@ -12,7 +12,7 @@ module Lca
12
12
 
13
13
  included do
14
14
 
15
- has_many :lca_cycles, class_name: "::Lca::Cycle", foreign_key: :owner_id, as: :owner
15
+ has_many :lca_cycles, class_name: "::Lca::Model", foreign_key: :owner_id, as: :owner
16
16
  after_create :lca_create_storage
17
17
  before_destroy :lca_delete_storage
18
18
 
@@ -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,6 +1,6 @@
1
1
  class Lca::Cycle < Lca::Model
2
2
 
3
- has_many :stages, class_name: "::Lca::Stage", foreign_key: :parent_id, dependent: :destroy
3
+ has_many :stages, class_name: "::Lca::Stage", foreign_key: :parent_entity_id, dependent: :destroy
4
4
 
5
5
  def subcycles
6
6
  ::Lca::Cycle.match_path("#{path}.*")
@@ -1,4 +1,4 @@
1
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
2
+ belongs_to :process, class_name: "::Lca::Process", foreign_key: :parent_entity_id, required: true
3
+ has_many :impacts, class_name: "::Lca::Impact", foreign_key: :parent_entity_id, dependent: :destroy
4
4
  end
@@ -1,6 +1,6 @@
1
1
  class Lca::Impact < Lca::Model
2
- belongs_to :exchange, class_name: "::Lca::Exchange", foreign_key: :parent_id
3
-
2
+ belongs_to :exchange, class_name: "::Lca::Exchange", foreign_key: :parent_entity_id, required: true
3
+
4
4
  validates_presence_of :impact_amount
5
5
  validates_presence_of :impact_amount_unit, allow_blank: false
6
6
  validates_presence_of :impact_factor
@@ -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,12 +4,14 @@ 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"
10
12
  end
11
13
 
12
- belongs_to :owner, polymorphic: :true
14
+ belongs_to :owner, polymorphic: :true, required: true
13
15
 
14
16
  scope :match_path, -> (some_path) { where("path ~ ?", "#{some_path}") }
15
17
 
@@ -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
 
22
- before_save :set_defaults
24
+ has_many :metadata, class_name: "::Lca::Metadata", dependent: :destroy, as: :model
25
+
26
+ before_validation :set_defaults
23
27
  def set_defaults
24
- self.path ||= name.parameterize.gsub("-", ".")
25
- self.path_slug = path.parameterize
28
+ self.path ||= name.delete(" ").gsub(/[^0-9a-z ]/i, '') if name
29
+ self.path_slug = path.parameterize if path
30
+ self.metadata_inline ||= {}
26
31
  end
27
32
 
28
33
 
@@ -57,6 +62,10 @@ class Lca::Model < ActiveRecord::Base
57
62
  class_name
58
63
  end
59
64
 
65
+ model_class.define_singleton_method(:sti_name) do
66
+ original_class_name
67
+ end
68
+
60
69
  # override the STI name lmfao
61
70
  model_class.define_singleton_method(:find_sti_class) do |p|
62
71
  original_class_name.constantize
@@ -1,10 +1,9 @@
1
1
  class Lca::Process < Lca::Model
2
2
 
3
- has_many :exchanges, class_name: "::Lca::Exchange", foreign_key: :parent_id, dependent: :destroy
3
+ has_many :exchanges, class_name: "::Lca::Exchange", foreign_key: :parent_entity_id, dependent: :destroy
4
4
  has_many :impacts, through: :exchanges
5
- belongs_to :stage, class_name: "::Lca::Stage", foreign_key: :parent_id
6
5
 
7
- def subprocesses
8
- ::Lca::Process.where("path ~ ?", "#{path}.*")
9
- end
6
+ has_many :subprocesses, class_name: "::Lca::Process", foreign_key: :parent_entity_id, dependent: :destroy, as: :parent_entity
7
+ belongs_to :parent_entity, polymorphic: true, required: true
8
+
10
9
  end
@@ -1,6 +1,6 @@
1
1
  class Lca::Stage < Lca::Model
2
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
3
+ belongs_to :cycle, class_name: "::Lca::Cycle", foreign_key: :parent_entity_id, required: true
4
+ has_many :processes, class_name: "::Lca::Process", foreign_key: :parent_entity_id
5
5
 
6
6
  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.1"
4
+ VERSION = "0.2.5"
5
5
  end
data/lib/lca.rb CHANGED
@@ -3,9 +3,12 @@ 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"
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.1
4
+ version: 0.2.5
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-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -118,6 +118,7 @@ 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/metadata.rb
121
122
  - lib/lca/models/model.rb
122
123
  - lib/lca/models/process.rb
123
124
  - lib/lca/models/process/raw_resource.rb