aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorteddywing2018-03-01 17:12:19 +0100
committerGitHub2018-03-01 17:12:19 +0100
commit3d5e2bc56650471954ba1c51c7f53cbd84013d47 (patch)
treef82a0b7fbf81d4e4ad60492f6c51268356b1e256
parent9ae8c7e2f7efd8441b2c403c35eca3cbccdfd6cc (diff)
parentb9c3c0a47db7d733a80001f2b80ddcb7d69ec852 (diff)
downloadchouette-core-3d5e2bc56650471954ba1c51c7f53cbd84013d47.tar.bz2
Merge pull request #237 from af83/proposed-custom-fields-refactor
Proposed custom fields refactor
-rw-r--r--app/models/chouette/vehicle_journey.rb16
-rw-r--r--app/models/concerns/custom_fields_support.rb24
-rw-r--r--app/models/custom_field.rb76
-rw-r--r--spec/factories/custom_fields.rb2
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb5
-rw-r--r--spec/models/custom_field_spec.rb42
6 files changed, 144 insertions, 21 deletions
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 9b94f7f0e..c5a6901d7 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -3,6 +3,7 @@ module Chouette
class VehicleJourney < Chouette::TridentActiveRecord
has_paper_trail
include ChecksumSupport
+ include CustomFieldsSupport
include VehicleJourneyRestrictions
include ObjectidSupport
include StifTransportModeEnumerations
@@ -340,21 +341,6 @@ module Chouette
end
end
- def self.custom_fields
- CustomField.where(resource_type: self.name.split("::").last)
- end
-
-
- def custom_fields
- Hash[*self.class.custom_fields.map do |v|
- [v.code, v.slice(:code, :name, :field_type, :options).update(value: custom_field_value(v.code))]
- end.flatten]
- end
-
- def custom_field_value key
- (custom_field_values || {})[key.to_s]
- end
-
def self.matrix(vehicle_journeys)
Hash[*VehicleJourneyAtStop.where(vehicle_journey_id: vehicle_journeys.pluck(:id)).map do |vjas|
[ "#{vjas.vehicle_journey_id}-#{vjas.stop_point_id}", vjas]
diff --git a/app/models/concerns/custom_fields_support.rb b/app/models/concerns/custom_fields_support.rb
new file mode 100644
index 000000000..6c76bd653
--- /dev/null
+++ b/app/models/concerns/custom_fields_support.rb
@@ -0,0 +1,24 @@
+module CustomFieldsSupport
+ extend ActiveSupport::Concern
+
+ included do
+ validate :custom_fields_values_are_valid
+
+ def self.custom_fields
+ CustomField.where(resource_type: self.name.split("::").last)
+ end
+
+ def custom_fields
+ CustomField::Collection.new self
+ end
+
+ def custom_field_value key
+ (custom_field_values || {})[key.to_s]
+ end
+
+ private
+ def custom_fields_values_are_valid
+ custom_fields.values.all?{|cf| cf.valid?}
+ end
+ end
+end
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index 774c8b0f6..4a840744e 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -6,4 +6,80 @@ class CustomField < ActiveRecord::Base
validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]}
validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}
+
+ class Collection < HashWithIndifferentAccess
+ def initialize object
+ vals = object.class.custom_fields.map do |v|
+ [v.code, CustomField::Value.new(object, v, object.custom_field_value(v.code))]
+ end
+ super Hash[*vals.flatten]
+ end
+
+ def to_hash
+ HashWithIndifferentAccess[*self.map{|k, v| [k, v.to_hash]}.flatten(1)]
+ end
+ end
+
+ class Value
+ def self.new owner, custom_field, value
+ field_type = custom_field.options["field_type"]
+ klass_name = field_type && "CustomField::Value::#{field_type.classify}"
+ klass = klass_name && const_defined?(klass_name) ? klass_name.constantize : CustomField::Value::Base
+ klass.new owner, custom_field, value
+ end
+
+ class Base
+ def initialize owner, custom_field, value
+ @custom_field = custom_field
+ @raw_value = value
+ @owner = owner
+ @errors = []
+ @validated = false
+ @valid = false
+ end
+
+ delegate :code, :name, :field_type, :options, to: :@custom_field
+
+ def validate
+ @valid = true
+ end
+
+ def valid?
+ validate unless @validated
+ @valid
+ end
+
+ def value
+ @raw_value
+ end
+
+ def errors_key
+ "custom_fields.#{code}"
+ end
+
+ def to_hash
+ HashWithIndifferentAccess[*%w(code name field_type options value).map{|k| [k, send(k)]}.flatten(1)]
+ end
+ end
+
+ class Integer < Base
+ def value
+ @raw_value.to_i
+ end
+
+ def validate
+ @valid = true
+ unless @raw_value =~ /\A\d*\Z/
+ @owner.errors.add errors_key, "'#{@raw_value}' is not a valid integer"
+ @valid = false
+ end
+ end
+ end
+
+ class String < Base
+ def value
+ "#{@raw_value}"
+ end
+ end
+ end
end
diff --git a/spec/factories/custom_fields.rb b/spec/factories/custom_fields.rb
index 2f5fae555..7c43a6147 100644
--- a/spec/factories/custom_fields.rb
+++ b/spec/factories/custom_fields.rb
@@ -4,6 +4,6 @@ FactoryGirl.define do
resource_type "VehicleJourney"
sequence(:name){|n| "custom field ##{n}"}
field_type "list"
- options( { "capacity" => "0" } )
+ options( { capacity: "0" } )
end
end
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 76e73d9cf..c69655bd4 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -260,7 +260,7 @@ describe Chouette::VehicleJourney, :type => :model do
item['purchase_windows'] = []
item['footnotes'] = []
item['purchase_windows'] = []
- item['custom_fields'] = vj.custom_fields
+ item['custom_fields'] = vj.custom_fields.to_hash
vj.vehicle_journey_at_stops.each do |vjas|
item['vehicle_journey_at_stops'] << vehicle_journey_at_stop_to_state(vjas)
@@ -282,7 +282,6 @@ describe Chouette::VehicleJourney, :type => :model do
Chouette::VehicleJourney.state_update(route, collection)
}.to change {Chouette::VehicleJourney.count}.by(1)
-
obj = Chouette::VehicleJourney.last
expect(obj).to receive(:after_commit_objectid).and_call_original
@@ -292,7 +291,7 @@ describe Chouette::VehicleJourney, :type => :model do
expect(collection.last['objectid']).to eq obj.objectid
expect(obj.published_journey_name).to eq 'dummy'
- expect(obj.custom_fields["energy"]["value"]).to eq 99
+ expect(obj.custom_fields["energy"].value).to eq 99
end
it 'should expect local times' do
diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb
index 51128b0a2..b92bcfbdb 100644
--- a/spec/models/custom_field_spec.rb
+++ b/spec/models/custom_field_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe CustomField, type: :model do
end
end
-
context "custom fields for a resource" do
let!( :fields ){ [create(:custom_field), create(:custom_field, code: :energy)] }
let!( :instance_fields ){
@@ -26,10 +25,49 @@ RSpec.describe CustomField, type: :model do
}
}
it { expect(Chouette::VehicleJourney.custom_fields).to eq(fields) }
- it { expect(vj.custom_fields).to eq(instance_fields) }
+ it {
+ instance_fields.each do |code, cf|
+ cf.each do |k, v|
+ expect(vj.custom_fields[code].send(k)).to eq(v)
+ end
+ end
+ }
end
context "custom field_values for a resource" do
it { expect(vj.custom_field_value("energy")).to eq(99) }
end
+
+ context "with an 'integer' field_type" do
+ let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'integer'})] }
+ let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: "99"} }
+ it "should cast the value" do
+ expect(vj.custom_fields[:energy].value).to eq 99
+ end
+
+ it "should validate the value" do
+ {
+ "99" => true,
+ "azerty" => false,
+ "91a" => false,
+ "a91" => false
+ }.each do |val, valid|
+ vj = build :vehicle_journey, custom_field_values: {energy: val}
+ if valid
+ expect(vj.validate).to be_truthy
+ else
+ expect(vj.validate).to be_falsy
+ expect(vj.errors.messages[:"custom_fields.energy"]).to be_present
+ end
+ end
+ end
+ end
+
+ context "with a 'string' field_type" do
+ let!(:field){ [create(:custom_field, code: :energy, options: {field_type: 'string'})] }
+ let!( :vj ){ create :vehicle_journey, custom_field_values: {energy: 99} }
+ it "should cast the value" do
+ expect(vj.custom_fields[:energy].value).to eq '99'
+ end
+ end
end