relevance-obsidian 0.0.5

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/History.txt ADDED
@@ -0,0 +1,11 @@
1
+ 2008-10-30
2
+ * Added Object#try
3
+ * Added meta extensions from Why
4
+
5
+ 2008-04-11
6
+ * Updated documentation in README
7
+
8
+ 2008-03-28
9
+ * Added model_update_tracker
10
+ * Added example rails applicaion in branch example
11
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2008 Relevance, Inc.
2
+
3
+ (The MIT License)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,23 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.rdoc
5
+ README.txt
6
+ Rakefile
7
+ lib/obsidian.rb
8
+ lib/obsidian/extensions/object.rb
9
+ lib/obsidian/extensions/try.rb
10
+ lib/obsidian/rails/helper_test.rb
11
+ lib/obsidian/rails/model_update_tracker.rb
12
+ lib/obsidian/spec.rb
13
+ lib/obsidian/spec/map_spec_helper.rb
14
+ lib/obsidian/spec/set_spec_helper.rb
15
+ obsidian.gemspec
16
+ test/obsidian_test.rb
17
+ test/test_helper.rb
18
+ test/units/obsidian/object_extensions_test.rb
19
+ test/units/obsidian/rails/model_update_tracker_test.rb
20
+ test/units/obsidian/spec/map_spec_helper_test.rb
21
+ test/units/obsidian/spec/set_spec_helper_test.rb
22
+ test/units/obsidian/spec_test.rb
23
+ test/units/obsidian/try_test.rb
data/README.rdoc ADDED
@@ -0,0 +1,96 @@
1
+ = Obsidian
2
+
3
+ == DESCRIPTION
4
+
5
+ Obsidian. It's metastable.
6
+
7
+ In the tangible world, obsidian is a naturally-occurring glass that is metastable at the earth's
8
+ surface. It's commonly found within the margins of rhyolitic lava flows, where cooling of the lava is
9
+ rapid. [1] In the slightly less tangible world of programming, <b>Obsidian</b> is home to chunks of
10
+ Ruby code that we've found helpful. These bits of code are often found at the bottom of a mountain of
11
+ yak hair, the occasionally-occurring product of simplifying various tasks.
12
+
13
+ 1 http://en.wikipedia.org/wiki/Obsidian
14
+
15
+ == FEATURES
16
+
17
+ === Model Update Tracker
18
+ This library allows you to functionally test updates to models with clearer and more focused intent.
19
+ It forces you to have a better understanding of how your objects interact and lets you demonstrate
20
+ that knowledge by specifying it in your tests. For example, consider the following test which asserts
21
+ that the code block persists an Asset record.
22
+
23
+ assert_difference Asset :count do
24
+ post :create, :asset => {}
25
+ end
26
+
27
+ Is it okay if the code block also persists a Location record? An AssetOwner record? Some other record?
28
+ Chances are that the developer knows the answer to this question, but the test fails to capture that
29
+ knowledge. Side effects matter, and our tests should adequately validate that the desired side effects
30
+ (and <i>only</i> the desired side effects) are taking place. In the example below, assert_models_created
31
+ allows us to validate that an Asset is created <i>and</i> that no other objects are inadvertently
32
+ persisted.
33
+
34
+ assert_models_created(Asset) do
35
+ post :create, :asset => {}
36
+ end
37
+
38
+ If the code block happened to create records other than just an Asset, a friendly test failure would let
39
+ you know right away.
40
+
41
+ Suppose, on the other hand, that you expect the creation of an Asset to <i>also</i> produce an AssetOwner
42
+ and a Location. This is where you exercise your deep domain knowledge muscles and make your new
43
+ Obsidian-powered test express and validate your intent.
44
+
45
+ assert_models_created(Asset, AssetOwner, Location) do
46
+ post: create, :asset => {}
47
+ end
48
+
49
+ Now, if your code block fails to create the expected records, the above test would fail stating that it
50
+ expected more to happen. If future changes to your code cause things to fall out of place, this test will
51
+ catch that regression where the original assert_difference may not. In addition to assert_models_created,
52
+ Model Update Tracker provides a whole host of other methods that provide similar functionality for
53
+ asserting behavior related to updates, deletes, or even validating that nothing happened.
54
+
55
+ * assert_models_created(models)
56
+ * assert_models_updated(models)
57
+ * assert_models_destroyed(models)
58
+ * assert_no_models_created
59
+ * assert_no_models_destroyed
60
+ * assert_no_models_updated
61
+
62
+ == INSTALL
63
+
64
+ sudo gem install obsidian
65
+
66
+ == URLS
67
+
68
+ * Log bugs, issues, and suggestions on Trac: http://opensource.thinkrelevance.com/wiki/obsidian
69
+ * View Source: http://github.com/relevance/obsidian/tree/master
70
+ * Git clone Source: git://github.com/relevance/obsidian.git
71
+ * RDocs: http://thinkrelevance.rubyforge.org/obsidian/
72
+
73
+ == LICENSE
74
+
75
+ (The MIT License)
76
+
77
+ Copyright (c) 2008 Relevance, Inc. - http://thinkrelevance.com
78
+
79
+ Permission is hereby granted, free of charge, to any person obtaining
80
+ a copy of this software and associated documentation files (the
81
+ 'Software'), to deal in the Software without restriction, including
82
+ without limitation the rights to use, copy, modify, merge, publish,
83
+ distribute, sublicense, and/or sell copies of the Software, and to
84
+ permit persons to whom the Software is furnished to do so, subject to
85
+ the following conditions:
86
+
87
+ The above copyright notice and this permission notice shall be
88
+ included in all copies or substantial portions of the Software.
89
+
90
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
91
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
92
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
93
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
94
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
95
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
96
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,96 @@
1
+ = Obsidian
2
+
3
+ == DESCRIPTION
4
+
5
+ Obsidian. It's metastable.
6
+
7
+ In the tangible world, obsidian is a naturally-occurring glass that is metastable at the earth's
8
+ surface. It's commonly found within the margins of rhyolitic lava flows, where cooling of the lava is
9
+ rapid. [1] In the slightly less tangible world of programming, <b>Obsidian</b> is home to chunks of
10
+ Ruby code that we've found helpful. These bits of code are often found at the bottom of a mountain of
11
+ yak hair, the occasionally-occurring product of simplifying various tasks.
12
+
13
+ 1 http://en.wikipedia.org/wiki/Obsidian
14
+
15
+ == FEATURES
16
+
17
+ === Model Update Tracker
18
+ This library allows you to functionally test updates to models with clearer and more focused intent.
19
+ It forces you to have a better understanding of how your objects interact and lets you demonstrate
20
+ that knowledge by specifying it in your tests. For example, consider the following test which asserts
21
+ that the code block persists an Asset record.
22
+
23
+ assert_difference Asset :count do
24
+ post :create, :asset => {}
25
+ end
26
+
27
+ Is it okay if the code block also persists a Location record? An AssetOwner record? Some other record?
28
+ Chances are that the developer knows the answer to this question, but the test fails to capture that
29
+ knowledge. Side effects matter, and our tests should adequately validate that the desired side effects
30
+ (and <i>only</i> the desired side effects) are taking place. In the example below, assert_models_created
31
+ allows us to validate that an Asset is created <i>and</i> that no other objects are inadvertently
32
+ persisted.
33
+
34
+ assert_models_created(Asset) do
35
+ post :create, :asset => {}
36
+ end
37
+
38
+ If the code block happened to create records other than just an Asset, a friendly test failure would let
39
+ you know right away.
40
+
41
+ Suppose, on the other hand, that you expect the creation of an Asset to <i>also</i> produce an AssetOwner
42
+ and a Location. This is where you exercise your deep domain knowledge muscles and make your new
43
+ Obsidian-powered test express and validate your intent.
44
+
45
+ assert_models_created(Asset, AssetOwner, Location) do
46
+ post: create, :asset => {}
47
+ end
48
+
49
+ Now, if your code block fails to create the expected records, the above test would fail stating that it
50
+ expected more to happen. If future changes to your code cause things to fall out of place, this test will
51
+ catch that regression where the original assert_difference may not. In addition to assert_models_created,
52
+ Model Update Tracker provides a whole host of other methods that provide similar functionality for
53
+ asserting behavior related to updates, deletes, or even validating that nothing happened.
54
+
55
+ * assert_models_created(models)
56
+ * assert_models_updated(models)
57
+ * assert_models_destroyed(models)
58
+ * assert_no_models_created
59
+ * assert_no_models_destroyed
60
+ * assert_no_models_updated
61
+
62
+ == INSTALL
63
+
64
+ sudo gem install obsidian
65
+
66
+ == URLS
67
+
68
+ * Log bugs, issues, and suggestions on Trac: http://opensource.thinkrelevance.com/wiki/obsidian
69
+ * View Source: http://github.com/relevance/obsidian/tree/master
70
+ * Git clone Source: git://github.com/relevance/obsidian.git
71
+ * RDocs: http://thinkrelevance.rubyforge.org/obsidian/
72
+
73
+ == LICENSE
74
+
75
+ (The MIT License)
76
+
77
+ Copyright (c) 2008 Relevance, Inc. - http://thinkrelevance.com
78
+
79
+ Permission is hereby granted, free of charge, to any person obtaining
80
+ a copy of this software and associated documentation files (the
81
+ 'Software'), to deal in the Software without restriction, including
82
+ without limitation the rights to use, copy, modify, merge, publish,
83
+ distribute, sublicense, and/or sell copies of the Software, and to
84
+ permit persons to whom the Software is furnished to do so, subject to
85
+ the following conditions:
86
+
87
+ The above copyright notice and this permission notice shall be
88
+ included in all copies or substantial portions of the Software.
89
+
90
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
91
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
92
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
93
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
94
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
95
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
96
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'hoe'
6
+ require 'lib/obsidian'
7
+
8
+ hoe = Hoe.new('obsidian', Obsidian::VERSION) do |p|
9
+ p.rubyforge_name = "thinkrelevance"
10
+ p.description = "It's metastable"
11
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
12
+ p.name = 'obsidian'
13
+ p.summary = "It's metastable"
14
+ p.author = "Relevance"
15
+ p.email = "opensource@thinkrelevance.com"
16
+ p.url = "http://opensource.thinkrelevance.com"
17
+ p.rdoc_pattern = /^(lib|bin|ext)|txt|rdoc$/
18
+ p.test_globs = "test/**/*_test.rb"
19
+ end
20
+
21
+ # Override RDoc to use allison template, and also use our .rdoc README as the main page instead of the default README.txt
22
+ Rake::RDocTask.new(:docs) do |rd|
23
+ gem "allison"
24
+ gem "markaby"
25
+ rd.main = "README.rdoc"
26
+ # rd.options << '-d' if RUBY_PLATFORM !~ /win32/ and `which dot` =~ /\/dot/ and not ENV['NODOT']
27
+ rd.rdoc_dir = 'doc'
28
+ files = hoe.spec.files.grep(hoe.rdoc_pattern)
29
+ files -= ['Manifest.txt']
30
+ rd.rdoc_files.push(*files)
31
+
32
+ title = "#{hoe.name}-#{hoe.version} Documentation"
33
+ title = "#{hoe.rubyforge_name}'s " + title if hoe.rubyforge_name != hoe.name
34
+ end
35
+
36
+ begin
37
+ require 'rcov'
38
+ require "rcov/rcovtask"
39
+
40
+ namespace :coverage do
41
+ rcov_output = ENV["CC_BUILD_ARTIFACTS"] || 'tmp/coverage'
42
+ rcov_exclusions = %w{
43
+ }.join(',')
44
+
45
+ desc "Delete aggregate coverage data."
46
+ task(:clean) { rm_f "rcov_tmp" }
47
+
48
+ Rcov::RcovTask.new(:unit => :clean) do |t|
49
+ t.test_files = FileList['test/**/*_test.rb']
50
+ t.rcov_opts = ["--sort coverage", "--aggregate 'rcov_tmp'", "--html", "--rails", "--exclude '/Library'"]
51
+ t.output_dir = rcov_output + '/unit'
52
+ end
53
+
54
+ desc "Generate and open coverage report"
55
+ task(:all => [:unit]) do
56
+ system("open #{rcov_output}/unit/index.html") if PLATFORM['darwin']
57
+ end
58
+ end
59
+ rescue LoadError
60
+ if RUBY_PLATFORM =~ /java/
61
+ puts 'running in jruby - rcov tasks not available'
62
+ else
63
+ puts 'sudo gem install rcov # if you want the rcov tasks'
64
+ end
65
+ end
66
+
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+
3
+ module Obsidian::Rails
4
+ module HelperTest
5
+ # include all the fun dependancies needed to test helpers
6
+ def helper_test helper_under_test = nil
7
+ include ActionView::Helpers::CaptureHelper
8
+ include ActionView::Helpers::DateHelper
9
+ include ActionView::Helpers::UrlHelper
10
+ include ActionView::Helpers::TextHelper
11
+ include ActionView::Helpers::AssetTagHelper
12
+ include ActionView::Helpers::TagHelper
13
+ include ActionView::Helpers::FormOptionsHelper
14
+ include ActionView::Helpers::FormTagHelper
15
+ include ApplicationHelper
16
+ include helper_under_test if helper_under_test
17
+ end
18
+
19
+ end
20
+ end
21
+
22
+ Test::Unit::TestCase.extend Obsidian::Rails::HelperTest
@@ -0,0 +1,182 @@
1
+ if defined? RAILS_ENV
2
+ raise "For testing only!" unless RAILS_ENV=="test"
3
+ end
4
+
5
+ module Obsidian::Rails
6
+ module ModelUpdateTracker
7
+ class Delta
8
+ attr_accessor :committed, :uncommitted
9
+ def initialize
10
+ reset
11
+ end
12
+
13
+ def reset
14
+ @committed = []
15
+ @uncommitted = []
16
+ end
17
+
18
+ def << (obj)
19
+ @uncommitted << obj
20
+ end
21
+
22
+ def transaction_committed
23
+ @committed = @committed + @uncommitted
24
+ @uncommitted = []
25
+ end
26
+
27
+ def transaction_rolled_back
28
+ @uncommitted = []
29
+ end
30
+
31
+ def instances
32
+ @committed + @uncommitted
33
+ end
34
+
35
+ def class_names
36
+ set = instances.inject(Set.new) do |set,inst|
37
+ set << inst.class.to_s
38
+ set
39
+ end
40
+ set
41
+ end
42
+ end
43
+ class << self
44
+ attr_accessor :created_delta, :destroyed_delta, :updated_delta
45
+ def reset
46
+ created_delta.reset
47
+ destroyed_delta.reset
48
+ updated_delta.reset
49
+ end
50
+
51
+ def after_create(model)
52
+ log("Model create #{model} is a #{model.class}")
53
+ created_delta << model
54
+ end
55
+
56
+ def after_update(model)
57
+ log("Model update #{model}")
58
+ updated_delta << model
59
+ end
60
+
61
+ def after_destroy(model)
62
+ log("Model destroy #{model}")
63
+ destroyed_delta << model
64
+ end
65
+
66
+ def after_transaction_commit
67
+ log("Commit transaction")
68
+ created_delta.transaction_committed
69
+ updated_delta.transaction_committed
70
+ destroyed_delta.transaction_committed
71
+ end
72
+
73
+ def after_transaction_rollback
74
+ log("Rollback of transaction}")
75
+ created_delta.transaction_rolled_back
76
+ updated_delta.transaction_rolled_back
77
+ destroyed_delta.transaction_rolled_back
78
+ end
79
+
80
+ def after_transaction_exception
81
+ log("Exception in transaction}")
82
+ created_delta.transaction_rolled_back
83
+ updated_delta.transaction_rolled_back
84
+ destroyed_delta.transaction_rolled_back
85
+ end
86
+
87
+ def log(msg)
88
+ # puts msg
89
+ end
90
+ end
91
+ self.created_delta = Delta.new
92
+ self.updated_delta = Delta.new
93
+ self.destroyed_delta = Delta.new
94
+ self.reset
95
+ end
96
+ end
97
+
98
+ # not using normal after save hooks because of initialization order issues
99
+ class ActiveRecord::Base
100
+ def create_with_model_update_tracker(*args,&blk)
101
+ result = create_without_model_update_tracker(*args,&blk)
102
+ Obsidian::Rails::ModelUpdateTracker.after_create(self) if result
103
+ result
104
+ end
105
+
106
+ alias_method_chain :create, :model_update_tracker
107
+
108
+ def update_with_model_update_tracker(*args,&blk)
109
+ result = update_without_model_update_tracker(*args,&blk)
110
+ Obsidian::Rails::ModelUpdateTracker.after_update(self) if result
111
+ result
112
+ end
113
+
114
+ alias_method_chain :update, :model_update_tracker
115
+
116
+ def destroy_with_model_destroy_tracker(*args,&blk)
117
+ result = destroy_without_model_destroy_tracker(*args,&blk)
118
+ Obsidian::Rails::ModelUpdateTracker.after_destroy(self) if result
119
+ result
120
+ end
121
+
122
+ alias_method_chain :destroy, :model_destroy_tracker
123
+ end
124
+
125
+ class Test::Unit::TestCase
126
+ def assert_no_models_created(&blk)
127
+ assert_models_created(&blk)
128
+ end
129
+
130
+ def assert_no_models_destroyed(&blk)
131
+ assert_models_destroyed(&blk)
132
+ end
133
+
134
+ def assert_no_models_updated(&blk)
135
+ assert_models_updated(&blk)
136
+ end
137
+
138
+ def assert_models_destroyed(*models, &blk)
139
+ Obsidian::Rails::ModelUpdateTracker.reset
140
+ blk.call
141
+ assert_equal(Set.new(models.map(&:to_s)), Obsidian::Rails::ModelUpdateTracker.destroyed_delta.class_names)
142
+ end
143
+
144
+ def assert_models_updated(*models, &blk)
145
+ Obsidian::Rails::ModelUpdateTracker.reset
146
+ blk.call
147
+ assert_equal(Set.new(models.map(&:to_s)), Obsidian::Rails::ModelUpdateTracker.updated_delta.class_names)
148
+ end
149
+
150
+ def assert_models_created(*models, &blk)
151
+ Obsidian::Rails::ModelUpdateTracker.reset
152
+ blk.call
153
+ assert_equal(Set.new(models.map(&:to_s)), Obsidian::Rails::ModelUpdateTracker.created_delta.class_names)
154
+ end
155
+ end
156
+
157
+ module ActiveRecord
158
+ module ConnectionAdapters # :nodoc:
159
+ module DatabaseStatements
160
+ def transaction_with_model_update_tracker(*args,&blk)
161
+ transaction_without_model_update_tracker(*args,&blk)
162
+ rescue
163
+ Obsidian::Rails::ModelUpdateTracker.after_transaction_exception
164
+ raise
165
+ end
166
+
167
+ def rollback_db_transaction_with_model_update_tracker
168
+ rollback_db_transaction_without_model_update_tracker
169
+ Obsidian::Rails::ModelUpdateTracker.after_transaction_rollback
170
+ end
171
+
172
+ def commit_db_transaction_with_model_update_tracker
173
+ commit_db_transaction_without_model_update_tracker
174
+ Obsidian::Rails::ModelUpdateTracker.after_transaction_commit
175
+ end
176
+
177
+ alias_method_chain :transaction, :model_update_tracker
178
+ alias_method_chain :rollback_db_transaction, :model_update_tracker
179
+ alias_method_chain :commit_db_transaction, :model_update_tracker
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,48 @@
1
+ require 'obsidian/spec'
2
+
3
+ module Obsidian
4
+ module Spec
5
+ class MappingMatcher
6
+ include Obsidian::Spec
7
+ def initialize(object)
8
+ @object = object.to_set
9
+ end
10
+ def map
11
+ self
12
+ end
13
+ def to(*args, &blk)
14
+ case args.size
15
+ when 0 then self
16
+ when 1 then test("to", args.first, &blk)
17
+ else raise ArgumentError, "wrong number of arguments (#{args.size} for 0-1)"
18
+ end
19
+ end
20
+ # for map.to.values.in
21
+ def values
22
+ self
23
+ end
24
+ def in(*other, &blk)
25
+ test("in", *other, &blk)
26
+ end
27
+ def test(method, *other, &blk)
28
+ other = args_to_set(*other)
29
+ @object.each do |obj|
30
+ assert(other.include?(blk.call(obj)),
31
+ "Expected #{obj} to map to #{error_message(other)} at\n\t#{read_calling_line(caller,method)}")
32
+ end
33
+ end
34
+ def error_message(set)
35
+ (set.size == 1 ? set.to_a.first : set).inspect
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ class Test::Spec::Should
42
+ def all
43
+ Obsidian::Spec::MappingMatcher.new(@object)
44
+ end
45
+ def map
46
+ Obsidian::Spec::MappingMatcher.new(@object)
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ require 'obsidian/spec'
2
+ require 'set'
3
+
4
+ def Set(*args)
5
+ Set.new(args)
6
+ end
7
+
8
+ module Obsidian
9
+ module Spec
10
+ class SubsetMatcher
11
+ include Obsidian::Spec
12
+ def initialize(object)
13
+ @object = object.to_set
14
+ end
15
+ def of(*other)
16
+ other = args_to_set(*other)
17
+ assert(@object.proper_subset?(other) || @object==other, "Expected #{@object.inspect} to be a subset of #{other.inspect}")
18
+ end
19
+ end
20
+ class SupersetMatcher
21
+ include Obsidian::Spec
22
+ def initialize(object)
23
+ @object = object.to_set
24
+ end
25
+ def of(*other)
26
+ other = args_to_set(*other)
27
+ assert(@object.proper_superset?(other) || @object==other, "Expected #{@object.inspect} to be a superset of #{other.inspect}")
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ class Test::Spec::Should
34
+ def subset
35
+ Obsidian::Spec::SubsetMatcher.new(@object)
36
+ end
37
+ def superset
38
+ Obsidian::Spec::SupersetMatcher.new(@object)
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ require 'test/spec'
2
+
3
+ module Obsidian
4
+ module Spec
5
+ include Test::Unit::Assertions
6
+ def args_to_set(*args)
7
+ if args.size == 1
8
+ args.first.respond_to?(:to_set) ? args.first.to_set : Set.new([args.first])
9
+ else
10
+ args.to_set
11
+ end
12
+ end
13
+
14
+ # find the line in a stack trace above the one from method_name
15
+ def find_calling_line(trace, method_name)
16
+ trace.each_cons(2) do |line, next_line|
17
+ if /(.*):(\d+):in .(.*)'/ =~ line &&
18
+ $3 == method_name &&
19
+ /(.*):(\d+):in .(.*)'/ =~ next_line
20
+ return [$1,$2]
21
+ end
22
+ end
23
+ nil
24
+ end
25
+
26
+ def read_calling_line(trace, method_name)
27
+ file, line_number = find_calling_line(trace, method_name)
28
+ File.readlines(file)[Integer(line_number) - 1].chop.strip if file
29
+ end
30
+
31
+ end
32
+ end
data/lib/obsidian.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Obsidian
2
+ VERSION = '0.0.5'
3
+ end
data/obsidian.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{obsidian}
5
+ s.version = "0.0.5"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Relevance"]
9
+ s.date = %q{2008-10-30}
10
+ s.description = %q{It's metastable}
11
+ s.email = %q{opensource@thinkrelevance.com}
12
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
13
+ s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.rdoc", "README.txt", "Rakefile", "lib/obsidian.rb", "lib/obsidian/rails/helper_test.rb", "lib/obsidian/rails/model_update_tracker.rb", "lib/obsidian/spec.rb", "lib/obsidian/spec/map_spec_helper.rb", "lib/obsidian/spec/set_spec_helper.rb", "obsidian.gemspec", "test/obsidian_test.rb", "test/test_helper.rb", "test/units/obsidian/rails/model_update_tracker_test.rb", "test/units/obsidian/spec/map_spec_helper_test.rb", "test/units/obsidian/spec/set_spec_helper_test.rb", "test/units/obsidian/spec_test.rb", "test/units/obsidian/object_extensions_test.rb", "test/units/obsidian/try_test.rb"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://opensource.thinkrelevance.com}
16
+ s.rdoc_options = ["--main", "README.txt"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{thinkrelevance}
19
+ s.rubygems_version = %q{1.3.0}
20
+ s.summary = %q{It's metastable}
21
+ s.test_files = ["test/obsidian_test.rb", "test/units/obsidian/object_extensions_test.rb", "test/units/obsidian/rails/model_update_tracker_test.rb", "test/units/obsidian/spec/map_spec_helper_test.rb", "test/units/obsidian/spec/set_spec_helper_test.rb", "test/units/obsidian/spec_test.rb", "test/units/obsidian/try_test.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_development_dependency(%q<hoe>, [">= 1.8.1"])
29
+ else
30
+ s.add_dependency(%q<hoe>, [">= 1.8.1"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<hoe>, [">= 1.8.1"])
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
@@ -0,0 +1,13 @@
1
+ basedir = File.dirname(__FILE__)
2
+ $:.unshift "#{basedir}/../lib"
3
+ require 'rubygems'
4
+ gem 'test-spec'
5
+ gem 'activesupport'
6
+ gem 'actionpack'
7
+ gem 'activerecord'
8
+
9
+ require 'test/spec'
10
+ require 'mocha'
11
+ require 'ostruct'
12
+ require 'activerecord'
13
+ require 'obsidian'
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), "../..", "test_helper.rb")
2
+ require 'obsidian/extensions/object'
3
+
4
+ describe "object extensions" do
5
+ it "should get metaclass" do
6
+ obj = Object.new
7
+ metaklass = (class << obj; self; end;)
8
+ obj.metaclass.should == metaklass
9
+ end
10
+
11
+ it "should be able to add methods to the meta class" do
12
+ obj = Object.new
13
+ obj.meta_def(:speak) { "bark!" }
14
+ obj.should.respond_to :speak
15
+
16
+ another_obj = Object.new
17
+ another_obj.should.not.respond_to :speak
18
+ end
19
+
20
+ class Foo; end
21
+
22
+ it "should be able to define instance methods from the class" do
23
+ Foo.class_def(:hi) { "hello!" }
24
+ Foo.new.hi.should == "hello!"
25
+ Foo.should.not.respond_to :hi
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,126 @@
1
+ require File.join(File.dirname(__FILE__), "../../..", "test_helper.rb")
2
+ require 'obsidian/rails/model_update_tracker'
3
+ include Obsidian::Rails::ModelUpdateTracker
4
+
5
+ describe "ModelUpdateTracker" do
6
+ describe "Delta" do
7
+ it "adds uncommitted objects with <<" do
8
+ delta = Delta.new
9
+ delta << "Foo"
10
+ delta.uncommitted.should == ["Foo"]
11
+ end
12
+
13
+ it "moves uncommitted objects to committed on commit" do
14
+ delta = Delta.new
15
+ delta << "Foo"
16
+ delta.transaction_committed
17
+ delta << "Bar"
18
+ delta.transaction_committed
19
+ delta << "Quux"
20
+ delta.committed.should == ["Foo", "Bar"]
21
+ delta.uncommitted.should == ["Quux"]
22
+ end
23
+
24
+ it "moves uncommitted objects to committed on rollback" do
25
+ delta = Delta.new
26
+ delta << "Foo"
27
+ delta.transaction_rolled_back
28
+ delta.committed.should == []
29
+ delta.uncommitted.should == []
30
+ end
31
+
32
+ it "gathers both committed and uncommitted changes into instances" do
33
+ delta = Delta.new
34
+ delta.committed = ["Foo"]
35
+ delta.uncommitted = ["Bar"]
36
+ delta.instances.should == ["Foo", "Bar"]
37
+ end
38
+
39
+ it "gathers the class names of modified instances" do
40
+ delta = Delta.new
41
+ delta.committed = ["String"]
42
+ delta.uncommitted = [10]
43
+ delta.class_names.should == Set.new(["Fixnum", "String"])
44
+ end
45
+ end
46
+
47
+ before do
48
+ @tracker = Obsidian::Rails::ModelUpdateTracker
49
+ @tracker.reset
50
+ end
51
+
52
+ it "reset clears all the deltas" do
53
+ @tracker.after_create("Foo")
54
+ @tracker.after_update("Bar")
55
+ @tracker.after_destroy("Quux")
56
+ @tracker.reset
57
+ @tracker.created_delta.instances.should == []
58
+ @tracker.updated_delta.instances.should == []
59
+ @tracker.destroyed_delta.instances.should == []
60
+ end
61
+
62
+ describe "Data access callbacks" do
63
+ before do
64
+ @tracker = Obsidian::Rails::ModelUpdateTracker
65
+ @tracker.reset
66
+ end
67
+
68
+ it "after_create bumps the created_delta" do
69
+ @tracker.after_create("Foo")
70
+ @tracker.created_delta.instances.should == ["Foo"]
71
+ @tracker.updated_delta.instances.should == []
72
+ @tracker.destroyed_delta.instances.should == []
73
+ end
74
+
75
+ it "after_update bumps the updated_delta" do
76
+ @tracker.after_update("Foo")
77
+ @tracker.created_delta.instances.should == []
78
+ @tracker.updated_delta.instances.should == ["Foo"]
79
+ @tracker.destroyed_delta.instances.should == []
80
+ end
81
+
82
+ it "after_destroyed bumps the destroyed_delta" do
83
+ @tracker.after_destroy("Foo")
84
+ @tracker.created_delta.instances.should == []
85
+ @tracker.updated_delta.instances.should == []
86
+ @tracker.destroyed_delta.instances.should == ["Foo"]
87
+ end
88
+ end
89
+
90
+ describe "Transaction callbacks" do
91
+ before do
92
+ @tracker = Obsidian::Rails::ModelUpdateTracker
93
+ @tracker.reset
94
+ end
95
+
96
+ it "after_transaction_commit commits all the deltas" do
97
+ @tracker.after_create("Foo")
98
+ @tracker.after_update("Bar")
99
+ @tracker.after_destroy("Quux")
100
+ @tracker.after_transaction_commit
101
+ @tracker.created_delta.committed.should == ["Foo"]
102
+ @tracker.updated_delta.committed.should == ["Bar"]
103
+ @tracker.destroyed_delta.committed.should == ["Quux"]
104
+ end
105
+
106
+ it "after_transaction_rollback rolls back all the deltas" do
107
+ @tracker.after_create("Foo")
108
+ @tracker.after_update("Bar")
109
+ @tracker.after_destroy("Quux")
110
+ @tracker.after_transaction_rollback
111
+ @tracker.created_delta.instances.should == []
112
+ @tracker.updated_delta.instances.should == []
113
+ @tracker.destroyed_delta.instances.should == []
114
+ end
115
+
116
+ it "after_transaction_exception rolls back all the deltas" do
117
+ @tracker.after_create("Foo")
118
+ @tracker.after_update("Bar")
119
+ @tracker.after_destroy("Quux")
120
+ @tracker.after_transaction_exception
121
+ @tracker.created_delta.instances.should == []
122
+ @tracker.updated_delta.instances.should == []
123
+ @tracker.destroyed_delta.instances.should == []
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), "../../..", "test_helper.rb")
2
+ require 'obsidian/spec/map_spec_helper'
3
+
4
+ describe "to" do
5
+ it "should reject wrong number of arguments" do
6
+ m = Obsidian::Spec::MappingMatcher.new([1])
7
+ lambda{m.to(1,2)}.should.raise(ArgumentError)
8
+ end
9
+ end
10
+
11
+ describe "should.map.to" do
12
+
13
+ it "should be able to check agains single values" do
14
+ [1,2,3].should.all.map.to(true) {|item| item < 10}
15
+ end
16
+
17
+ it "should give a great error message" do
18
+ err = lambda{[1,12,3].should.all.map.to(true) {|item| item < 10}}.should.raise(Test::Unit::AssertionFailedError)
19
+ err.message.should == <<-END.chop
20
+ Expected 12 to map to true at
21
+ \terr = lambda{[1,12,3].should.all.map.to(true) {|item| item < 10}}.should.raise(Test::Unit::AssertionFailedError).
22
+ <false> is not true.
23
+ END
24
+ end
25
+
26
+ end
27
+
28
+ describe "should.be.in" do
29
+
30
+ it "should be able check against multiple values" do
31
+ [1,2,3].should.map.to.values.in(2,4,6) {|item| item * 2}
32
+ end
33
+
34
+ it "should not care about order" do
35
+ [1,2,3].should.map.to.values.in(6,4,2) {|item| item * 2}
36
+ end
37
+
38
+ it "should give a great error message" do
39
+ err = lambda{[1,2,3].should.map.to.values.in(2,4) {|item| item * 2}}.should.raise(Test::Unit::AssertionFailedError)
40
+ err.message.should == <<-END.chop
41
+ Expected 3 to map to #<Set: {2, 4}> at
42
+ \terr = lambda{[1,2,3].should.map.to.values.in(2,4) {|item| item * 2}}.should.raise(Test::Unit::AssertionFailedError).
43
+ <false> is not true.
44
+ END
45
+ end
46
+
47
+ end
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), "../../..", "test_helper.rb")
2
+ require 'obsidian/spec/set_spec_helper'
3
+
4
+ describe "Set extensions for spec" do
5
+ it "Set acts as a constructor function to approximate a literal syntax for sets" do
6
+ Set(1,2,3).should == Set.new([1,2,3])
7
+ end
8
+ end
9
+
10
+ describe "should.be.subset.of" do
11
+ it "succeeds for subset" do
12
+ [1,2].should.be.subset.of([1,2,3])
13
+ end
14
+
15
+ it "succeeds for equal set" do
16
+ (1..3).should.be.subset.of([1,2,3])
17
+ end
18
+
19
+ it "will splat args for convenience" do
20
+ (1..3).should.be.subset.of(1,2,3)
21
+ end
22
+
23
+ it "fails for non-subset" do
24
+ err = lambda{Set(0,5).should.be.subset.of([1,2,3])}.should.raise(Test::Unit::AssertionFailedError)
25
+ err.message.should == "Expected #<Set: {5, 0}> to be a subset of #<Set: {1, 2, 3}>.\n<false> is not true."
26
+ end
27
+ end
28
+
29
+ describe "should.be.superset.of" do
30
+ it "succeeds for superset" do
31
+ [1,2,3].should.be.superset.of([1,2])
32
+ end
33
+
34
+ it "succeeds for equal set" do
35
+ (1..3).should.be.superset.of([1,2,3])
36
+ end
37
+
38
+ it "will splat args for convenience" do
39
+ (1..3).should.be.superset.of(1,2,3)
40
+ end
41
+
42
+ it "fails for non-superset" do
43
+ err = lambda {Set(1,2).should.be.superset.of([1,2,3])}.should.raise(Test::Unit::AssertionFailedError)
44
+ err.message.should == "Expected #<Set: {1, 2}> to be a superset of #<Set: {1, 2, 3}>.\n<false> is not true."
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), "../..", "test_helper.rb")
2
+ require 'obsidian/spec'
3
+ include Obsidian::Spec
4
+
5
+ describe "Obsidian::Spec args_to_set" do
6
+ it "can convert a single scalar to a set" do
7
+ args_to_set(:scalar).should == Set.new([:scalar])
8
+ end
9
+
10
+ it "can convert a single collection to a set" do
11
+ args_to_set([:a, :b]).should == Set.new([:a, :b])
12
+ end
13
+
14
+ it "can convert multiple scalars to a set" do
15
+ args_to_set(:a, :b).should == Set.new([:a, :b])
16
+ end
17
+ end
18
+
19
+ describe "Obsidian::Spec find_calling_line" do
20
+ def nest_2(name)
21
+ find_calling_line(caller, name)
22
+ end
23
+ def nest_1(name)
24
+ nest_2(name)
25
+ end
26
+ it "can parse a stack trace to find the caller of a method" do
27
+ file, line_no = nest_1("nest_1")
28
+ file.should.match %r{spec_test.rb$}
29
+ line_no.should.match(/^\d+$/)
30
+ end
31
+ it "returns nil if a method does not exist" do
32
+ file, line_no = nest_1("not_a_real_method_name")
33
+ file.should.be nil
34
+ line_no.should.be nil
35
+ end
36
+ end
37
+
38
+ describe "Obsidian::Spec read_calling_line" do
39
+ def nest_2(name)
40
+ read_calling_line(caller, name)
41
+ end
42
+ def nest_1(name)
43
+ nest_2(name)
44
+ end
45
+ it "can parse a stack trace to find the caller of a method" do
46
+ nest_1("nest_1").should.match(/nest_1\("nest_1"\)/)
47
+ end
48
+ it "returns nil if a method does not exist" do
49
+ nest_1("not_a_real_method_name").should.be nil
50
+ end
51
+ end
52
+
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), "../..", "test_helper.rb")
2
+ require 'obsidian/extensions/try'
3
+
4
+ describe "try object extension" do
5
+
6
+ it "is equivalent to @foo ? @foo.meth : nil" do
7
+ ["my string", Array.new, nil].each do |val|
8
+ @foo = val
9
+ @foo.try(:length).should == (@foo ? @foo.length : nil)
10
+ end
11
+
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relevance-obsidian
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Relevance
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-30 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.1
23
+ version:
24
+ description: It's metastable
25
+ email: opensource@thinkrelevance.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - History.txt
32
+ - Manifest.txt
33
+ - README.txt
34
+ files:
35
+ - History.txt
36
+ - LICENSE
37
+ - Manifest.txt
38
+ - README.rdoc
39
+ - README.txt
40
+ - Rakefile
41
+ - lib/obsidian.rb
42
+ - lib/obsidian/rails/helper_test.rb
43
+ - lib/obsidian/rails/model_update_tracker.rb
44
+ - lib/obsidian/spec.rb
45
+ - lib/obsidian/spec/map_spec_helper.rb
46
+ - lib/obsidian/spec/set_spec_helper.rb
47
+ - obsidian.gemspec
48
+ - test/obsidian_test.rb
49
+ - test/test_helper.rb
50
+ - test/units/obsidian/rails/model_update_tracker_test.rb
51
+ - test/units/obsidian/spec/map_spec_helper_test.rb
52
+ - test/units/obsidian/spec/set_spec_helper_test.rb
53
+ - test/units/obsidian/spec_test.rb
54
+ - test/units/obsidian/object_extensions_test.rb
55
+ - test/units/obsidian/try_test.rb
56
+ has_rdoc: true
57
+ homepage: http://opensource.thinkrelevance.com
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --main
61
+ - README.txt
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project: thinkrelevance
79
+ rubygems_version: 1.2.0
80
+ signing_key:
81
+ specification_version: 2
82
+ summary: It's metastable
83
+ test_files:
84
+ - test/obsidian_test.rb
85
+ - test/units/obsidian/object_extensions_test.rb
86
+ - test/units/obsidian/rails/model_update_tracker_test.rb
87
+ - test/units/obsidian/spec/map_spec_helper_test.rb
88
+ - test/units/obsidian/spec/set_spec_helper_test.rb
89
+ - test/units/obsidian/spec_test.rb
90
+ - test/units/obsidian/try_test.rb