Feedback on my design and how to use DCI Design Pattern?

Discussion in 'Ruby' started by josh, Dec 24, 2011.

  1. josh

    josh Guest

    I would like to get your feedback on the initial design of my app.
    The goal is to email a user when an event he cares about change it's time.
    i have User class, Event Class and an Email module.

    some questions:
    1. should Email be a class instead of a module?
    2. should Email be refactored into a method of the User class?
    3. I read a bit about DCI Design Pattern ( and and I am curious if there is a way to use it here.
    In DCI I believe I'll need a context module that will describe the main use cases of my app,
    and it each use case i'll add roles to some objects. in my case the user object will be extended with Email module (the Email will turn into a role, being able to send emails).

    I pasted my code below but it's nicer here:

    # email a user when an event's time was changed.
    # features
    # 1. user can add events (each event has a known url).
    # 2. every morning if any event's time was changed,
    # the user gets email with the changed events.
    # 3. on Monday morning, user gets email of all his events
    # regardless of changes in times.

    # holds a user and it's events
    class User
    attr_accessor :events, :name

    def initialize name
    @name = name
    @events = nil
    @updated = false

    # ask each event to get it's current time and
    # mark the user as 'updated' so later on we
    # will email him/her
    def get_time
    @updated = @events.any?{|e| e.get_time; e.updated?}

    def updated?

    # holds info about a event
    class Event
    attr_accessor :name, :url, :base_time

    def initialize name, url, base_time
    @name = name
    @url = url
    @base_time = base_time
    @current_time = nil
    @updated = false

    # scrape event from the website and mark it
    # as 'updated' if it's time was modified
    def get_time
    current_time = fetch_current_time
    @current_current_time = current_time if current_time
    if current_time != @base_time
    @updated = true

    def updated?


    # get current event time from the website
    def fetch_current_time


    # send email to a user
    module Email

    def send user
    # if it's Monday send email with all events
    # else send email with only the updated events
    puts "sending email to #{}"

    user ='josh')
    party ='party', '', 1234234)
    movie ='movie', 'http://events/events/564', 1243133) = [party, movie]


    if user.updated? or
    josh, Dec 24, 2011
    1. Advertisements

  2. With the current logic a module or class of its own seems overkill.
    However, if you need to do formatting etc. to send out an email a
    separate class might be appropriate. I'd probably construct an instance
    of Email for every User that is to receive an email, set all necessary
    properties (address, changed events) and then have a single method for
    sending (btw. #send might not be a good name because of Object#send).
    Frankly, I believe your application - which really seems to be a single
    use case only - is not complex enough to think about how to bring DCI
    into this. I have to admit, I just glanced over the text at because I did not find it
    well structured, but the main point seems to be that functionality is
    not attached to individual classes but rather use cases and multiple
    classes. I believe this is what is called "multiple dispatch" elsewhere
    (Common Lisp has it IIRC).
    There's one thing I'd change about your code: change @updated in Event
    from a boolean flag to the point in Time of last update (or throw it
    away since @base_time might have the proper semantics already) and that
    in User to point in time of last check. This has a few advantages

    - The email sending is a read only operation for Events (the flag does
    not need to be changed any more) and modification is restricted to
    methods which actually modify an Event.
    - If there are multiple clients interested in checking whether an Event
    has changed the code will break with a flag only (for example, you might
    want to send an administrator a single email per day with all changed
    events for monitoring or debugging purposes)
    - You can devise other action schemes (e.g. order Events in the email by
    modification time).

    Also, get rid of @current_time - this is what is for. This
    again makes querying a read only operation while currently you need to
    update @current_time.

    Kind regards

    Robert Klemme, Dec 25, 2011
    1. Advertisements

  3. josh

    josh Guest

    Thanks for your insights Robert.

    Here is another question - should I pass the user object as argument
    of method that belong to email object or is it better to pass an email
    object into a method that belong to the user object?
    josh, Dec 26, 2011
  4. I'd probably do

    class User
    def send_updates(after = @updated)
    evts = {|e| e.updated >= after}

    unless evts.empty?
    em =
    em.user = self = evts
    em.whatever_else = 123
    @updated =

    You could as well also do this externally from User, e.g.

    class UpdateEmail
    attr_accessor :user, :events

    def self.send_updates(user)
    em = new
    em.user = user = user.updated_events
    em.whatever_else = 123

    UpdateEmail.send_updates a_user

    Kind regards

    Robert Klemme, Dec 26, 2011
  5. josh

    josh Guest


    I think i like the second option better.
    maybe I feel that the user should not be responsible for sending
    I am curious if there is any advantage regarding testing it.
    I believe that in both cases I would stub the Email.send_email method.

    also, is it ok to send an email object to User.send_updates (the first
    option) instead of creating the object inside the method?
    not sure which one is considered better. it's just a few thoughts I
    always have when designing an app.
    josh, Dec 27, 2011
  6. Thank you!
    I tend to prefer the first option - probably because the instance of
    Email needs so much information that User does not necessarily have to
    exhibit publicly. I'd probably have to muse about it a bit more.

    Btw., I'd do slight changes:

    class User
    def send_updates(after = @updated)
    evts = {|e| e.updated >= after}
    now = # reduce risk of race conditions

    unless evts.empty?
    em =
    em.user = self # or set specific fields like mail_addr = evts
    em.whatever_else = 123


    # only set if sending was successful:
    @updated = now

    not evts.empty?
    For testing you could override the method in Email that does delivery.
    Or you have an instance of a separate class for email delivery which
    then can be mocked.
    Yes, you could reuse an Email instance. But that is only worthwhile
    IMHO if the cost of creating an Email instance is significant. If not
    I'd keep it an implementation detail of User so users of User do not
    need to know.
    Yep, and these change over time together with requirements and
    experience - so there is no all time best solution. But there aren't
    silver bullets either... :)

    Kind regards

    Robert Klemme, Dec 27, 2011
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.