Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rewrite. Package as a gem.

  • Loading branch information...
commit e9ba8140b7677cace0110272e81d518ac808f3bb 1 parent fcd628e
@stevehodgkiss stevehodgkiss authored
View
4 .gitignore
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
View
1  .rspec
@@ -0,0 +1 @@
+--color
View
3  Gemfile
@@ -0,0 +1,3 @@
+source "http://rubygems.org"
+
+gemspec
View
6 Rakefile
@@ -0,0 +1,6 @@
+require 'bundler/gem_tasks'
+
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec)
+
+task :default => :spec
View
80 lib/modelling.rb
@@ -0,0 +1,80 @@
+require 'modelling/version'
+require 'ostruct'
+
+module Modelling
+
+ def self.included(receiver)
+ receiver.extend ClassMethods
+ end
+
+ module ClassMethods
+ # A descendent gets a copy of the super classes
+ # attributes when inheritence is used
+ def inherited(descendant)
+ descendant.instance_variable_set(:@members, members.dup)
+ super
+ end
+
+ def attributes(*args)
+ generate_accessors_from_args(args, Proc.new { nil })
+ end
+
+ def collections(*args)
+ generate_accessors_from_args(args, Proc.new { Array.new })
+ end
+
+ def maps(*args)
+ generate_accessors_from_args(args, Proc.new { Hash.new })
+ end
+
+ def structs(*args)
+ generate_accessors_from_args(args, Proc.new { OpenStruct.new })
+ end
+
+ def members
+ @members ||= {}
+ end
+
+ private
+
+ def generate_accessors_from_args(args, default_initializer)
+ names_to_initializer = args.last.is_a?(Hash) ? args.pop : {}
+ args.each do |name|
+ names_to_initializer[name] = default_initializer
+ end
+ generate_accessors(names_to_initializer)
+ end
+
+ def generate_accessors(names_to_initializer)
+ names_to_initializer.each do |name, initializer|
+ create_accessor(name)
+ if initializer.is_a?(Proc)
+ members[name] = initializer
+ else
+ members[name] = Proc.new { initializer.new }
+ end
+ end
+ end
+
+ def create_accessor(name)
+ instance_eval { attr_accessor name }
+ end
+
+ end
+
+ def members
+ self.class.members
+ end
+
+ def initialize(args = {})
+ members.each do |accessor, initializer|
+ if initializer.arity > 0
+ send "#{accessor}=", initializer.call(self)
+ else
+ send "#{accessor}=", initializer.call
+ end
+ end
+ args.each { |name, value| send "#{name}=", value }
+ end
+
+end
View
3  lib/modelling/version.rb
@@ -0,0 +1,3 @@
+module Modelling
+ VERSION = "0.0.1"
+end
View
24 modelling.gemspec
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path('../lib', __FILE__)
+require 'modelling/version'
+
+Gem::Specification.new do |s|
+ s.name = 'modelling'
+ s.version = Modelling::VERSION
+ s.authors = ['Ryan Allen', 'Steve Hodgkiss', 'Mark Turnley', 'John Barton']
+ s.email = ['ryan@eden.cc', 'steve@hodgkiss.me', 'ravagedcarrot@gmail.com', 'jrbarton@gmail.com']
+ s.homepage = ''
+ s.summary = %q{TODO: Write a gem summary}
+ s.description = %q{TODO: Write a gem description}
+
+ s.rubyforge_project = 'modelling'
+
+ s.files = `git ls-files`.split('\n')
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split('\n')
+ s.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) }
+ s.require_paths = ['lib']
+
+ # specify any dependencies here; for example:
+ # s.add_development_dependency 'rspec'
+ s.add_runtime_dependency 'rspec'
+end
View
87 modelling.rb
@@ -1,87 +0,0 @@
-require 'ostruct'
-
-module Modelling
-
- class << self
-
- def append_features(receiver)
- initialize_members_classvar(receiver)
- bind_attributes_meta_method(receiver)
- bind_maps_meta_method(receiver)
- bind_collection_meta_method(receiver)
- build_structs_meta_method(receiver)
- bind_default_constructor(receiver)
- end
-
- def generate_accessors(receiver, names, initializer)
- receiver.instance_eval do
- names.each do |name|
- attr_accessor name
- @@members[self][name] = initializer
- end
- end
- end
-
- def generate_adhoc_accessors(receiver, names_to_class_or_proc)
- receiver.instance_eval do
- names_to_class_or_proc.each do |name, class_or_proc|
- attr_accessor name
- if class_or_proc.is_a?(Proc)
- @@members[self][name] = class_or_proc
- else
- @@members[self][name] = Proc.new { class_or_proc.new }
- end
-
- end
- end
- end
-
- private
-
- def initialize_members_classvar(receiver)
- receiver.instance_eval do
- @@members ||= {}; @@members[self] ||= {}
- end
- end
-
- def bind_meta_method(receiver, name, initializer)
- receiver.instance_eval { class << self; self; end }.instance_eval do
- define_method name do |*args|
- if args.last.is_a?(Hash)
- Modelling.generate_adhoc_accessors(self, args.pop)
- end
- Modelling.generate_accessors(self, args, initializer)
- end
- end
- end
-
- def bind_attributes_meta_method(receiver)
- bind_meta_method(receiver, :attributes, Proc.new { nil })
- end
-
- def bind_collection_meta_method(receiver)
- bind_meta_method(receiver, :collections, Proc.new { Array.new })
- end
-
- def bind_maps_meta_method(receiver)
- bind_meta_method(receiver, :maps, Proc.new { Hash.new })
- end
-
- def build_structs_meta_method(receiver)
- bind_meta_method(receiver, :structs, Proc.new { OpenStruct.new })
- end
-
- def bind_default_constructor(receiver)
- receiver.class_eval do
- def initialize(args = {})
- @@members[self.class].each do |accessor, initializer|
- send "#{accessor}=", initializer.call(self)
- end
- args.each { |name, value| send "#{name}=", value }
- end
- end
- end
-
- end
-
-end
View
122 modelling_test.rb
@@ -1,122 +0,0 @@
-require 'test/unit'
-require 'modelling'
-
-# this raises an error if the class vars are not initialized by
-# default, we do this here before the unit tests below so we can
-# test for this case before other classes start using their class
-# level macros :) complete hack, but it fulfils the test criteria
-class BlankSlate; include Modelling; end
-BlankSlate.new
-
-class User
- include Modelling
- attributes :name, :age
- collections :fav_colours, :biggest_gripes
-end
-
-class MyArray < Array; end
-class MyHash < Hash; end
-
-class Car
- include Modelling
- attributes :name => Proc.new { |car| String.new(car.class.to_s) }
- collections :doors => MyArray
-end
-
-class Site
- include Modelling
- maps :users => MyHash
-end
-
-class Bike
- include Modelling
- attributes :manufacturer
- maps :stickers
- structs :features
-end
-
-class ModellingTest < Test::Unit::TestCase
-
- def test_can_has_domain_builder
- assert Modelling
- end
-
- def test_user_has_name
- user = User.new
- user.name = 'Ryan'
- assert_equal 'Ryan', user.name
- end
-
- def test_user_has_age
- user = User.new
- user.age = 24
- assert_equal 24, user.age
- end
-
- def test_user_has_fav_colours_collection
- user = User.new
- user.fav_colours = [:green, :yellow]
- assert_equal [:green, :yellow], user.fav_colours
- end
-
- def test_user_has_biggest_gripes_collection
- user = User.new
- user.biggest_gripes = [:mediocrity, :CUB_beer]
- assert_equal [:mediocrity, :CUB_beer], user.biggest_gripes
- end
-
- def test_attributes_are_initialized_as_nil
- user = User.new
- assert_nil user.name
- assert_nil user.age
- end
-
- def test_collections_are_initialized_as_empty_array
- user = User.new
- assert_equal [], user.fav_colours
- assert_equal [], user.biggest_gripes
- end
-
- def test_can_initialize_with_attributes
- user = User.new(:name => 'Ryan')
- assert_equal 'Ryan', user.name
- end
-
- def test_can_initialize_with_collections
- user = User.new(:biggest_gripes => [:mediocrity, :CUB_beer])
- assert_equal [:mediocrity, :CUB_beer], user.biggest_gripes
- end
-
- def test_car_has_doors_and_all_is_good
- car = Car.new
- assert_equal [], car.doors
- end
-
- def test_bike_does_not_bunk_on_having_no_collection
- bike = Bike.new(:manufacturer => 'Kawasaki')
- # if nothing is rased, we fixed the bug
- end
-
- def test_bike_has_map_of_stickers
- assert_equal({}, Bike.new.stickers)
- end
-
- def test_cars_doors_is_a_my_array
- assert_kind_of MyArray, Car.new.doors
- end
-
- def test_sites_users_is_a_my_hash
- assert_kind_of MyHash, Site.new.users
- end
-
- def test_can_initialize_with_proc_and_get_reference_to_new_instance
- car = Car.new
- assert_equal String.new(car.class.to_s), car.name
- end
-
- def test_bike_has_features
- bike = Bike.new
- assert_kind_of OpenStruct, bike.features
- end
-
-end
View
7 rakefile.rb
@@ -1,7 +0,0 @@
-task :default do
- Rake::Task['run_tests'].invoke
-end
-
-task :run_tests do
- require 'modelling_test'
-end
View
153 spec/modelling_spec.rb
@@ -0,0 +1,153 @@
+require 'bundler'
+Bundler.require
+$: << '.'
+require 'modelling'
+
+class User
+ include Modelling
+ attributes :name, :age
+ collections :fav_colours, :biggest_gripes, :test => lambda { 3 }
+end
+
+class NonSpecificUser < User
+end
+
+class MyArray < Array; end
+class MyHash < Hash; end
+
+class Car
+ include Modelling
+ attributes :name => Proc.new { |car| String.new(car.class.to_s) }
+ collections :doors => MyArray
+end
+
+class SuperCar < Car
+ attributes :bhp => Proc.new { 400 }
+end
+
+class Site
+ include Modelling
+ maps :users => MyHash
+end
+
+class Bike
+ include Modelling
+ attributes :manufacturer
+ maps :stickers
+ structs :features
+end
+
+class LambdaTest
+ include Modelling
+ attributes :lambda => lambda { "boo" }
+end
+
+describe Modelling do
+
+ specify 'user has name' do
+ user = User.new
+ user.name = 'Ryan'
+ user.name.should eq 'Ryan'
+ end
+
+ specify 'user has age' do
+ user = User.new
+ user.age = 24
+ user.age.should eq 24
+ end
+
+ specify 'user has fav colours collection' do
+ user = User.new
+ user.fav_colours = [:green, :yellow]
+ user.fav_colours.should eq [:green, :yellow]
+ end
+
+ specify 'user has biggest gripes collection' do
+ user = User.new
+ user.biggest_gripes = [:mediocrity, :CUB_beer]
+ user.biggest_gripes.should eq [:mediocrity, :CUB_beer]
+ end
+
+ specify 'attributes are initialized as nil' do
+ user = User.new
+ user.name.should be_nil
+ user.age.should be_nil
+ end
+
+ specify 'collections are initialized as empty array' do
+ user = User.new
+ user.fav_colours.should be_empty
+ user.biggest_gripes.should be_empty
+ end
+
+ it 'can initialize with attributes' do
+ user = User.new(:name => 'Ryan')
+ user.name.should eq 'Ryan'
+ end
+
+ it 'can initialize with collections' do
+ user = User.new(:biggest_gripes => [:mediocrity, :CUB_beer])
+ user.biggest_gripes.should eq [:mediocrity, :CUB_beer]
+ end
+
+ specify 'car has doors and all is good' do
+ car = Car.new
+ car.doors.should be_empty
+ end
+
+ specify 'bike does not bunk on having no collection' do
+ bike = Bike.new(:manufacturer => 'Kawasaki')
+ # if nothing is rased, we fixed the bug
+ end
+
+ specify 'bike has map of stickers' do
+ Bike.new.stickers.should be_empty
+ end
+
+ specify 'cars doors is a my array' do
+ Car.new.doors.should be_instance_of MyArray
+ end
+
+ specify 'sites users is a my hash' do
+ Site.new.users.should be_instance_of MyHash
+ end
+
+ it 'can initialize with proc and get reference to new instance' do
+ car = Car.new
+ car.name.should eq String.new(car.class.to_s)
+ end
+
+ specify 'bike has features' do
+ bike = Bike.new
+ bike.features.should be_instance_of OpenStruct
+ end
+
+ it 'doesnt fail when lambdas with no args are used' do
+ LambdaTest.new.lambda.should eq 'boo'
+ end
+
+ context 'inheritence' do
+ let(:car) { Car.new }
+ let(:super_car) { SuperCar.new }
+
+ it 'inherits attributes' do
+ super_car.doors.should be_instance_of MyArray
+ super_car.name.should eq "SuperCar"
+ end
+
+ it 'allows extra attributes' do
+ super_car.bhp.should eq 400
+ end
+
+ it 'doesnt mess up the super classes members' do
+ car.should_not respond_to(:bhp)
+ car.should respond_to(:doors)
+ car.should respond_to(:name)
+ end
+
+ specify 'non specific user inherits attributes' do
+ user = NonSpecificUser.new(:name => 'Ryan you cocktard')
+ user.name.should eq 'Ryan you cocktard'
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.