Skip to content
This repository has been archived by the owner on Sep 18, 2019. It is now read-only.

Commit

Permalink
This is a sampling of code meant to demonstrate collections for attrio.
Browse files Browse the repository at this point in the history
This code is not tested yet.

Readme changes are examples of using Collections.
attributes_parser had collection, create_collections, and
fetch_container_type methods added.
collection is a class parallel to Attribute for use with collections.
  • Loading branch information
anithri committed Jun 25, 2013
1 parent dda5c73 commit 0779cf6
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 2 deletions.
33 changes: 31 additions & 2 deletions README.md
Expand Up @@ -24,7 +24,7 @@ Or install it yourself as:

## Usage

Include Attrio into your class and then use `#define_attributes` block to declare you attributes.
Include Attrio into your class and then use `#define_attributes` block to declare your attributes and collections.

```ruby
class User
Expand All @@ -34,6 +34,8 @@ class User
attr :name, String
attr :age, Integer
attr :birthday, DateTime
collection :roles, Symbol, unique: true
collection :pictures, UserPicture
end
end
```
Expand Down Expand Up @@ -72,10 +74,12 @@ Accessor name can be easily overridden by passing `:as` option to `define_attrib
class User
include Attrio

define_attributes :as => 'api_attributes' do
define_attributes :as => 'api_attributes', collection_as: 'api_collections' do
attr :name, String
attr :age, Integer
attr :birthday, DateTime
collection :roles, Symbol
collection :pictures, UserPicture, unique: true, index: :filename
end
end
```
Expand All @@ -85,6 +89,31 @@ user = User.new
user.api_attributes # => {...}
```

### Accessing Collections
By default Attrio defines `#collections` accessor which contains `Hash` with collection names as keys and instances of `Attrio::Collection` as values. Each instance of `Attrio::Collection` contains the following information:
* type
* collection reader name
* collection reader visibility
* add element method name
* add element method visibility
* find element method name
* find element method visibility
* instance variable name
* additional options

```ruby
user = User.new
user.collections
# => {
# :roles => #<Attrio::Collection:0x007fc44defa680 @name='roles', @type=Symbol, @options={}, @collection_reader_name="roles", @collection_reader_visibility=:pubic, @add_element_name='add_role',@add_element_visibility=:public, @find_element_name='find_role', @find_element_name=:private, @instance_variable_name='@roles'>
# :pictures => #<Attrio::Collection:0x007f322efa680 @name='pcitures', @type=UserPicture, @options={:unique => true, :index => :filename}, @collection_reader_name="pictures", @collection_reader_visibility=:pubic, @add_element_name='add_picture',@add_element_visibility=:public, @find_element_name='find_picture', @find_element_name=:public, @instance_variable_name='@pictures'>
# }
user.collections.keys
# => [:roles, :pictures]
```

Collections with a truthy option :unique (and no :index), are created as sets. Collections with a truthy option :unique and a :index set are created as a hash using the value returned by the method defined as :index as a key and the object as a hash. Collections without a truthy :unique options are created as an array

### Default values

Attrio supports all the ways to setup default values that Virtus has.
Expand Down
21 changes: 21 additions & 0 deletions lib/attrio/attributes_parser.rb
Expand Up @@ -26,6 +26,14 @@ def attr(*args)

alias_method :attribute, :attr

def collection(*args)
collection_name = args[0].to_s
collection_options = args.last.kind_of?(Hash) ? args.pop : Hash.new
collection_type = self.fetch_type(collection_options.delete(:type) || args[1])

collection_container_type = self.fetch_container_type(options)
end

def self.cast_type(constant)
return constant if constant.is_a?(Class) && !!(constant < Attrio::Types::Base)

Expand Down Expand Up @@ -60,10 +68,23 @@ def fetch_type(name)
type
end

def fetch_container_type(options)
if options.fetch(:unique, false)
return options.fetch(:index,false) ? Hash : Set
else
return Array
end
end

def create_attribute(name, type, options)
Attrio::Attribute.new(name, type, options).define_writer(self.klass).define_reader(self.klass)
end

def create_collection(name, container, type, options)
#TODO chain define_? calls when those are determined
Attrio::Collection.new(name, container, type, options)
end

def add_attribute(name, attribute)
@klass.send(self.as)[name.to_sym] = attribute
end
Expand Down
81 changes: 81 additions & 0 deletions lib/attrio/collection.rb
@@ -0,0 +1,81 @@
# encoding: utf-8

#TODO should behavior common to both Collection and Attribute be refactored out into a base class?

module Attrio
class Collection
attr_reader :name, :container, :type, :options

def initialize(name, container, type, options)
@name = name; @container = container; @type = type, @options = Helpers.symbolize_keys(options)
end

def collection_reader_name
@collection_reader_name ||= self.accessor_name_from_options(:collection_reader) || self.name
end

def collection_reader_visibility
@collection_reader_visibility ||= self.accessor_visibility_from_options(:collection_reader) || :public
end

def add_element_name
@add_element_name ||= self.accessor_name_from_options(:add_element) || "add_#{self.name}"
end

def add_element_visibility
@add_element_visibility ||= self.accessor_visibility_from_options(:add_element) || :public
end

def find_element_name
@add_element_name ||= self.accessor_name_from_options(:find_element) || "find_#{self.name}"
end

def find_element_visibility
@add_element_visibility ||= self.accessor_visibility_from_options(:find_element) || :public
end

def instance_variable_name
@instance_variable_name ||= self.options[:instance_variable_name] || "@#{self.name}"
end

def default_value
if !defined?(@default_value)
@default_value = Attrio::DefaultValue.new(self.name, self.options[:default])
end
@default_value
end

def define_reader(klass)
#TODO complete after creating Attrio::Builders::CollectionAddElementBuilder
end

def define_add_element(klass)
#TODO complete after creating Attrio::Builders::CollectionAddElementBuilder
end

def define_find_element(klass)
#TODO complete after creating Attrio::Builders::CollectionAddElementBuilder
end

protected
def accessor_name_from_options(accessor)
(self.options[accessor.to_sym].is_a?(Hash) && self.options[accessor.to_sym][:name]) || self.options["#{accessor.to_s}_name".to_sym]
end

def accessor_visibility_from_options(accessor)
return self.options[accessor] if self.options[accessor].present? && [:public, :protected, :private].include?(self.options[accessor])
(self.options[accessor].is_a?(Hash) && self.options[accessor][:visibility]) || self.options["#{accessor.to_s}_visibility".to_sym]
end

end
end

#* type
#* collection reader name
#* collection reader visibility
#* add element method name
#* add element method visibility
#* find element method name
#* find element method visibility
#* instance variable name
#* additional options

0 comments on commit 0779cf6

Please sign in to comment.