Skip to content

Instantly share code, notes, and snippets.

@solnic
Created March 25, 2011 13:02
Show Gist options
  • Save solnic/886807 to your computer and use it in GitHub Desktop.
Save solnic/886807 to your computer and use it in GitHub Desktop.
This gist shows the status of EmbeddedValue development in dm-core and my notes

API common for Model and EmbeddedValue::Model

Class methods

  • self.append_extensions
  • self.append_inclusions
  • self.default_storage_name
  • self.descendants
  • self.extended
  • self.extra_extensions
  • self.extra_inclusions
  • self.name
  • self.new
  • self.raise_on_save_failure
  • self.raise_on_save_failure=

Instance methods

  • all
  • assert_valid
  • assert_valid_key_size
  • at
  • const_missing
  • copy
  • create!
  • create
  • default_order
  • default_repository_name
  • default_storage_name
  • descendants
  • destroy
  • destroy!
  • each
  • fetch
  • first
  • first_or_create
  • first_or_new
  • get!
  • get
  • inherited
  • last
  • load
  • new_collection
  • raise_on_save_failure
  • raise_on_save_failure=
  • repositories
  • repository_name
  • repository
  • reverse
  • scoped_query
  • storage_name
  • storage_names
  • update!
  • update
  • values_at

API common for Resource and EmbeddedValue

Class methods

  • self.append_inclusions(*inclusions)
  • self.descendants
  • self.extra_inclusions
  • self.included(model)

Instance methods

  • add_to_identity_map
  • after_create_hook
  • after_destroy_hook
  • after_save_hook
  • after_update_hook
  • assert_not_destroyed
  • assert_save_successful
  • assert_update_clean_only
  • attribute_dirty?
  • attribute_get
  • attribute_loaded?
  • attributes=
  • attribute_set
  • attributes
  • before_create_hook
  • before_destroy_hook
  • before_save_hook
  • before_update_hook
  • child_associations
  • child_relationships
  • clean?
  • clear_subjects
  • cmp?
  • collection
  • collection=
  • collection_for_self
  • conditions
  • create_with_hooks
  • destroy
  • destroy!
  • destroyed?
  • _destroy
  • dirty?
  • dirty_attributes
  • dirty_children?
  • dirty_parents?
  • dirty_self?
  • eager_load
  • eql?
  • execute_hooks_for
  • fields
  • hash
  • identity_map
  • initialize
  • initialize_copy
  • inspect
  • key
  • lazy_load
  • new?
  • original_attributes
  • parent_associations
  • parent_relationships
  • _persist
  • persisted_state
  • persisted_state?
  • persisted_state=
  • properties
  • query
  • raise_on_save_failure
  • raise_on_save_failure=
  • readonly?
  • relationships
  • reload
  • remove_from_identity_map
  • repository
  • repository_name
  • reset_key
  • run_once
  • save
  • save!
  • save_children
  • saved?
  • _save
  • save_parents
  • save_self
  • set_default_value
  • update!
  • update
  • update_attributes
  • update_with_hooks
################################################################################
# IMPORTANT: Initially I'm working only with mongo adapter.
# After that's done DO adapters will get proper support for EV too
#
#
# Following things already work
#
# EmbeddedValue declared as a property
#
class Address
include DataMapper::EmbeddedValue
property :street, String
property :zip, String
end
class User
include DataMapper::Resource
property :id, Serial
property :address, Address
end
#
# create API is the same like in case of a 1:1 relationship
#
User.create :address => { :street => "foo", :zip => '1234' }
#
# Query API is also the same like in case of 1:1 relationship
#
User.all :address => { :street => /foo/ }
######################################################################
#
# TODO
#
#
# dirty tracking
#
user = User.new :address => { :street => "Bar" }
user.dirty? # should be true
user.address.dirty? # should also be true
user.dirty_attributes # should include :address property
user.address.dirty_attributes # should include :street
#
# Query via dot notation
#
User.first "address.street" => "Foo"
#
# DataMapper::EmbeddedValue::Collection
#
class Comment
include DataMapper::EmbeddedValue
property :id, Serial
property :author, String
property :content, Text
end
class Post
include DataMapper::Resource
property :id, Serial
property :title, String
property :body, Text
has n, :comments
end
Post.relationships[:comments] # DataMapper::EmbeddedValue::Collection
  • Integration Model shared specs for Resource/EV:

    • DataMapper::Model::Core
    • DataMapper::Model::Property
    • DataMapper::Model::Hook
    • DataMapper::Model::Relationship
  • Integration Resource shared specs for Resource/EV:

    • DataMapper::Resource::Attributes
    • DataMapper::Resource::RaiseOnSave
    • DataMapper::Resource::PersistedState
    • DataMapper::Resource::Hooks
    • DataMapper::Resource::Operators
  • EmbeddedCollection

@dkubb
Copy link

dkubb commented Mar 27, 2011

Wow, I forgot how many instance methods we have now. It seems like so much when you list it like that. One question that comes to mind is, do we refactor the code when we generalize it for usage by EV and Resource instances?

I see several different concerns when I look at those methods, and it makes me think that we could create objects that are responsible for each of those concerns (like dirty tracking for example), and then have the methods delegate to those. (In the ideal case we'd eventually deprecate those delegate methods and "thin out" the methods in the Resource public API; but that probably comes later on)

Once we have each concern encapsulated in one class, we can generalize things so that they work with both resources and EVs.

I don't know if that's the best approach, but it does seem like a lot of functionality you'd eventually have to duplicate between Resource and EV; it seems like using composition to extend those objects might be a relatively simple approach.

@solnic
Copy link
Author

solnic commented Mar 27, 2011

Yes that's a lot. I'd prefer extract certain parts of functionality into shared modules instead of creating separate classes and using delegation. We would see how that works and if it's still too complex we could think about separate classes.

Another thing is that Model and Collection have a lot in common - but maybe let's deal with that after EV is done.

@dkubb
Copy link

dkubb commented Mar 27, 2011

As a first step, I think that's ok, but I generally prefer composition to inheritance (which module inclusion is just another form of). IMHO it's much easier to test things when you have explicit injection of dependencies, than when you have to stub out module behaviour in order to test things in isolation.

It just depends on the use case, if I actually want to add methods that don't exist in the including class, then I will use a module. However, you have to be aware when doing this that you're increasing the complexity of the API by jamming more methods into the including class. IMHO things are easier to use when they do one thing well, and have slim APIs so that you can compartmentalize the behaviours. I'll only include methods when it's orthogonal to the responsibilities of the including class.

The only reason we jammed all those methods into Resource was a mistaken belief it would be higher performance; and frankly we were modelling parts of AR and didn't really know what we needed until we were done. Now that we're mostly feature complete on that end, we can start to break things down and design things how we should've done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment