Comments (5)
@tute, Ambry keeps fields as class attributes which, within Ruby's object model, are not inherited by descendant classes. Of course, this is a well know topic and yes, it can be done.
Still, I think that, for Ambry, inheriting fields wouldn't solve a problem but create a new one. For example, how would you handle a case where you want to inherit behavior but not all attributes? You would need to either remove or reset field attributes until the set matches your need.
Taller class hierarchies are not very modern Ruby-ish in style and less flexible overall. That's why Ambry, and most recent libraries, favor module inclusion/extension over class inheritance.
from ambry.
@tute, this is the way I'd go for your example:
require 'ambry'
require 'ambry/active_model'
module Badge
module Basic
def self.included(klass)
klass.instance_eval do
extend Ambry::Model
extend Ambry::ActiveModel
field :id, :name, :level, :image, :description
end
end
end
end
class BuyerBadge
include Badge::Basic
end
class SellerBadge
include Badge::Basic
field :featured
end
BuyerBadge.attribute_names # => [:id, :name, :level, :image, :description]
SellerBadge.attribute_names # => [:id, :name, :level, :image, :description, :featured]
Same features without inheritance.
from ambry.
Hi @xymbol. Subclasses not inheriting attributes feel weird to me because it doesn't respect the "is a" relationship. For this example, badges or any of it's subclasses should act semantically as badges, following the substitutability principle.
The reason for subclassing should be that the hierarchy in software design models a hierarchy in the real world, so subclassing for inheriting behavior feels weird to me. If I needed so I'd use module inclusion, as your code example works.
I still vote for Ambry allowing traditional subclassing/subtyping mechanism, though I understand why Ruby community tends to use module inclusion in favor of subclassing.
Thanks for your comments!
from ambry.
@tute With the risk of starting a completely off-topic debate, the short reply would be that subclassing, especially when dealing with real world models, is usually abused. Yes, we all accept that a scooter and a vessel are subclasses of some sort of vehicle class but, the truth is, they share orders of magnitude less than what they don't.
So, back to your badge example, I would try to think more about what badges are and do instead of internal details, such as persistence, validation, etc. In that respect, Ambry (or whatever ORM) is more like materials or tools to produce your badges, not the badges themselves. They shouldn't be that important.
As an example, let's add an AgentBadge
which gets MongoDB persistence via Mongoid.
require 'ambry'
require 'ambry/active_model'
require 'mongoid'
class Badge
module Ambry
def self.included(klass)
klass.instance_eval do
extend ::Ambry::Model
extend ::Ambry::ActiveModel
field :id, :name, :level, :image, :description
end
end
end
module Mongoid
def self.included(klass)
klass.instance_eval do
include ::Mongoid::Document
field :name
field :level, type: Integer
field :image
field :description
end
end
end
def gold?
level == 3
end
def platinum?
level == 4
end
def executive_platinum?
level == 5
end
def premium?
gold? || platinum? || executive_platinum?
end
end
class BuyerBadge < Badge
include Badge::Ambry
end
class SellerBadge < Badge
include Badge::Ambry
field :featured
end
class AgentBadge < Badge
include Badge::Mongoid
field :commission
end
BuyerBadge.attribute_names # => [:id, :name, :level, :image, :description]
SellerBadge.attribute_names # => [:id, :name, :level, :image, :description, :featured]
AgentBadge.attribute_names # => ["_type", "_id", "name", "level", "image", "description", "commission"]
Now all badges inherit methods from a parent class, whereas persistence is added on a per-class basis.
The difference may seem subtle but the added flexibility is not. Some libraries like ActiveRecord have been long criticized for forcing this meaningless subclassing. IIRC, starting with Rails 4 we will be able to drop in ActiveRecord persistence without inheriting from ActiveRecord::Base
.
Hope this example makes a better case than the previous one.
from ambry.
It's the best code I've seen for my example, in which I want to have the persistance logic and add only one field. Thanks @xymbol for your response.
from ambry.
Related Issues (5)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ambry.