schizo 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp
6
+ doc
7
+ vendor
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ 0.1.1
2
+ * Updated gemspec info
3
+
4
+ 0.1.0
5
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dci.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,175 @@
1
+ == Schizo {<img src="https://secure.travis-ci.org/cjbottaro/schizo.png" />}[http://travis-ci.org/cjbottaro/schizo]
2
+
3
+ Schizo is a libary that aids in using DCI (data, context and interaction) in Ruby/Rails projects. It aims
4
+ to overcome some of the shortcomings of using plain <tt>Object#extend</tt>, namely the issue that extending
5
+ a role can permenantly alter a class.
6
+
7
+ == Quickstart
8
+
9
+ Dive in...
10
+
11
+ class User < ActiveRecord::Base
12
+ include Schizo::Data
13
+ end
14
+
15
+ module Poster
16
+ extend Schizo::Role
17
+
18
+ extended do
19
+ has_many :posts
20
+ end
21
+
22
+ def post_count_in_english
23
+ "#{name} has #{posts.count} post(s)"
24
+ end
25
+ end
26
+
27
+ user = User.find(1)
28
+ user.as(Poster) do |poster|
29
+ poster.respond_to?(:posts) # => true
30
+ user.respond_to?(:posts) # => false
31
+
32
+ poster.respond_to?(:post_count_in_english) # => true
33
+ user.respond_to?(:post_count_in_english) # => false
34
+
35
+ poster.kind_of?(User) # => true
36
+ poster.instance_of?(User) # => true
37
+
38
+ poster.class.name # => "User"
39
+ end
40
+
41
+ == DCI
42
+
43
+ {\http://en.wikipedia.org/wiki/Data,_context_and_interaction}[http://en.wikipedia.org/wiki/Data,_context_and_interaction]
44
+
45
+ {\http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby}[http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby]
46
+
47
+ {\http://saturnflyer.com/blog/jim/2011/10/04/oop-dci-and-ruby-what-your-system-is-vs-what-your-system-does/}[http://saturnflyer.com/blog/jim/2011/10/04/oop-dci-and-ruby-what-your-system-is-vs-what-your-system-does/]
48
+
49
+ {\http://victorsavkin.com/post/13966712168/dci-in-ruby}[http://victorsavkin.com/post/13966712168/dci-in-ruby]
50
+
51
+ {\http://andrzejonsoftware.blogspot.com/2011/02/dci-and-rails.html}[http://andrzejonsoftware.blogspot.com/2011/02/dci-and-rails.html]
52
+
53
+ == The Problem
54
+
55
+ So what's wrong with just using <tt>Object#extend</tt>? Nothing, until you want to alter an instance's class
56
+ as a side effect of adorning it with a role... which happens often when using ActiveRecord.
57
+
58
+ Consider the following use of DCI and ActiveRecord with plain old <tt>Object#extend</tt>:
59
+
60
+ class User < ActiveRecord::Base
61
+ end
62
+
63
+ module Poster
64
+ def self.extended(object)
65
+ object.class.class_eval do
66
+ has_many :posts
67
+ end
68
+ end
69
+
70
+ def post_count_in_english
71
+ "#{name} has #{posts.count} post(s)"
72
+ end
73
+ end
74
+
75
+ user1 = User.find(1)
76
+ user1.extend(Poster)
77
+ user1.respond_to?(:posts) # Ok
78
+
79
+ user2 = User.find(2)
80
+ user2.respond_to?(:posts) # Oops, extending user1 ended up changing *all* users!
81
+
82
+ That goes against the core concept in DCI that your data should only be injected with behavior for a specific
83
+ context.
84
+
85
+ == The Magic
86
+
87
+ So how does Schizo work? It creates <i>facade classes</i> and <i>facade objects</i> that stand in for the
88
+ classes and objects you really want. The facades try to quack as best they can like the real objects/classes.
89
+
90
+ This is easier to explain in an example (continuing from the Quickstart example):
91
+
92
+ user = User.find(1)
93
+ user.as(Poster) do |poster|
94
+ poster.kind_of?(User) # => true
95
+ poster.instance_of?(User) # => true
96
+
97
+ poster.class.name # => "User"
98
+ poster.class # => Schizo::Facades::User::Poster
99
+ end
100
+
101
+ <tt>Schizo::Facades::User::Poster</tt> inherits from +User+, that's why <tt>poster.kind_of?(User)</tt> works natrually.
102
+ <tt>poster.instance_of?(User)</tt> works because of the facade consciously trying to quack like +User+.
103
+
104
+ == Facades and Objects
105
+
106
+ So knowing you're working with a facade instead of the original object, some of the gotchas become obvious.
107
+
108
+ class Foo
109
+ include Schizo::Data
110
+ attr_reader :bar
111
+ def initialize
112
+ @bar = "low"
113
+ end
114
+ end
115
+
116
+ module Baz
117
+ extend Schizo::Role
118
+ def set_bar(value)
119
+ @bar = value
120
+ end
121
+ end
122
+
123
+ foo = Foo.new
124
+ baz = foo.as(Baz)
125
+ baz.set_bar("high")
126
+ baz.bar # => "high"
127
+ foo.bar # => "low"
128
+
129
+ Makes perfect sense, right? But what about this...
130
+
131
+ foo = Foo.new
132
+ foo.as(Baz) do |baz|
133
+ baz.set_bar("high")
134
+ end
135
+ foo.bar # => "high"
136
+
137
+ What?! Nah, it's really simple. At the end of the code block, +baz.actualize+ is called. All +#actualize+ does
138
+ is copy over the instances variables from the facade to the real object.
139
+
140
+ You can get the exact same affect by doing:
141
+
142
+ foo = Foo.new
143
+ baz = foo.as(Baz)
144
+ baz.set_bar("high")
145
+ baz.actualize
146
+ foo.bar # => "high"
147
+
148
+ Hmm, maybe +#actualize+ should be renamed +#converge+... what do you think?
149
+
150
+ == Multiple Roles and Nesting
151
+
152
+ You can adorn a data object with more than one role...
153
+
154
+ poster = User.new.as(Poster)
155
+ commenter = poster.as(Commenter) # Has all the methods of a Commenter AND Poster
156
+
157
+ Alternatively...
158
+
159
+ User.new.as(Poster) do |poster|
160
+ poster.as(Commenter) do |commenter|
161
+ # Has all the methods of a Commenter AND Poster
162
+ end
163
+ end
164
+
165
+ == Documentation
166
+
167
+ {\http://doc.stochasticbytes.com/schizo/index.html}[http://doc.stochasticbytes.com/schizo/index.html]
168
+
169
+ == Contact
170
+
171
+ {@cjbottaro}[http://twitter.com/cjbottaro]
172
+
173
+ == Liscense
174
+
175
+ MIT or something
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'active_record'
4
+ require 'yaml'
5
+ require 'logger'
6
+ require 'rspec/core/rake_task'
7
+
8
+ ENV["RAILS_ENV"] ||= "test"
9
+
10
+ task :default => :spec
11
+
12
+ desc "Run specs (default)"
13
+ RSpec::Core::RakeTask.new(:spec => :migrate) do |t|
14
+ t.rspec_opts = "--color -f d"
15
+ end
16
+
17
+ desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
18
+ task :migrate => :environment do
19
+ ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil )
20
+ end
21
+
22
+ task :environment do
23
+ FileUtils.mkdir_p("tmp")
24
+ ActiveRecord::Base.establish_connection(YAML::load(File.open('db/database.yml')))
25
+ ActiveRecord::Base.logger = Logger.new(File.open('tmp/database.log', 'a'))
26
+ end
27
+
data/db/database.yml ADDED
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: "tmp/test.db"
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ class CreatePosts < ActiveRecord::Migration
2
+ def change
3
+ create_table :posts do |t|
4
+ t.integer :user_id
5
+ t.string :title
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ module Schizo
2
+
3
+ # Include Data into a class to give it the ability to be adorned with a Role.
4
+ module Data
5
+
6
+ # Adorn a Data object with a Role.
7
+ #
8
+ # If a block is given, then a facade for _self_ is yielded to the block and the return value
9
+ # is _self_ with the facade's instance variables copied over to it.
10
+ # user.name = "callie"
11
+ # user.as(Poster) do |poster|
12
+ # poster.name = "coco"
13
+ # end
14
+ # user.name # => "coco"
15
+ #
16
+ # Without a block, a facade for _self_ is returned.
17
+ # user.name = "callie"
18
+ # poster = user.as(Poster)
19
+ # poster.name = "coco"
20
+ # user.name # => "callie"
21
+ def as(role, &block)
22
+ facade = Facade::ObjectBuilder.new(self, role).product
23
+ if block_given?
24
+ block.call(facade)
25
+ facade.actualize
26
+ else
27
+ facade
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,44 @@
1
+ module Schizo
2
+ # Rdoc won't let me :nodoc: this module. Nothing to see here.
3
+ module Facade
4
+
5
+ # This module is included in each Data object facade's class.
6
+ module Base
7
+
8
+ def self.included(mod) #:nodoc:
9
+ mod.extend(ClassMethods)
10
+ mod.class_eval <<-STR
11
+ attr_reader :#{DCI_INSTANCE_VARIABLE.to_s[1..-1]}
12
+ STR
13
+ end
14
+
15
+ module ClassMethods #:nodoc:
16
+
17
+ def name
18
+ superclass.name || "AnonClass#{object_id}"
19
+ end
20
+
21
+ end
22
+
23
+ def initialize(object, role) #:nodoc:
24
+ instance_variable_set(DCI_INSTANCE_VARIABLE, Struct.new(:object, :role).new(object, role))
25
+ end
26
+
27
+ def instance_of?(klass) #:nodoc:
28
+ # This is to get it working with nested facades. We need to traverse
29
+ # superclasses until we get to the first real (non facade) superclass.
30
+ my_superclass = self.class.superclass
31
+ while my_superclass.ancestors.include?(Base) and my_superclass
32
+ my_superclass = my_superclass.superclass
33
+ end
34
+ my_superclass == klass
35
+ end
36
+
37
+ # Copy a facade's instance variables to the object it's facading for.
38
+ def actualize
39
+ dci.object.tap{ |object| Schizo::Facade.copy_instance_variables(self, object) }
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ module Schizo
2
+ module Facade #:nodoc:
3
+ class ClassBuilder #:nodoc:
4
+
5
+ attr_reader :base, :role
6
+
7
+ def initialize(base, role)
8
+ @base, @role = base, role
9
+ end
10
+
11
+ def product
12
+ @product ||= role_class
13
+ end
14
+
15
+ private
16
+
17
+ def module_name
18
+ base.name || "AnonClass#{base.object_id}"
19
+ end
20
+
21
+ def role_name
22
+ role.name || "AnonRole#{role.object_id}"
23
+ end
24
+
25
+ def container_module
26
+ @container_module ||= begin
27
+ if Schizo::Facades.const_defined?(module_name, false)
28
+ Schizo::Facades.const_get(module_name)
29
+ else
30
+ Schizo::Facades.const_set(module_name, Module.new)
31
+ end
32
+ end
33
+ end
34
+
35
+ def role_class
36
+ @role_class ||= begin
37
+ if container_module.const_defined?(role_name, false)
38
+ container_module.const_get(role_name)
39
+ else
40
+ container_module.const_set(role_name, build)
41
+ end
42
+ end
43
+ end
44
+
45
+ def build
46
+ Class.new(base){ include Base }.tap do |klass|
47
+ klass.class_eval(&role.extended_block) if role.extended_block
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ module Schizo
2
+ module Facade #:nodoc:
3
+
4
+ DCI_INSTANCE_VARIABLE = :@dci
5
+
6
+ def self.copy_instance_variables(from, to)
7
+ from.instance_variables.each do |ivar_name|
8
+ ivar = from.instance_variable_get(ivar_name)
9
+ to.instance_variable_set(ivar_name, ivar) unless ivar_name == DCI_INSTANCE_VARIABLE
10
+ end
11
+ end
12
+
13
+ class ObjectBuilder #:nodoc:
14
+ attr_reader :object, :role
15
+
16
+ def initialize(object, role)
17
+ @object, @role = object, role
18
+ end
19
+
20
+ def product
21
+ @product ||= build
22
+ end
23
+
24
+ private
25
+
26
+ def facade_class
27
+ @facade_class ||= ClassBuilder.new(object.class, role).product
28
+ end
29
+
30
+ def build
31
+ facade_class.new(object, role).tap do |facade|
32
+
33
+ # This is to get nesting to work. Because each facade keeps a reference to the object
34
+ # it's facading for, we can traverse nested facades. Each time we find one, we extend
35
+ # its role into facade we're building.
36
+ previous_facade = object
37
+ while previous_facade.respond_to?(:dci)
38
+ facade.extend(previous_facade.dci.role)
39
+ previous_facade = previous_facade.dci.object
40
+ end
41
+
42
+ facade.extend(role)
43
+ Schizo::Facade.copy_instance_variables(object, facade)
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,9 @@
1
+ require "schizo/facade/base"
2
+ require "schizo/facade/class_builder"
3
+ require "schizo/facade/object_builder"
4
+
5
+ module Schizo
6
+ # This module solely exists to store facade classes.
7
+ module Facades #:nodoc:
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ module Schizo
2
+
3
+ # Extend a module with Role to make that module a _role_.
4
+ # module RoleA
5
+ # extend Schizo::Role
6
+ #
7
+ # def method_a
8
+ # end
9
+ #
10
+ # def method_b
11
+ # end
12
+ # end
13
+ module Role
14
+
15
+ # call-seq:
16
+ # extended{ ... }
17
+ #
18
+ # Call this method with a block and that block will be evaled by the Data object's class when
19
+ # the Data object is adorned with this role.
20
+ # module Poster
21
+ # extend Schizo::Role
22
+ #
23
+ # extended do
24
+ # has_many :posts
25
+ # end
26
+ # end
27
+ #
28
+ # User.new.as(Poster).do |poster|
29
+ # poster.posts.create :title => "My first post!"
30
+ # end
31
+ def extended(object = nil, &block)
32
+ if block_given?
33
+ @extended_block = block
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def extended_block #:nodoc:
40
+ @extended_block
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Schizo #:nodoc:
2
+ VERSION = "0.1.1"
3
+ end
data/lib/schizo.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "schizo/data"
2
+ require "schizo/role"
3
+ require "schizo/version"
4
+ require "schizo/facade"
5
+
6
+ # The top level module; acts as a namespace.
7
+ module Schizo
8
+ end
data/schizo.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "schizo/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "schizo"
7
+ s.version = Schizo::VERSION
8
+ s.authors = ["Christopher J. Bottaro"]
9
+ s.email = ["cjbottaro@alumni.cs.utexas.edu"]
10
+ s.homepage = "https://github.com/cjbottaro/schizo"
11
+ s.summary = %q{DCI (data, context and interaction) for Ruby / Rails / ActiveRecord}
12
+ s.description = %q{DCI (data, context and interaction) for Ruby / Rails / ActiveRecord}
13
+
14
+ s.rubyforge_project = "schizo"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "activerecord"
24
+ s.add_development_dependency "sqlite3"
25
+ s.add_development_dependency "rdoc"
26
+ s.add_development_dependency "pry"
27
+ s.add_development_dependency "rake"
28
+ # s.add_runtime_dependency "rest-client"
29
+ end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+
3
+ describe "an ActiveRecord instance" do
4
+ context "adorned with a role" do
5
+
6
+ let(:role) do
7
+ Module.new do
8
+ extend Schizo::Role
9
+ def set_name(name)
10
+ self.name = name
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:user){ User.new :name => "christopher" }
16
+
17
+ let(:adorned_user){ user.as(role) }
18
+
19
+ it "setting attributes should affect the original instance because they aren't duped" do
20
+ adorned_user.set_name("callie")
21
+ adorned_user.should be_name_changed
22
+ adorned_user.name.should == "callie"
23
+ user.name.should == "callie"
24
+ user.should be_name_changed
25
+ end
26
+
27
+ context "saving" do
28
+
29
+ before(:all){ adorned_user.save }
30
+
31
+ it "does not affect the original instance's status as a new record" do
32
+ adorned_user.should_not be_new_record
33
+ user.should be_new_record
34
+ end
35
+
36
+ it "unless #actualize is called" do
37
+ adorned_user.actualize
38
+ user.should_not be_new_record
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+
3
+ describe "an ActiveRecord instance" do
4
+ context "extended with a role that has_many :posts" do
5
+
6
+ let(:user){ User.create! :name => "chris" }
7
+
8
+ let(:role) do
9
+ Module.new do
10
+ extend Schizo::Role
11
+ extended do
12
+ has_many :posts
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:adorned_user){ user.as(role) }
18
+
19
+ it "responds to #posts" do
20
+ adorned_user.should respond_to(:posts)
21
+ user.should_not respond_to(:posts)
22
+ end
23
+
24
+ context "#posts" do
25
+
26
+ it "works with #<<" do
27
+ post = Post.new :title => "first"
28
+ post.should be_a_new_record
29
+ adorned_user.posts << post
30
+ post.should_not be_a_new_record
31
+ adorned_user.posts.count.should == 1
32
+ end
33
+
34
+ it "works with #build" do
35
+ post = adorned_user.posts.build :title => "second"
36
+ post.should be_a_new_record
37
+ adorned_user.save!
38
+ post.should_not be_a_new_record
39
+ adorned_user.posts.find(post.id).should == post
40
+ end
41
+
42
+ it "works with #create" do
43
+ post = adorned_user.posts.create :title => "third"
44
+ post.should_not be_a_new_record
45
+ adorned_user.posts.find(post.id).should == post
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+ require "schizo/facade"
3
+
4
+ module Schizo
5
+ module Facade
6
+
7
+ describe ClassBuilder do
8
+
9
+ let(:base) do
10
+ Class.new do
11
+ def self.name; "Foo"; end
12
+ end
13
+ end
14
+
15
+ let(:role) do
16
+ Module.new do
17
+ extend Role
18
+ extended do
19
+ @test_var = 1
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:builder) do
25
+ ClassBuilder.new(base, role)
26
+ end
27
+
28
+ context "#initialize" do
29
+
30
+ it "sets base and role" do
31
+ builder.base.should == base
32
+ builder.role.should == role
33
+ end
34
+
35
+ end
36
+
37
+ context "#product" do
38
+
39
+ it "returns a facade class" do
40
+ builder.product.tap do |facade|
41
+ facade.should be_a(Class)
42
+ facade.ancestors.should include(base)
43
+ facade.ancestors.should include(Base)
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ module Schizo
4
+ module Facade
5
+
6
+ describe "a facade class" do
7
+
8
+ let(:base) do
9
+ Class.new do
10
+ def self.name; "Foo"; end
11
+ end
12
+ end
13
+
14
+ let(:role) do
15
+ Module.new do
16
+ extend Role
17
+ extended do
18
+ @test_var = 1
19
+ end
20
+ end
21
+ end
22
+
23
+ let(:builder) do
24
+ ClassBuilder.new(base, role)
25
+ end
26
+
27
+ let(:facade) do
28
+ builder.product
29
+ end
30
+
31
+ it "has the same #name as its superclass" do
32
+ facade.name.should == "Foo"
33
+ base.name.should == "Foo"
34
+ end
35
+
36
+ it "#initialize has arity == 2" do
37
+ base.instance_method(:initialize).arity.should < 2
38
+ facade.instance_method(:initialize).arity.should == 2
39
+ end
40
+
41
+ it "has class evaled the extended block of the role" do
42
+ facade.should be_instance_variable_defined(:@test_var)
43
+ end
44
+
45
+ it "is defined in Schizo::Facades" do
46
+ facade.should == Schizo::Facades.const_get(base.name).const_get("AnonRole#{role.object_id}")
47
+ end
48
+
49
+ it "is a singleton" do
50
+ facade.object_id.should == ClassBuilder.new(base, role).product.object_id
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+
3
+ module Schizo
4
+ module Facade
5
+
6
+ describe(ObjectBuilder) do
7
+
8
+ let(:base) do
9
+ Class.new do
10
+ end
11
+ end
12
+
13
+ let(:role) do
14
+ Module.new do
15
+ extend Role
16
+ def bar(v)
17
+ @bar = v
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:object) do
23
+ base.new
24
+ end
25
+
26
+ let(:builder) do
27
+ ObjectBuilder.new(object, role)
28
+ end
29
+
30
+ context "#initialize" do
31
+
32
+ it "sets object and role" do
33
+ builder.object.should == object
34
+ builder.role.should == role
35
+ end
36
+
37
+ end
38
+
39
+ context "#product" do
40
+
41
+ it "returns a facade object" do
42
+ builder.product.should be_a(base)
43
+ builder.product.should be_instance_of(base)
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ module Schizo
4
+ module Facade
5
+
6
+ describe "a facade object" do
7
+
8
+ let(:base) do
9
+ Class.new do
10
+ end
11
+ end
12
+
13
+ let(:role) do
14
+ Module.new do
15
+ extend Role
16
+ def bar(v)
17
+ @bar = v
18
+ end
19
+ end
20
+ end
21
+
22
+ let(:object) do
23
+ base.new
24
+ end
25
+
26
+ let(:builder) do
27
+ ObjectBuilder.new(object, role)
28
+ end
29
+
30
+ let(:facade) do
31
+ builder.product
32
+ end
33
+
34
+ it "is not the same as the original object" do
35
+ facade.should_not == object
36
+ end
37
+
38
+ it "responds to methods defined in its role" do
39
+ facade.should respond_to(:bar)
40
+ end
41
+
42
+ it "calling methods should not affect original object" do
43
+ facade.bar("test")
44
+ facade.instance_variable_get(:@bar).should == "test"
45
+ object.instance_variable_get(:@bar).should be_nil
46
+ end
47
+
48
+ context "#actualize" do
49
+
50
+ before(:all) do
51
+ facade.bar("blah")
52
+ facade.actualize
53
+ end
54
+
55
+ it "returns the original object" do
56
+ facade.actualize.should == object
57
+ end
58
+
59
+ it "sets instance variables in the original object" do
60
+ object.instance_variable_get(:@bar).should == "blah"
61
+ end
62
+
63
+ it "does not set any internal dci instance variables" do
64
+ object.instance_variable_defined?(Schizo::Facade::DCI_INSTANCE_VARIABLE).should be_false
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,105 @@
1
+ require "spec_helper"
2
+
3
+ describe "a nested facade for ClassA with RoleA, RoleB and RoleC" do
4
+
5
+ let(:class_a) do
6
+ Class.new do
7
+ include Schizo::Data
8
+ attr_reader :foo, :bar, :baz
9
+ end
10
+ end
11
+
12
+ let(:role_a) do
13
+ Module.new do
14
+ extend Schizo::Role
15
+ def set_foo; @foo = "foo"; end
16
+ end
17
+ end
18
+
19
+ let(:role_b) do
20
+ Module.new do
21
+ extend Schizo::Role
22
+ def set_bar; @bar = "bar"; end
23
+ end
24
+ end
25
+
26
+ let(:role_c) do
27
+ Module.new do
28
+ extend Schizo::Role
29
+ def set_baz; @baz = "baz"; end
30
+ end
31
+ end
32
+
33
+ let(:object) do
34
+ class_a.new
35
+ end
36
+
37
+ let(:facade_a) do
38
+ object.as(role_a)
39
+ end
40
+
41
+ let(:facade_b) do
42
+ facade_a.as(role_b)
43
+ end
44
+
45
+ let(:facade_c) do
46
+ facade_b.as(role_c)
47
+ end
48
+
49
+ it "#instance_of?(ClassA) is true" do
50
+ facade_c.should be_instance_of(class_a)
51
+ end
52
+
53
+ it "#kind_of?(ClassA) is true" do
54
+ facade_c.should be_kind_of(class_a)
55
+ end
56
+
57
+ it "should have methods from RoleA, RoleB and RoleC" do
58
+ facade_a.should respond_to(:set_foo)
59
+ facade_a.should_not respond_to(:set_bar)
60
+ facade_a.should_not respond_to(:set_baz)
61
+
62
+ facade_b.should respond_to(:set_foo)
63
+ facade_b.should respond_to(:set_bar)
64
+ facade_a.should_not respond_to(:set_baz)
65
+
66
+ facade_c.should respond_to(:set_foo)
67
+ facade_c.should respond_to(:set_bar)
68
+ facade_c.should respond_to(:set_baz)
69
+ end
70
+
71
+ it "#actualize should walk up the chain" do
72
+ facade_c.set_foo
73
+ facade_c.set_bar
74
+ facade_c.set_baz
75
+
76
+ facade_c.foo.should == "foo"
77
+ facade_c.bar.should == "bar"
78
+ facade_c.baz.should == "baz"
79
+
80
+ facade_b.foo.should be_nil
81
+ facade_b.bar.should be_nil
82
+ facade_b.baz.should be_nil
83
+
84
+ facade_a.foo.should be_nil
85
+ facade_a.bar.should be_nil
86
+ facade_a.baz.should be_nil
87
+
88
+ facade_c.actualize
89
+
90
+ facade_b.foo.should == "foo"
91
+ facade_b.bar.should == "bar"
92
+ facade_b.baz.should == "baz"
93
+
94
+ facade_a.foo.should be_nil
95
+ facade_a.bar.should be_nil
96
+ facade_a.baz.should be_nil
97
+
98
+ facade_b.actualize
99
+
100
+ facade_a.foo.should == "foo"
101
+ facade_a.bar.should == "bar"
102
+ facade_a.baz.should == "baz"
103
+ end
104
+
105
+ end
@@ -0,0 +1,17 @@
1
+ require "schizo"
2
+ require "active_record"
3
+ require "pry"
4
+
5
+ ActiveRecord::Base.establish_connection(YAML::load(File.open('db/database.yml')))
6
+
7
+ class User < ActiveRecord::Base
8
+ include Schizo::Data
9
+ end
10
+
11
+ class Post < ActiveRecord::Base
12
+ include Schizo::Data
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ #config.mock_with :rr
17
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schizo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christopher J. Bottaro
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70202402544160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70202402544160
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &70202402543320 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70202402543320
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ requirement: &70202402542560 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70202402542560
47
+ - !ruby/object:Gem::Dependency
48
+ name: rdoc
49
+ requirement: &70202402541760 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70202402541760
58
+ - !ruby/object:Gem::Dependency
59
+ name: pry
60
+ requirement: &70202402706420 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70202402706420
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: &70202402705740 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70202402705740
80
+ description: DCI (data, context and interaction) for Ruby / Rails / ActiveRecord
81
+ email:
82
+ - cjbottaro@alumni.cs.utexas.edu
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - .travis.yml
89
+ - CHANGELOG
90
+ - Gemfile
91
+ - README.rdoc
92
+ - Rakefile
93
+ - db/database.yml
94
+ - db/migrate/001_create_users.rb
95
+ - db/migrate/002_create_posts.rb
96
+ - lib/schizo.rb
97
+ - lib/schizo/data.rb
98
+ - lib/schizo/facade.rb
99
+ - lib/schizo/facade/base.rb
100
+ - lib/schizo/facade/class_builder.rb
101
+ - lib/schizo/facade/object_builder.rb
102
+ - lib/schizo/role.rb
103
+ - lib/schizo/version.rb
104
+ - schizo.gemspec
105
+ - spec/active_record/attributes_spec.rb
106
+ - spec/active_record/has_many_spec.rb
107
+ - spec/facade/class_builder_spec.rb
108
+ - spec/facade/class_spec.rb
109
+ - spec/facade/object_builder_spec.rb
110
+ - spec/facade/object_spec.rb
111
+ - spec/nested_spec.rb
112
+ - spec/spec_helper.rb
113
+ homepage: https://github.com/cjbottaro/schizo
114
+ licenses: []
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ segments:
126
+ - 0
127
+ hash: -910995716218782308
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ segments:
135
+ - 0
136
+ hash: -910995716218782308
137
+ requirements: []
138
+ rubyforge_project: schizo
139
+ rubygems_version: 1.8.11
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: DCI (data, context and interaction) for Ruby / Rails / ActiveRecord
143
+ test_files:
144
+ - spec/active_record/attributes_spec.rb
145
+ - spec/active_record/has_many_spec.rb
146
+ - spec/facade/class_builder_spec.rb
147
+ - spec/facade/class_spec.rb
148
+ - spec/facade/object_builder_spec.rb
149
+ - spec/facade/object_spec.rb
150
+ - spec/nested_spec.rb
151
+ - spec/spec_helper.rb