snepo-dm-machinist 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Cameron Barrie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,9 @@
1
+ dm-machinist
2
+ =========
3
+
4
+ DataMapper plugin for Pete Yandell's(http://notahat.com/) excellent Machinist library.
5
+
6
+ require 'dm-machinist' in your spec_helper.rb and you can use it in your specs.
7
+
8
+ See Tim Lucus's sweet write up for usage details on Machinist.
9
+ http://toolmantim.com/article/2008/10/27/fixtureless_datas_with_machinist_and_sham
data/Rakefile ADDED
@@ -0,0 +1,80 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'spec/rake/spectask'
6
+ require 'pathname'
7
+
8
+ CLEAN.include '{log,pkg}/'
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = 'dm-machinist'
12
+ s.version = '0.1.0'
13
+ s.platform = Gem::Platform::RUBY
14
+ s.has_rdoc = true
15
+ s.extra_rdoc_files = %w[ README LICENSE TODO ]
16
+ s.summary = 'DataMapper plugin for machinist'
17
+ s.description = s.summary
18
+ s.author = 'Cameron Barrie'
19
+ s.email = 'cameron@snepo'
20
+ s.homepage = 'http://github.com/snepo/dm-machinist/tree/master'
21
+ s.require_path = 'lib'
22
+ s.files = FileList[ '{lib,spec}/**/*.rb', 'spec/spec.opts', 'Rakefile', *s.extra_rdoc_files ]
23
+ s.add_dependency('dm-core')
24
+ end
25
+
26
+ task :default => [ :spec ]
27
+
28
+ WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
29
+ SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.gem_spec = spec
33
+ end
34
+
35
+ desc "Generate GemSpec for github"
36
+ task :gemspec do
37
+ spec_text = <<EOF
38
+ Gem::Specification.new do |s|
39
+ s.name = #{spec.name.inspect}
40
+ s.version = '#{spec.version}'
41
+ s.platform = Gem::Platform::RUBY
42
+ s.has_rdoc = #{spec.has_rdoc.inspect}
43
+ s.extra_rdoc_files = #{spec.extra_rdoc_files.inspect}
44
+ s.summary = #{spec.summary.inspect}
45
+ s.description = #{spec.description.inspect}
46
+ s.author = #{spec.author.inspect}
47
+ s.email = #{spec.email.inspect}
48
+ s.homepage = #{spec.homepage.inspect}
49
+ s.require_path = #{spec.require_path.inspect}
50
+ s.files = #{spec.files.inspect}
51
+ s.add_dependency('dm-core')
52
+ end
53
+ EOF
54
+ File.open("dm-machinist.gemspec", "w") do |f|
55
+ f.write(spec_text)
56
+ end
57
+ end
58
+
59
+ desc "Install #{spec.name} #{spec.version} (default ruby)"
60
+ task :install => [ :package ] do
61
+ sh "#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources", :verbose => false
62
+ end
63
+
64
+ desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
65
+ task :uninstall => [ :clobber ] do
66
+ sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
67
+ end
68
+
69
+ namespace :jruby do
70
+ desc "Install #{spec.name} #{spec.version} with JRuby"
71
+ task :install => [ :package ] do
72
+ sh %{#{SUDO} jruby -S gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
73
+ end
74
+ end
75
+
76
+ desc 'Run specifications'
77
+ Spec::Rake::SpecTask.new(:spec) do |t|
78
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
79
+ t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
80
+ end
data/TODO ADDED
@@ -0,0 +1,5 @@
1
+ TODO
2
+ ====
3
+ Lots of stuff...
4
+ 1. Specing and completing of the Sham class
5
+ 2. ...
@@ -0,0 +1,30 @@
1
+ # Needed to import datamapper and other gems
2
+ require 'rubygems'
3
+ require 'pathname'
4
+
5
+ # Add all external dependencies for the plugin here
6
+ gem 'dm-core', '>=0.9.6'
7
+ gem 'dm-aggregates', '>=0.9.6'
8
+ require 'dm-core'
9
+ require 'dm-aggregates'
10
+
11
+ # Require plugin-files
12
+ require Pathname(__FILE__).dirname.expand_path / 'dm-machinist' / 'machinist.rb'
13
+ require Pathname(__FILE__).dirname.expand_path / 'dm-machinist' / 'sham.rb'
14
+
15
+ # From ActiveSupport
16
+ class Object
17
+ def returning(value)
18
+ yield(value)
19
+ value
20
+ end
21
+ end
22
+
23
+ # Include the plugin in Resource
24
+ module DataMapper
25
+ module Resource
26
+ module ClassMethods
27
+ include DataMapper::Machinist::DataMapperExtentions
28
+ end # module ClassMethods
29
+ end # module Resource
30
+ end # module DataMapper
@@ -0,0 +1,72 @@
1
+ module DataMapper
2
+ module Machinist
3
+ def self.with_save_nerfed
4
+ begin
5
+ @@nerfed = true
6
+ yield
7
+ ensure
8
+ @@nerfed = false
9
+ end
10
+ end
11
+
12
+ @@nerfed = false
13
+ def self.nerfed?
14
+ @@nerfed
15
+ end
16
+
17
+ module DataMapperExtentions
18
+ def blueprint(&blueprint)
19
+ @blueprint = blueprint
20
+ end
21
+
22
+ def make(attributes = {})
23
+ raise "No blueprint for class #{self}" if @blueprint.nil?
24
+ lathe = Lathe.new(self, attributes)
25
+ lathe.instance_eval(&@blueprint)
26
+ unless Machinist.nerfed?
27
+ lathe.object.save
28
+ lathe.object.reload
29
+ end
30
+ returning(lathe.object) do |object|
31
+ yield object if block_given?
32
+ end
33
+ end
34
+
35
+ def make_unsaved(attributes = {})
36
+ returning(Machinist.with_save_nerfed { make(attributes) }) do |object|
37
+ yield object if block_given?
38
+ end
39
+ end
40
+
41
+ class Lathe
42
+ def initialize(object, attributes)
43
+ @object = object.new
44
+ @assigned_attributes = []
45
+ attributes.each do |key, value|
46
+ @object.send("#{key}=", value)
47
+ @assigned_attributes << key
48
+ end
49
+ end
50
+
51
+ attr_reader :object
52
+
53
+ def method_missing(symbol, *args, &block)
54
+ if @assigned_attributes.include?(symbol)
55
+ @object.send(symbol)
56
+ else
57
+ value = if block
58
+ block.call
59
+ elsif args.first.is_a?(Hash) || args.empty?
60
+ symbol.to_s.camelize.constantize.make(args.first || {})
61
+ else
62
+ args.first
63
+ end
64
+ @object.send("#{symbol}=", value)
65
+ @assigned_attributes << symbol
66
+ end
67
+ end
68
+ end #Lathe
69
+
70
+ end #DataMapperExtentions
71
+ end #Machinist
72
+ end #DataMapper
@@ -0,0 +1,63 @@
1
+ class Sham
2
+ @@shams = {}
3
+
4
+ # Over-ride module's built-in name method, so we can re-use it for
5
+ # generating names. This is a bit of a no-no, but we get away with
6
+ # it in this context.
7
+ def self.name(*args, &block)
8
+ method_missing(:name, *args, &block)
9
+ end
10
+
11
+ def self.method_missing(symbol, *args, &block)
12
+ if block_given?
13
+ @@shams[symbol] = Sham.new(symbol, args.pop || {}, &block)
14
+ else
15
+ sham = @@shams[symbol]
16
+ raise "No sham defined for #{symbol}" if sham.nil?
17
+ sham.fetch_value
18
+ end
19
+ end
20
+
21
+ def self.reset
22
+ @@shams.values.each{ |v| v.reset }
23
+ end
24
+
25
+ def initialize(name, options = {}, &block)
26
+ @name = name
27
+ @generator = block
28
+ @offset = 0
29
+ @unique = options.has_key?(:unique) ? options[:unique] : true
30
+ generate_values(12)
31
+ end
32
+
33
+ def reset
34
+ @offset = 0
35
+ end
36
+
37
+ def fetch_value
38
+ # Generate more values if we need them.
39
+ if @offset >= @values.length
40
+ generate_values(2 * @values.length)
41
+ raise "Can't generate more unique values for Sham.#{@name}" if @offset >= @values.length
42
+ end
43
+ returning @values[@offset] do
44
+ @offset += 1
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def generate_values(count)
51
+ @values = seeded { (1..count).map(&@generator) }
52
+ @values.uniq! if @unique
53
+ end
54
+
55
+ def seeded
56
+ begin
57
+ srand(1)
58
+ yield
59
+ ensure
60
+ srand
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,7 @@
1
+ class Comment
2
+ include DataMapper::Resource
3
+ property :id, Serial
4
+ property :author, String
5
+ property :body, Text
6
+ belongs_to :post
7
+ end
data/spec/data/post.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Post
2
+ include DataMapper::Resource
3
+ property :id, Serial
4
+ property :title, String
5
+ property :body, Text
6
+ has n, :comments
7
+ end
@@ -0,0 +1,137 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + "/../lib/" + 'dm-machinist'
3
+
4
+ Post.blueprint do
5
+ title "An Example Post"
6
+ body { "The quick brown fox." }
7
+ end
8
+
9
+ Comment.blueprint do
10
+ post
11
+ author "Fred Bloggs"
12
+ body "Just a comment."
13
+ end
14
+
15
+
16
+
17
+
18
+ describe "Machinist" do
19
+
20
+ class BeSaved
21
+
22
+ def matches?(target)
23
+ @target = target
24
+ !@target.dirty?
25
+ end
26
+
27
+ def failure_message
28
+ "expected the object to be saved but it's not"
29
+ end
30
+
31
+ def negative_failure_message
32
+ "expected the object not to be saved but is is"
33
+ end
34
+ end
35
+
36
+ def be_saved
37
+ BeSaved.new
38
+ end
39
+
40
+ # class BeReloaded
41
+ # def matches?(target)
42
+ # @target = target
43
+ # @target.methods.each{|m| p m}
44
+ # end
45
+ #
46
+ # def failure_message
47
+ # "expected the object to be reloaded but it's not"
48
+ # end
49
+ #
50
+ # def negative_failure_message
51
+ # "expected the object to not be reloaded but it has been"
52
+ # end
53
+ # end
54
+ #
55
+ # def be_reloaded
56
+ # BeReloaded.new
57
+ # end
58
+
59
+ describe "make methd" do
60
+ before do
61
+ @post = Post.make
62
+ end
63
+
64
+ it "should set a field from a constant in the blueprint" do
65
+ @post.title.should == "An Example Post"
66
+ end
67
+
68
+ it "should set a field from a block in the blueprint" do
69
+ @post.body.should == "The quick brown fox."
70
+ end
71
+
72
+ it "should save the object" do
73
+ @post.should be_saved
74
+ end
75
+
76
+ # There doesn't seem to be a way to do this with DataMapper.
77
+ #We can reload but not test that it's been done(without hacking at least)
78
+ # I'll leave it pending
79
+ it "should reload the object" #do
80
+ # @post.should be_reloaded
81
+ # end
82
+ end
83
+
84
+ describe "make_unsaved method" do
85
+ before do
86
+ @comment = Comment.make_unsaved
87
+ end
88
+
89
+ it "should not save the object" do
90
+ @comment.should_not be_saved
91
+ end
92
+
93
+ # Pending till I work out how to do this with DataMapper
94
+ it "should not reload the object" #do
95
+ # @comment.should_not be_reloaded
96
+ # end
97
+
98
+ it "should not save associated objects" do
99
+ @comment.post.should_not be_saved
100
+ end
101
+
102
+ # Pending till I work out how to do this with DataMapper
103
+ it "should not reload associated objects" #do
104
+ # @comment.post.should_not be_reloaded
105
+ # end
106
+ end
107
+
108
+ it "should override a field from the blueprint with a parameter" do
109
+ post = Post.make(:title => "A Different Title")
110
+ post.title.should == "A Different Title"
111
+ end
112
+
113
+ it "should create an associated object for a field with no arguments in the blueprint" do
114
+ comment = Comment.make
115
+ comment.post.should_not be_nil
116
+ end
117
+
118
+ it "should allow passing a block to make" do
119
+ comments = nil
120
+ post = Post.make do |post|
121
+ comments = (1..3).map { Comment.make(:post => post) }
122
+ end
123
+ post.should be_an_instance_of(Post)
124
+ comments.should_not be_nil
125
+ comments.each {|comment| comment.post.should == post }
126
+ end
127
+
128
+ it "should not nerf make within a block passed to make_unsaved" do
129
+ comment = nil
130
+ post = Post.make_unsaved do |post|
131
+ comment = Comment.make(:post => post)
132
+ end
133
+ post.should_not be_saved
134
+ comment.should be_saved
135
+ comment.post.should == post
136
+ end
137
+ end
data/spec/helpers.rb ADDED
@@ -0,0 +1,46 @@
1
+ class String
2
+ # Ripped from Rails Inflectors
3
+ def camelize(first_letter_in_uppercase = true)
4
+ lower_case_and_underscored_word = self
5
+ if first_letter_in_uppercase
6
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
7
+ else
8
+ lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
9
+ end
10
+ end
11
+
12
+ # Ripped from Rails Inflectors
13
+ def constantize
14
+ camel_cased_word = self
15
+ names = camel_cased_word.split('::')
16
+ names.shift if names.empty? || names.first.empty?
17
+
18
+ constant = Object
19
+ names.each do |name|
20
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
21
+ end
22
+ constant
23
+ end
24
+ end
25
+ module CustomMatchers
26
+ class DataMapperBeSaved
27
+ def initialize(expected)
28
+ @expected = expected
29
+ end
30
+
31
+ def matches?(target)
32
+ @target = target
33
+ p @target
34
+ p @expected
35
+ #satisfy expectation here
36
+ end
37
+
38
+ def failure_message
39
+ "expected #{@expected} but got #{@target}"
40
+ end
41
+
42
+ def negative_failure_message
43
+ "expected #{@expected} to not be #{@target}"
44
+ end
45
+ end
46
+ end
data/spec/sham_spec.rb ADDED
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ Sham.random { rand }
4
+ Sham.half_index {|index| index/2 }
5
+ Sham.coin_toss(:unique => false) {|index| index % 2 == 1 ? 'heads' : 'tails' }
6
+ Sham.limited {|index| index%10 }
7
+ Sham.index {|index| index }
8
+ Sham.name {|index| index }
9
+
10
+ describe Sham do
11
+ it "should ensure generated values are unique" do
12
+ values = (1..10).map { Sham.half_index }
13
+ values.should == (0..9).to_a
14
+ end
15
+
16
+ it "should generate non-unique values when asked" do
17
+ values = (1..4).map { Sham.coin_toss }
18
+ values.should == ['heads', 'tails', 'heads', 'tails']
19
+ end
20
+
21
+ it "should generate more than a dozen values" do
22
+ values = (1..25).map { Sham.index }
23
+ values.should == (1..25).to_a
24
+ end
25
+
26
+ it "should generate the same sequence of values after a reset" do
27
+ values1 = (1..10).map { Sham.random }
28
+ Sham.reset
29
+ values2 = (1..10).map { Sham.random }
30
+ values2.should == values1
31
+ end
32
+
33
+ it "should die when it runs out of unique values" do
34
+ lambda {
35
+ (1..100).map { Sham.limited }
36
+ }.should raise_error(RuntimeError)
37
+ end
38
+
39
+ it "should allow over-riding the name method" do
40
+ Sham.name.should == 1
41
+ end
42
+ end
data/spec/spec.opts ADDED
File without changes
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.1.3'
3
+ require 'spec'
4
+ require 'pathname'
5
+ require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-machinist'
6
+ require Pathname(__FILE__).dirname.expand_path / 'helpers'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.include(CustomMatchers)
10
+ end
11
+
12
+
13
+ def load_driver(name, default_uri)
14
+ return false if ENV['ADAPTER'] != name.to_s
15
+
16
+ lib = "do_#{name}"
17
+
18
+ begin
19
+ gem lib, '>=0.9.5'
20
+ require lib
21
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
22
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
23
+ true
24
+ rescue Gem::LoadError => e
25
+ warn "Could not load #{lib}: #{e}"
26
+ false
27
+ end
28
+ end
29
+
30
+ ENV['ADAPTER'] ||= 'sqlite3'
31
+
32
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
33
+
34
+ require "dm-types"
35
+ require "dm-aggregates"
36
+
37
+ # Create models
38
+ require Pathname(__FILE__).dirname.expand_path / 'data' / 'comment'
39
+ require Pathname(__FILE__).dirname.expand_path / 'data' / 'post'
40
+
41
+ # Migrate to sqlite3 in memory
42
+ DataMapper.auto_migrate!
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snepo-dm-machinist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cameron Barrie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dm-core
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description: DataMapper plugin for machinist
25
+ email: cameron@snepo
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ - LICENSE
33
+ - TODO
34
+ files:
35
+ - lib/dm-machinist/machinist.rb
36
+ - lib/dm-machinist/sham.rb
37
+ - lib/dm-machinist.rb
38
+ - spec/data/comment.rb
39
+ - spec/data/post.rb
40
+ - spec/dm_machinist_spec.rb
41
+ - spec/helpers.rb
42
+ - spec/sham_spec.rb
43
+ - spec/spec_helper.rb
44
+ - spec/spec.opts
45
+ - Rakefile
46
+ - README
47
+ - LICENSE
48
+ - TODO
49
+ has_rdoc: true
50
+ homepage: http://github.com/snepo/dm-machinist/tree/master
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.2.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: DataMapper plugin for machinist
75
+ test_files: []
76
+