diff --git a/.rubocop.yml b/.rubocop.yml index d4761e1fb..f1253a4ab 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,4 @@ -require: +plugins: - rubocop-performance - rubocop-rspec @@ -329,3 +329,6 @@ Lint/InterpolationCheck: Lint/UnusedMethodArgument: Exclude: - 'packages/forest_admin_rails/config/initializers/forest_admin_error_subscriber.rb' + +Style/RedundantParentheses: + Enabled: false diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb b/packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb index dd0cdf6ff..326bda986 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb @@ -88,7 +88,7 @@ def serialize(schema) { data: data, - included: included.reject!(&:empty?)&.flatten, + included: included.reject!(&:empty?)&.flatten || [], meta: schema[:meta] } end diff --git a/packages/forest_admin_agent/spec/lib/forest_admin_agent/utils/schema/schema_emitter_spec.rb b/packages/forest_admin_agent/spec/lib/forest_admin_agent/utils/schema/schema_emitter_spec.rb index be65f9edc..2c41a2f57 100644 --- a/packages/forest_admin_agent/spec/lib/forest_admin_agent/utils/schema/schema_emitter_spec.rb +++ b/packages/forest_admin_agent/spec/lib/forest_admin_agent/utils/schema/schema_emitter_spec.rb @@ -91,7 +91,7 @@ module Schema expect(schema).to eq( { data: [], - included: nil, + included: [], meta: { liana: described_class::LIANA_NAME, liana_version: described_class::LIANA_VERSION, @@ -123,7 +123,7 @@ module Schema expect(schema).to eq( { data: [], - included: nil, + included: [], meta: json_schema[:meta] } ) diff --git a/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb b/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb index 141143751..f8fc142fa 100644 --- a/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb +++ b/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb @@ -175,6 +175,18 @@ def fetch_associations ) end end + when :has_and_belongs_to_many + add_field( + association.name.to_s, + ForestAdminDatasourceToolkit::Schema::Relations::ManyToManySchema.new( + foreign_collection: format_model_name(association.klass.name), + origin_key: association.join_primary_key, + origin_key_target: association.join_foreign_key, + foreign_key: association.association_foreign_key, + foreign_key_target: association.association_primary_key, + through_collection: association.join_table.classify + ) + ) end end end diff --git a/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/datasource.rb b/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/datasource.rb index cf0b6cad5..e7352edc6 100644 --- a/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/datasource.rb +++ b/packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/datasource.rb @@ -78,7 +78,8 @@ def fetch_model(model) @models << model unless model.abstract_class? || @models.include?(model) || !model.table_exists? || - !primary_key?(model) + !primary_key?(model) || + model.const_defined?(:VIRTUAL_THROUGH_COLLECTION) end end @@ -101,19 +102,24 @@ def build_habtm(model) if @habtm_models.key?(model.table_name) @habtm_models[model.table_name].left_reflection = model.right_reflection # when the second model is added, we can push the HABTM model to the models list - @models << make_through_model( + through_model = make_through_model( model.table_name, [ @habtm_models[model.table_name].left_reflection, @habtm_models[model.table_name].right_reflection ] ) + + add_collection( + Collection.new(self, through_model, support_polymorphic_relations: @support_polymorphic_relations) + ) else @habtm_models[model.table_name] = model end end def make_through_model(table_name, associations) + through_model_name = table_name.classify through_model = Class.new(ActiveRecord::Base) do class << self attr_accessor :name, :table_name @@ -124,13 +130,15 @@ def self.add_association(name, options) end end - through_model.name = table_name.singularize + through_model.name = through_model_name through_model.table_name = table_name through_model.primary_key = [associations[0].foreign_key, associations[1].foreign_key] associations.each do |association| through_model.add_association(association.name, association.options) end + through_model.const_set(:VIRTUAL_THROUGH_COLLECTION, true) + through_model end end diff --git a/packages/forest_admin_datasource_active_record/spec/dummy/app/models/company.rb b/packages/forest_admin_datasource_active_record/spec/dummy/app/models/company.rb new file mode 100644 index 000000000..2ee075340 --- /dev/null +++ b/packages/forest_admin_datasource_active_record/spec/dummy/app/models/company.rb @@ -0,0 +1,3 @@ +class Company < ApplicationRecord + has_and_belongs_to_many :users +end diff --git a/packages/forest_admin_datasource_active_record/spec/dummy/app/models/user.rb b/packages/forest_admin_datasource_active_record/spec/dummy/app/models/user.rb index f1df24e7e..2beb4c1f7 100644 --- a/packages/forest_admin_datasource_active_record/spec/dummy/app/models/user.rb +++ b/packages/forest_admin_datasource_active_record/spec/dummy/app/models/user.rb @@ -1,6 +1,7 @@ class User < ApplicationRecord belongs_to :car has_one :address, as: :addressable + has_and_belongs_to_many :companies enum :enum_field, { draft: 0, published: 1, archived: 2, trashed: 3 } end diff --git a/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095726_create_companies.rb b/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095726_create_companies.rb new file mode 100644 index 000000000..56abf97f2 --- /dev/null +++ b/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095726_create_companies.rb @@ -0,0 +1,8 @@ +class CreateCompanies < ActiveRecord::Migration[7.0] + def change + create_table :companies do |t| + t.column :name, :string + t.timestamps + end + end +end diff --git a/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095727_create_companies_users.rb b/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095727_create_companies_users.rb new file mode 100644 index 000000000..f9987cb12 --- /dev/null +++ b/packages/forest_admin_datasource_active_record/spec/dummy/db/migrate/20230810095727_create_companies_users.rb @@ -0,0 +1,8 @@ +class CreateCompaniesUsers < ActiveRecord::Migration[7.0] + def change + create_table :companies_users, id: false do |t| + t.belongs_to :company + t.belongs_to :user + end + end +end diff --git a/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/collection_spec.rb b/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/collection_spec.rb index e02dcefc1..fd7c82283 100644 --- a/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/collection_spec.rb +++ b/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/collection_spec.rb @@ -35,6 +35,12 @@ module ForestAdminDatasourceActiveRecord expect(datasource.get_collection('User').schema[:fields].keys).not_to include('address') expect(datasource.get_collection('Address').schema[:fields].keys).not_to include('addressable') end + + it 'add has_and_belongs_to_many relation' do + collection = described_class.new(datasource, Company) + + expect(collection.schema[:fields].keys).to include('users') + end end end diff --git a/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/datasource_spec.rb b/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/datasource_spec.rb index ef583c150..2e496a012 100644 --- a/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/datasource_spec.rb +++ b/packages/forest_admin_datasource_active_record/spec/lib/forest_admin_datasource_active_record/datasource_spec.rb @@ -4,7 +4,7 @@ module ForestAdminDatasourceActiveRecord describe Datasource do it 'fetch all models' do datasource = described_class.new({ adapter: 'sqlite3', database: 'db/database.db' }) - expected = %w[User Order Check Category CarCheck Car Address] + expected = %w[User Order Check Category CarCheck Car Address Company CompaniesUser] expect(datasource.collections.keys).to match_array(expected) end