Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

Add required properties for events #21

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ spec/reports
test/tmp
test/version_tmp
tmp

# vim artifacts
*.swp
*.swo
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,25 @@ purposes; the exception is generated if `:retired_at` is set to _anything_.)
You can retire events, categories, and entire versions; this system ensures the DSL continues to be a historical record
of what things were in the past, as well as what they are today.

### Requiring Properties

Sometimes it is helpful to enforce the presence of certain properties. For example, imagine you are tracking views of
a set of banners that advertise a set of new features. In order to determine which banners (and therefore features) are
driving conversions, you need to be sure to include `:banner_id` and `:banner_size`. If there are many places in the
code that can call this event, you may forget to pass along these properties. With `:required_properties` an exception
is generated if the keys are not present _or if their values are blank_.

```ruby
global_events_prefix :ab

version 1, "2014-02-04" do
category :user do
event :viewed_banner, "2014-02-04", "user creates a brand-new account", :required_properties => [ :banner_id, :banner_size ]
end
end
```


### Adding Notes to Events

You can also add notes to events. They must be tagged with the author and the time, and they can be very useful for
Expand Down
1 change: 1 addition & 0 deletions lib/meta_events/definition/definition_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Definition
class DefinitionSet
class BaseError < StandardError; end
class RetiredEventError < BaseError; end
class RequiredPropertyMissingError < BaseError; end

class << self
# Creates an MetaEvents::Definition::DefinitionSet. +source+ can be one of:
Expand Down
23 changes: 21 additions & 2 deletions lib/meta_events/definition/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ def initialize(category, name, *args, &block)
# provide for required properties, property validation, or anything else.
def validate!(properties)
if retired_at
raise ::MetaEvents::Definition::DefinitionSet::RetiredEventError, "Event #{full_name} was retired at #{retired_at.inspect} (or its category or version was); you can't use it any longer."
raise ::MetaEvents::Definition::DefinitionSet::RetiredEventError,
"Event #{full_name} was retired at #{retired_at.inspect} (or its category or version was); you can't use it any longer."
end
validate_properties!(properties)
end

# Returns, or sets, the description for an event.
Expand Down Expand Up @@ -142,17 +144,34 @@ def ensure_complete!
raise ArgumentError, "You must record when you introduced event #{full_name}, either as an argument, in the options, or using 'introduced'" if (! @introduced)
end

def validate_properties!(properties)
properties_with_indifferent_access = properties.with_indifferent_access
if @required_properties
missing_properties = [ ]
@required_properties.each do |required_property|
if properties_with_indifferent_access[required_property].blank?
missing_properties << required_property
end
end
unless missing_properties.empty?
raise ::MetaEvents::Definition::DefinitionSet::RequiredPropertyMissingError,
"Event #{full_name} requires the properties #{@required_properties.join(', ')}. #{missing_properties.join(', ')} were missing or had blank values."
end
end
end

# Called with the set of options (which can be empty) supplied in the constructor; responsible for applying those
# to the object properly.
def apply_options!(options)
options.assert_valid_keys(:introduced, :desc, :description, :retired_at, :external_name)
options.assert_valid_keys(:introduced, :desc, :description, :retired_at, :external_name, :required_properties)

introduced options[:introduced] if options[:introduced]
desc options[:desc] if options[:desc]
desc options[:description] if options[:description]
external_name options[:external_name] if options[:external_name]

@retired_at = Time.parse(options[:retired_at]) if options[:retired_at]
@required_properties = Array(options[:required_properties]) if options[:required_properties]
end

# Called with the arguments (past the category and event name) supplied to the constructor; responsible for
Expand Down
2 changes: 1 addition & 1 deletion meta_events.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "json", "~> 1.0"
spec.add_dependency "activesupport", ">= 3.0"

spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "bundler", "~> 1.11.2"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "~> 2.14"
end
18 changes: 18 additions & 0 deletions spec/meta_events/definition/event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@
allow(category).to receive(:retired_at).and_return(Time.parse("2013-02-01"))
expect { instance.validate!(:foo => :bar) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RetiredEventError, /2013/)
end

it "should fail if required properties are missing" do
expect { klass.new(category, :foo, "2016-1-1", "foobar", :required_properties => [ :foo ]).validate!(:baz => :bar) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RequiredPropertyMissingError, /foo/)
end

it "should fail if required properties have blank values" do
expect { klass.new(category, :foo, "2016-1-1", "foobar", :required_properties => [ :foo ]).validate!(:foo => '') }.to raise_error(::MetaEvents::Definition::DefinitionSet::RequiredPropertyMissingError, /foo/)
end
end

it "should return and allow setting its description via #desc" do
Expand Down Expand Up @@ -143,5 +151,15 @@
expect(instance.external_name).to eq("my name")
end
end

context "with required properties" do
it "should work with strings and symbols" do
expect do
event = klass.new(category, :foo, "2016-1-1", "foobar", :required_properties => [ "string", :symbol ])
event.validate!('string' => "foo", :symbol => "foo")
event.validate!(:string => "foo", "symbol" => "foo")
end.to_not raise_error(::MetaEvents::Definition::DefinitionSet::RequiredPropertyMissingError)
end
end
end
end