0

I have created an interface called Collection to hold in a collection of any objects from my project's models. I want this collection vs an array as I want other fields in Collection.

    module Collection
      def self.included(klass)
        klass.attr_writer :list, type: Array
        klass.attr_writer :class_type,   type: Class
        # Other fields go in here, along with their validations
        klass.validate :validate_list

        def validate_list
          self.list.each { |o|
            if(!o.instance_of? self.class_type)
              klass.errors.add :list, 'Objects in list must be of the same type'
              return
            end
          }
        end
      end
    end

I want to use this Collection to hold a list of Models::Company 's objects, apart from other lists which I will add to Portfolio Model in the future. I want this list of companies to be only a part of the Portfolio Model.

class Portfolio
  include Model::Collection

  @schema = {
      'type' => 'object',
      'properties' => {
          'id'                       => { 'type' => 'string' },
          'title'                    => { 'type' => 'string'  },
          'description'              => { 'type' => 'string'  },
          'companies_list'          =>  {'type' => '?'}, # 1. Should this be array or Collections?
      }
  }
  @modelName      = 'portfolios'
  @collectionName = 'portfolios'


  store_in collection: 'portfolios'

  field :title,                     type: String
  field :description,               type: String
  field :companies_list,            type: Array # 2. Should this be array or array of Collections?

  embeds_many :companies

end

Any help is appreciated.

4
  • Using the term "Abstract" in a module is really going to confuse people. That has a specific meaning and it relates to classes, though Ruby doesn't really use that term at all since there's no standard method for expressing an abstract base class. Also it's really not clear what the problem is here. Commented Aug 24, 2016 at 23:27
  • @tadman: Sorry, I should've changed it to class ? Commented Aug 24, 2016 at 23:31
  • No, I mean it's fine that it's a mixin module, but calling it "Abstract" is misleading. Why not call it Model::CollectionMethods or Model::CollectionValidations or something like that? Commented Aug 24, 2016 at 23:32
  • ah, got it. done. I am calling it collections as it has both methods and fields. Is that ok? Commented Aug 24, 2016 at 23:34

1 Answer 1

2

I saw that you come from Java world, and I guess you want to bring Java's generics to Ruby. But, at first place, why Java has generics? Let's have a history lesson.

In early Java (before 1.5), there are no generic types, so the programmers have to write code like this:

List list = new ArrayList();
// add some strings to the list
list.add("foo");
list.add("bar");

// we have to iterate over each element as an Object
for (Object obj : list) {
  // and then cast it to String
  String str = (String) obj;
  // in order to call String methods on it.
  String uppercased = str.toUpperCase();

  // ...
}

This is certainly not DRY. To ease the pain of casting, Java 1.5 introduces generics.

List<String> list = new ArrayList<String>();
// add some strings to the list
list.add("foo");
list.add("bar");

// now we can iterate over the elements as strings
for (String str : list) {
  // no more casting, yay!
  String uppercased = str.toUpperCase();

  // ...
}

But wait, where does the non-generic version go wrong in the first place?

In Java, the variable type determines which methods can be called on the object, not the object itself. If you declare the variable in a more general type (i.e. a superclass), you can't call methods that belongs to a more special type (i.e. a subclass). If you want to call those methods, you have to cast.

But what if the object itself can decide which methods can be called on it? Suddenly generics become useless. Ruby and many many other dynamic languages follow this way. Rubyists call it duck typing - if something walks like a duck and quacks like a duck, it is a duck.

list = ['foo', 'bar']
list.each do |str|
  # we don't care what type str is,
  # as long as it has the method upcase.
  str.upcase if str.respond_to?(:upcase)
end

So rubyists usually don't define container classes, they just use arrays. If type restriction should be applied, they just apply it when the object is added to the array.

list = []
list << something if something.is_a? Portfolio

Another reason to stick to arrays is that arrays have awesome literals like ['foo', 'bar'] and %w(foo bar) and %i(foo bar), which custom container types don't have.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, that did help in clearing up a lot of concepts!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.