diff options
| author | teddywing | 2018-03-01 17:12:19 +0100 |
|---|---|---|
| committer | GitHub | 2018-03-01 17:12:19 +0100 |
| commit | 3d5e2bc56650471954ba1c51c7f53cbd84013d47 (patch) | |
| tree | f82a0b7fbf81d4e4ad60492f6c51268356b1e256 | |
| parent | 9ae8c7e2f7efd8441b2c403c35eca3cbccdfd6cc (diff) | |
| parent | b9c3c0a47db7d733a80001f2b80ddcb7d69ec852 (diff) | |
| download | chouette-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.rb | 16 | ||||
| -rw-r--r-- | app/models/concerns/custom_fields_support.rb | 24 | ||||
| -rw-r--r-- | app/models/custom_field.rb | 76 | ||||
| -rw-r--r-- | spec/factories/custom_fields.rb | 2 | ||||
| -rw-r--r-- | spec/models/chouette/vehicle_journey_spec.rb | 5 | ||||
| -rw-r--r-- | spec/models/custom_field_spec.rb | 42 |
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 |
