svenfuchs-stubby 0.0.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/MIT-LICENSE +20 -0
- data/README.markdown +242 -0
- data/lib/stubby.rb +54 -0
- data/lib/stubby/base.rb +38 -0
- data/lib/stubby/class_factory.rb +76 -0
- data/lib/stubby/definition.rb +78 -0
- data/lib/stubby/handle.rb +17 -0
- data/lib/stubby/has_many_proxy.rb +37 -0
- data/lib/stubby/instances.rb +62 -0
- data/lib/stubby/loader.rb +23 -0
- data/spec/no_rails.rb +102 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/stubby_spec.rb +205 -0
- data/spec/stubs/site.rb +34 -0
- data/stubby.gemspec +12 -0
- metadata +69 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2008 Sven Fuchs
|
|
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.markdown
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
## Stubby - lightweight and fast stubbing
|
|
2
|
+
|
|
3
|
+
Stubby is a lightweight and fast stubbing framework that was
|
|
4
|
+
|
|
5
|
+
* designed to help with the repetitive work of setting up stub scenarios for specifying/testing and
|
|
6
|
+
* optimized for speed and ease of use.
|
|
7
|
+
|
|
8
|
+
Stubby does not track references to stubbed methods on real objects (like
|
|
9
|
+
RSpec's `stub!` method does it which is used by `stub_model` & co).
|
|
10
|
+
|
|
11
|
+
Instead it dynamically creates a Ruby class for every stub object once. These
|
|
12
|
+
classes will then be instantiated for every specification (or test). This
|
|
13
|
+
circumvents the overhead of adding, tracking and removing lots of methods
|
|
14
|
+
and can - depending on your stubs and specs - result in significant speed
|
|
15
|
+
improvements.
|
|
16
|
+
|
|
17
|
+
## Stubs setup
|
|
18
|
+
|
|
19
|
+
Stubby comes with a slick DSL to make it easy to describe stub setups. Basically
|
|
20
|
+
there are three main statements:
|
|
21
|
+
|
|
22
|
+
* `define Model &block` - defines a stub base class that camouflages as a model class
|
|
23
|
+
* `instance :key`, methods - defines a concrete stub that inherits from a stub base class and will be instantiated on request
|
|
24
|
+
* `scenario :key &block` - defines a code block that can be executed from your spec (e.g. in before :each blocks)
|
|
25
|
+
|
|
26
|
+
Things are pretty much readable and self-explaining:
|
|
27
|
+
|
|
28
|
+
define Site do
|
|
29
|
+
methods :save => true, :destroy => true, :active? => true
|
|
30
|
+
instance :homepage, :id => 1
|
|
31
|
+
instance :customer, :id => 2, :active? => false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
This creates a stub base class that camouflages as your `Site` model (i.e. it
|
|
35
|
+
behaves as this model when methods like `class`, `is_a?`, `===` etc. are sent to it).
|
|
36
|
+
|
|
37
|
+
For the Site stub base class there are a couple of methods defined (stubbed)
|
|
38
|
+
that you can call in your specs and that will return the given values (like
|
|
39
|
+
`save` will return `true`).
|
|
40
|
+
|
|
41
|
+
It also defines two stub instances for this class that can be accessed through
|
|
42
|
+
the keys `:homepage` and `:customer`. These both respond to the `id` method. The
|
|
43
|
+
`:customer` instance additionally overwrites the `active?` value that's present
|
|
44
|
+
in the base class.
|
|
45
|
+
|
|
46
|
+
## Access stubs
|
|
47
|
+
|
|
48
|
+
You can access these stubs from
|
|
49
|
+
|
|
50
|
+
* the stub base class definition block (like the one above)
|
|
51
|
+
* scenario blocks (see below)
|
|
52
|
+
* your specs
|
|
53
|
+
|
|
54
|
+
To access them you can use the following method apis. With the Site stub base
|
|
55
|
+
class above being defined:
|
|
56
|
+
|
|
57
|
+
stub_site # returns the first defined stub instance (i.e. :homepage)
|
|
58
|
+
stub_site(:homepage) # returns the :homepage stub instance
|
|
59
|
+
stub_sites # returns an array with all defined stub instances
|
|
60
|
+
stub_sites(:homepage) # returns an array containing the :homepage stub instance
|
|
61
|
+
|
|
62
|
+
You can also use the `lookup(:site, :homepage)` method in the same way in case you
|
|
63
|
+
need it.
|
|
64
|
+
|
|
65
|
+
These accessors can be used from within the stub base class definition like
|
|
66
|
+
this:
|
|
67
|
+
|
|
68
|
+
define Site do
|
|
69
|
+
instance :homepage, :next => stub_site(:customer)
|
|
70
|
+
instance :customer, :next => stub_site(:homepage)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
This creates a method `next` on both instances which return the respective
|
|
74
|
+
stub instances.
|
|
75
|
+
|
|
76
|
+
For convenience you can pass arrays of method names as keys of method
|
|
77
|
+
hashs:
|
|
78
|
+
|
|
79
|
+
define Site do
|
|
80
|
+
methods [:save, :destroy, :active?] => true # same result as above
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
## Stubbing ActiveRecord associations
|
|
84
|
+
|
|
85
|
+
To make stubbing of more complex ActiveRecord models easier there are also the
|
|
86
|
+
following helpers:
|
|
87
|
+
|
|
88
|
+
define Site do
|
|
89
|
+
has_many :sections, [:find, :build] => stub_section,
|
|
90
|
+
[:paginate] => stub_sections
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
This has the expected effects. With this definition in place the following
|
|
94
|
+
lines will now all return the same section stub object:
|
|
95
|
+
|
|
96
|
+
stub_site.sections.first
|
|
97
|
+
stub_site.sections.find
|
|
98
|
+
stub_site.sections.build
|
|
99
|
+
stub_site.sections.paginate.first
|
|
100
|
+
|
|
101
|
+
Watch the first of these lines! As you can see the sections `has_many_proxy`
|
|
102
|
+
will automatically be populated with the array stub_sections if you don't
|
|
103
|
+
specify anything explicitely. I.e. that's the same as:
|
|
104
|
+
|
|
105
|
+
define Site do
|
|
106
|
+
has_many :sections, stub_sections,
|
|
107
|
+
[:find, :build] => stub_section,
|
|
108
|
+
[:paginate] => stub_sections
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
If you want the sections array to return something else, though you can specify
|
|
113
|
+
it:
|
|
114
|
+
|
|
115
|
+
define Site do
|
|
116
|
+
has_many :sections, stub_sections(:root),
|
|
117
|
+
[:find, :build] => stub_section,
|
|
118
|
+
[:paginate] => stub_sections
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
There are also the `has_one` and `belongs_to` statements which also behave in
|
|
122
|
+
the expected way:
|
|
123
|
+
|
|
124
|
+
define Site do
|
|
125
|
+
has_one :api_key
|
|
126
|
+
belongs_to :user
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
The `belongs_to` statement implicitely defines the `user_id` method so that it
|
|
130
|
+
returns the id defined for the `stub_user` stub.
|
|
131
|
+
|
|
132
|
+
## Scenarios
|
|
133
|
+
|
|
134
|
+
A scenario is simply a code block that can be accessed and executed from your
|
|
135
|
+
specs. You can define a scenario like this:
|
|
136
|
+
|
|
137
|
+
scenario :default do
|
|
138
|
+
@site = stub_site
|
|
139
|
+
Site.stub!(:find).and_return @site
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
You can then invoke this scenario from your Spec like so:
|
|
143
|
+
|
|
144
|
+
before :each do
|
|
145
|
+
scenario :default # pass as many scenario names as you like
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
Because the scenario code block is evaluated in the scope of the before :each
|
|
149
|
+
block the @site instance variable is then accessible from within your specs.
|
|
150
|
+
|
|
151
|
+
## Installation and Setup
|
|
152
|
+
|
|
153
|
+
Install Stubby as a usual Rails plugin and add the following line somewhere
|
|
154
|
+
in your specs where it gets executed. The best place for this is probably
|
|
155
|
+
your `spec/spec_helper.rb` file:
|
|
156
|
+
|
|
157
|
+
Stubby::Loader.load
|
|
158
|
+
|
|
159
|
+
This requires the stubby code and loads the stub definition files once.
|
|
160
|
+
|
|
161
|
+
## Stub definition files
|
|
162
|
+
|
|
163
|
+
Stubby assumes that you have on directory where you store your stub definition
|
|
164
|
+
files (and nothing else). A stub definition file is just a ruby file that contains
|
|
165
|
+
all or some of your stub base class and scenario definitions like described above.
|
|
166
|
+
You can structure your files as you like.
|
|
167
|
+
|
|
168
|
+
By default Stubby assumes that you
|
|
169
|
+
|
|
170
|
+
* use a directory named `stubs/`
|
|
171
|
+
* that is located beneath the directory of your `spec_helper.rb` file
|
|
172
|
+
* that starts requires the stubby code
|
|
173
|
+
|
|
174
|
+
Stubby then tries to guess the correct directory location. If it gets things
|
|
175
|
+
wrong or if you want to use a different setup you can specify the directory
|
|
176
|
+
where it looks for your stub definitions files like this:
|
|
177
|
+
|
|
178
|
+
Stubby.directory = 'path/to/stubs'
|
|
179
|
+
|
|
180
|
+
## Limitations
|
|
181
|
+
|
|
182
|
+
Be aware that Stubby is a framework for plain stubs. That means that it doesn't
|
|
183
|
+
make a single attemp of looking at your model classes and trying to mimick
|
|
184
|
+
their behaviour. This is different from what other solutions do (which might
|
|
185
|
+
instantiate full functional model objects, like fixtures, or at least mimick
|
|
186
|
+
the model's attributes or similar).
|
|
187
|
+
|
|
188
|
+
Thus you need to define every single method that your specs call. Personally
|
|
189
|
+
I feel that this is an advantage because it makes visible *what* portions
|
|
190
|
+
of the code my specs actually run (and what they shouldn't). But your mileage
|
|
191
|
+
may vary.
|
|
192
|
+
|
|
193
|
+
Also, with Stubby it is currently not possible:
|
|
194
|
+
|
|
195
|
+
* define class methods in stub base classes. You still need to stub class methods manually. The scenario block is a good place to stub common methods though.
|
|
196
|
+
* stub methods in a way so that they return different results depending on a certain parameter passed. You can still use RSpec `stub!(:method).with(:param).and_return(result)` for that though.
|
|
197
|
+
|
|
198
|
+
If you want to help me with these, please let me know :)
|
|
199
|
+
|
|
200
|
+
Also, this code is brand new. Consider this an alpha release. I have written
|
|
201
|
+
this library to clean-up and speed-up the 1000+ specs of a project that I'm
|
|
202
|
+
currently working on. For me Stubby works quite well in this project's specs
|
|
203
|
+
and it runs these specs in less than 30 seconds (which is less then 50% of
|
|
204
|
+
what a solution with `mock_model` needed).
|
|
205
|
+
|
|
206
|
+
That doesn't mean that there aren't any major bugs or problems though. Please
|
|
207
|
+
send me your pull requests, patches or just bug requests if so.
|
|
208
|
+
|
|
209
|
+
## Similar solutions
|
|
210
|
+
|
|
211
|
+
There are a couple of similar solutions that aim at the same problem but all
|
|
212
|
+
go down different routes. I highly recommend to have a look at:
|
|
213
|
+
|
|
214
|
+
* [Fixture Scenarios](http://code.google.com/p/fixture-scenarios/) by Tom Preston-Werner, extends Rails' native fixtures
|
|
215
|
+
* [Model Stubbing](http://ar-code.svn.engineyard.com/plugins/model_stubbing/README) by Rick Olson, creates in-memory versions of your models
|
|
216
|
+
* [Exemplar](http://www.bofh.org.uk/articles/2007/08/05/doing-the-fixture-thing) by Piers Cawley
|
|
217
|
+
|
|
218
|
+
## The name
|
|
219
|
+
|
|
220
|
+
I was told that in Canada a "stubby" is a certain kind of beer bottle. The
|
|
221
|
+
[Wikipedia page](http://en.wikipedia.org/wiki/Beer_bottle) lists a couple of
|
|
222
|
+
advantages that perfectly match the reasons why I've written this library in
|
|
223
|
+
the first place:
|
|
224
|
+
|
|
225
|
+
* easier to handle
|
|
226
|
+
* chills faster
|
|
227
|
+
* less breakage
|
|
228
|
+
* lighter in weight
|
|
229
|
+
* less storage space
|
|
230
|
+
* lower center of gravity
|
|
231
|
+
|
|
232
|
+
Now, given that I've thought of this name before I knew this page I think
|
|
233
|
+
that's pretty funny and a perfect name for the library.
|
|
234
|
+
|
|
235
|
+
Also, I'm not a native English speaker but I've been informed on #rubyonrails
|
|
236
|
+
that "Stubby" might also have some sexual connotations in some parts of the
|
|
237
|
+
English speaking world.
|
|
238
|
+
|
|
239
|
+
## Etc
|
|
240
|
+
|
|
241
|
+
Authors: [Sven Fuchs](http://www.artweb-design.de) <svenfuchs at artweb-design dot de>
|
|
242
|
+
License: MIT
|
data/lib/stubby.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'stubby/handle'
|
|
2
|
+
require 'stubby/instances'
|
|
3
|
+
require 'stubby/loader'
|
|
4
|
+
|
|
5
|
+
require 'stubby/base'
|
|
6
|
+
require 'stubby/definition'
|
|
7
|
+
require 'stubby/class_factory'
|
|
8
|
+
require 'stubby/has_many_proxy'
|
|
9
|
+
|
|
10
|
+
module Stubby
|
|
11
|
+
module Classes; end
|
|
12
|
+
|
|
13
|
+
mattr_accessor :directory
|
|
14
|
+
mattr_accessor :scenarios, :base_definitions, :instance_definitions
|
|
15
|
+
@@scenarios = {}
|
|
16
|
+
@@base_definitions = {}
|
|
17
|
+
@@instance_definitions = {}
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def included(base)
|
|
21
|
+
base.after :each do Stubby::Instances.clear! end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def base_definition(name)
|
|
25
|
+
@@base_definitions[name]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def instance_definitions(name)
|
|
29
|
+
@@instance_definitions[name] ||= {}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def scenario(*names)
|
|
34
|
+
names.each do |name|
|
|
35
|
+
raise "scenario :#{name} is not defined" unless scenarios[name]
|
|
36
|
+
instance_eval &scenarios[name]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def lookup(key, *args)
|
|
41
|
+
Stubby::Instances.lookup(key.to_s, *args)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def method_missing(name, *args)
|
|
45
|
+
return lookup($1, *args) if name.to_s =~ /^stub_(.*)/
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# trying to guess the stub definitions directory, overwrite this setting
|
|
51
|
+
# as needed in your spec_helper.rb
|
|
52
|
+
if filename = caller.detect{|line| line !~ /rubygems|dependencies/ }
|
|
53
|
+
Stubby.directory = File.expand_path File.dirname(filename) + "/stubs"
|
|
54
|
+
end
|
data/lib/stubby/base.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
class Base
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :original_class
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
returning super do |object|
|
|
8
|
+
object.instance_variable_set :@original_class, original_class
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def base_class
|
|
13
|
+
original_class.base_class
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_param
|
|
18
|
+
id.to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def class
|
|
22
|
+
@original_class
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def new_record?
|
|
26
|
+
id.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def is_a?(klass)
|
|
30
|
+
@original_class.ancestors.include? klass
|
|
31
|
+
end
|
|
32
|
+
alias :kind_of? :is_a?
|
|
33
|
+
|
|
34
|
+
def instance_of?(klass)
|
|
35
|
+
@original_class == klass
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
class ClassFactory # too bad, we can't extend ::Class
|
|
3
|
+
class << self
|
|
4
|
+
def create(base_class, *args, &block)
|
|
5
|
+
klass = new(base_class, *args, &block).klass
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :klass
|
|
10
|
+
|
|
11
|
+
def initialize(base_class, name, original_class, methods = {}, &block)
|
|
12
|
+
@klass = ::Class.new(base_class)
|
|
13
|
+
target = base_class == Stubby::Base ? Stubby::Classes : base_class
|
|
14
|
+
target.const_set name.sub('::', ''), @klass
|
|
15
|
+
|
|
16
|
+
@klass.original_class = original_class
|
|
17
|
+
define_methods methods
|
|
18
|
+
instance_eval &block if block
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def has_many(names, *args)
|
|
22
|
+
methods = args.extract_options!
|
|
23
|
+
Array(names).each do |name|
|
|
24
|
+
values = args.last || lookup(name, :all)
|
|
25
|
+
as = methods.delete(:_as) || name
|
|
26
|
+
proxy = Handle.new{ HasManyProxy.new name, values, methods }
|
|
27
|
+
define_method (as || name), proxy
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def has_one(names, object = nil)
|
|
32
|
+
Array(names).each do |name|
|
|
33
|
+
object ||= lookup(name, :first)
|
|
34
|
+
define_method name, object
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def belongs_to(names, object = nil)
|
|
39
|
+
Array(names).each do |name|
|
|
40
|
+
object ||= lookup(name, :first)
|
|
41
|
+
define_method name, object
|
|
42
|
+
define_method :"#{name}_id", Handle.new{ object.resolve.id }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def instance(*args)
|
|
47
|
+
definition = Definition.new :base_class => @klass,
|
|
48
|
+
:methods => args.extract_options!,
|
|
49
|
+
:name => args.shift.to_s.camelize
|
|
50
|
+
definition.create!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def lookup(key, *args)
|
|
54
|
+
Handle.new{ Stubby::Instances.lookup(key.to_s, *args) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def method_missing(name, *args)
|
|
58
|
+
return lookup($1, *args) if name.to_s =~ /^stub_(.*)/
|
|
59
|
+
super
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def define_methods(methods)
|
|
63
|
+
methods.each do |names, value|
|
|
64
|
+
Array(names).each{|name| define_method name, value}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
alias :methods :define_methods
|
|
68
|
+
|
|
69
|
+
def define_method(name, value)
|
|
70
|
+
@klass.send :define_method, name do |*args|
|
|
71
|
+
@__values ||= {}
|
|
72
|
+
@__values[name] ||= value.respond_to?(:resolve) ? value.resolve : value
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
class Definition
|
|
3
|
+
attr_reader :name, :class, :original_class, :base_class, :methods
|
|
4
|
+
attr_accessor :default_instance_key
|
|
5
|
+
|
|
6
|
+
def initialize(attributes = {})
|
|
7
|
+
attributes.each{|name, value| instance_variable_set :"@#{name}", value }
|
|
8
|
+
register
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def register
|
|
12
|
+
if base_class == Base
|
|
13
|
+
Stubby.base_definitions[key] = self
|
|
14
|
+
else
|
|
15
|
+
base_definition.default_instance_key ||= instance_key
|
|
16
|
+
Stubby.instance_definitions(base_key)[instance_key] = self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def name
|
|
21
|
+
@name ||= original_class.name if original_class
|
|
22
|
+
@name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def base_class
|
|
26
|
+
@base_class ||= Base
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def base_key
|
|
30
|
+
@base_key ||= base_class.name.demodulize
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def base_definition
|
|
34
|
+
@base_definition ||= Stubby.base_definitions[base_key]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def key
|
|
38
|
+
@key ||= name.to_s.classify.sub('::', '')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def instance_key
|
|
42
|
+
@instance_key ||= name.demodulize.underscore.to_sym
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def original_class
|
|
46
|
+
@original_class ||= base_class.original_class if base_class
|
|
47
|
+
@original_class
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def methods
|
|
51
|
+
@methods ||= {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create!(&block)
|
|
55
|
+
@class = ClassFactory.create(base_class, name, original_class, methods, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def instantiate(key = nil)
|
|
59
|
+
if key == :all
|
|
60
|
+
instance_definitions.collect{|key, definition| definition.instantiate }
|
|
61
|
+
elsif key
|
|
62
|
+
key = default_instance_key if key == :first
|
|
63
|
+
instance_definition(key).instantiate
|
|
64
|
+
else
|
|
65
|
+
Instances.by_key(base_class)[instance_key] or
|
|
66
|
+
Instances.store(base_class, self.class.new, instance_key)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def instance_definitions
|
|
71
|
+
Stubby.instance_definitions(key)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def instance_definition(instance_key)
|
|
75
|
+
Stubby.instance_definitions(key)[instance_key]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
class Handle < Proc
|
|
3
|
+
def initialize(&block)
|
|
4
|
+
super &block
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def resolve
|
|
8
|
+
result = call
|
|
9
|
+
result = result.resolve if result.respond_to? :resolve
|
|
10
|
+
result
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def inspect
|
|
14
|
+
"<Stubby::Handle:#{__id__}>"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
class HasManyProxy < Array
|
|
3
|
+
attr_accessor :name
|
|
4
|
+
|
|
5
|
+
def initialize(name, values, methods)
|
|
6
|
+
@name = name
|
|
7
|
+
@values = values
|
|
8
|
+
define_methods methods
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def resolve
|
|
12
|
+
if @values.respond_to?(:resolve)
|
|
13
|
+
replace @values.resolve
|
|
14
|
+
else
|
|
15
|
+
replace @values.collect{|object| object.resolve if object.respond_to?(:resolve) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def define_methods(methods)
|
|
20
|
+
methods.each do |names, value|
|
|
21
|
+
Array(names).each{|name| define_method name, value}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def define_method(name, value)
|
|
26
|
+
(class << self; self; end).send :define_method, name do |*args|
|
|
27
|
+
@__values ||= {}
|
|
28
|
+
@__values[name] ||= value.respond_to?(:resolve) ? value.resolve : value
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def inspect
|
|
33
|
+
"<HasManyProxy:#{name.to_s.camelize}:#{object_id} #{super}>"
|
|
34
|
+
end
|
|
35
|
+
alias :to_s :inspect
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
module Instances
|
|
3
|
+
class InstanceNotFound < RuntimeError; end
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
def lookup(klass, key = nil)
|
|
7
|
+
if (singular = klass.singularize) != klass
|
|
8
|
+
klass = singular
|
|
9
|
+
method = :find_all
|
|
10
|
+
key ||= :all
|
|
11
|
+
else
|
|
12
|
+
method = :find_one
|
|
13
|
+
key ||= :first
|
|
14
|
+
end
|
|
15
|
+
method = :find_all if key == :all
|
|
16
|
+
|
|
17
|
+
definition = Stubby.base_definition(klass.classify) or raise "could not find base_definition for #{klass.classify}"
|
|
18
|
+
send(method, definition, key)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_all(definition, key)
|
|
22
|
+
key = nil if key == :all
|
|
23
|
+
if key
|
|
24
|
+
[find_one(definition, key)].compact
|
|
25
|
+
else
|
|
26
|
+
definition.instantiate(:all) unless complete[definition.class]
|
|
27
|
+
complete[definition.class] = true
|
|
28
|
+
by_class(definition.class)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def find_one(definition, key)
|
|
33
|
+
key = definition.default_instance_key if key == :first
|
|
34
|
+
by_key(definition.class)[key] || definition.instantiate(key)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def by_class(klass)
|
|
38
|
+
@by_class ||= {}
|
|
39
|
+
@by_class[klass] ||= []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def by_key(klass)
|
|
43
|
+
@by_key ||= {}
|
|
44
|
+
@by_key[klass] ||= {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def complete
|
|
48
|
+
@complete ||= {}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def store(klass, object, key)
|
|
52
|
+
by_class(klass) << object
|
|
53
|
+
by_key(klass)[key] = object
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def clear!
|
|
57
|
+
@by_class = {}
|
|
58
|
+
@by_key = {}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Stubby
|
|
2
|
+
module Loader
|
|
3
|
+
class << self
|
|
4
|
+
def scenario(name, &block)
|
|
5
|
+
Stubby.scenarios[name] = block
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def define(original_class, &block)
|
|
9
|
+
definition = Definition.new :original_class => original_class
|
|
10
|
+
definition.create! &block
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load
|
|
14
|
+
unless @loaded
|
|
15
|
+
Dir["#{Stubby.directory}/*"].each do |filename|
|
|
16
|
+
instance_eval IO.read(filename), filename
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
@loaded = true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/spec/no_rails.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
def returning(value)
|
|
3
|
+
yield(value)
|
|
4
|
+
value
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class String
|
|
9
|
+
def singularize
|
|
10
|
+
sub /s$/, ''
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def underscore
|
|
14
|
+
gsub(/::/, '/').
|
|
15
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
16
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
17
|
+
tr("-", "_").
|
|
18
|
+
downcase
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def camelize
|
|
22
|
+
to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
|
23
|
+
end
|
|
24
|
+
alias :classify :camelize
|
|
25
|
+
|
|
26
|
+
def constantize
|
|
27
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
|
|
28
|
+
raise NameError, "#{inspect} is not a valid constant name!"
|
|
29
|
+
end
|
|
30
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def demodulize
|
|
34
|
+
gsub(/^.*::/, '')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Array
|
|
39
|
+
def extract_options!
|
|
40
|
+
last.is_a?(::Hash) ? pop : {}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Hash
|
|
45
|
+
# Returns a new hash with only the given keys.
|
|
46
|
+
def slice(*keys)
|
|
47
|
+
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
|
48
|
+
reject { |key,| !allowed.include?(key) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Replaces the hash with only the given keys.
|
|
52
|
+
def slice!(*keys)
|
|
53
|
+
replace(slice(*keys))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Module
|
|
58
|
+
def mattr_reader(*syms)
|
|
59
|
+
syms.each do |sym|
|
|
60
|
+
next if sym.is_a?(Hash)
|
|
61
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
|
62
|
+
unless defined? @@#{sym}
|
|
63
|
+
@@#{sym} = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.#{sym}
|
|
67
|
+
@@#{sym}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def #{sym}
|
|
71
|
+
@@#{sym}
|
|
72
|
+
end
|
|
73
|
+
EOS
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def mattr_writer(*syms)
|
|
78
|
+
options = syms.extract_options!
|
|
79
|
+
syms.each do |sym|
|
|
80
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
|
81
|
+
unless defined? @@#{sym}
|
|
82
|
+
@@#{sym} = nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.#{sym}=(obj)
|
|
86
|
+
@@#{sym} = obj
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
#{"
|
|
90
|
+
def #{sym}=(obj)
|
|
91
|
+
@@#{sym} = obj
|
|
92
|
+
end
|
|
93
|
+
" unless options[:instance_writer] == false }
|
|
94
|
+
EOS
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def mattr_accessor(*syms)
|
|
99
|
+
mattr_reader(*syms)
|
|
100
|
+
mattr_writer(*syms)
|
|
101
|
+
end
|
|
102
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
# require File.dirname(__FILE__) + "/no_rails"
|
|
3
|
+
# require 'spec'
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../lib")
|
|
6
|
+
|
|
7
|
+
require "stubby"
|
|
8
|
+
|
|
9
|
+
Stubby.directory = File.dirname(__FILE__) + "/stubs"
|
|
10
|
+
|
|
11
|
+
Spec::Runner.configure do |config|
|
|
12
|
+
# == Fixtures
|
|
13
|
+
#
|
|
14
|
+
# You can declare fixtures for each example_group like this:
|
|
15
|
+
# describe "...." do
|
|
16
|
+
# fixtures :table_a, :table_b
|
|
17
|
+
#
|
|
18
|
+
# Alternatively, if you prefer to declare them only once, you can
|
|
19
|
+
# do so right here. Just uncomment the next line and replace the fixture
|
|
20
|
+
# names with your fixtures.
|
|
21
|
+
#
|
|
22
|
+
# config.global_fixtures = :table_a, :table_b
|
|
23
|
+
#
|
|
24
|
+
# If you declare global fixtures, be aware that they will be declared
|
|
25
|
+
# for all of your examples, even those that don't use them.
|
|
26
|
+
#
|
|
27
|
+
# == Mock Framework
|
|
28
|
+
#
|
|
29
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
|
30
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
|
31
|
+
#
|
|
32
|
+
# config.mock_with :mocha
|
|
33
|
+
# config.mock_with :flexmock
|
|
34
|
+
# config.mock_with :rr
|
|
35
|
+
end
|
data/spec/stubby_spec.rb
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class Base
|
|
5
|
+
attr_accessor :id
|
|
6
|
+
def has_attribute?(name)
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
def inspect
|
|
10
|
+
"<#{self.class.name}:#{object_id}>"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Site < ActiveRecord::Base; end
|
|
16
|
+
class Section < ActiveRecord::Base; end
|
|
17
|
+
class User < ActiveRecord::Base; end
|
|
18
|
+
class ApiKey < ActiveRecord::Base; end
|
|
19
|
+
|
|
20
|
+
Stubby::Loader.load
|
|
21
|
+
|
|
22
|
+
describe "Stubby" do
|
|
23
|
+
include Stubby
|
|
24
|
+
|
|
25
|
+
before :each do
|
|
26
|
+
scenario :site
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "base class creation" do
|
|
30
|
+
it "creates a class Stubby::Classes::Site" do
|
|
31
|
+
lambda{ Stubby::Classes::Site }.should_not raise_error
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "creates a class Stubby::Classes::Section" do
|
|
35
|
+
lambda{ Stubby::Classes::Section }.should_not raise_error
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "an instance of the site base class" do
|
|
39
|
+
it "responds to :save" do
|
|
40
|
+
Stubby::Classes::Site.new.should respond_to(:save)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "responds to :destroy" do
|
|
44
|
+
Stubby::Classes::Site.new.should respond_to(:destroy)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "responds to :active?" do
|
|
48
|
+
Stubby::Classes::Site.new.should respond_to(:active?)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "responds to :sections" do
|
|
52
|
+
Stubby::Classes::Site.new.should respond_to(:sections)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "responds to :user" do
|
|
56
|
+
Stubby::Classes::Site.new.should respond_to(:user)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "responds to :api_key" do
|
|
60
|
+
Stubby::Classes::Site.new.should respond_to(:api_key)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "instance class creation" do
|
|
66
|
+
it "creates a class Stubby::Classes::Site::Site" do
|
|
67
|
+
lambda{ Stubby::Classes::Site::Site }.should_not raise_error
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "creates a class Stubby::Classes::Section::Root" do
|
|
71
|
+
lambda{ Stubby::Classes::Section::Root }.should_not raise_error
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "an instance of the site instance class" do
|
|
75
|
+
it "responds to :save (as inherited from its base class)" do
|
|
76
|
+
Stubby::Classes::Site::Site.new.should respond_to(:save)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "responds to :name" do
|
|
80
|
+
Stubby::Classes::Site::Site.new.should respond_to(:name)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "stub lookup" do
|
|
86
|
+
describe "with a singular lookup method" do
|
|
87
|
+
it "returns the first defined stub if no key is given" do
|
|
88
|
+
stub_site.name.should == 'site'
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "returns the stub referenced by a given key" do
|
|
92
|
+
stub_site(:another).name.should == 'another'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "returns a collection of all stubs when :all is given as a key" do
|
|
96
|
+
stub_site(:all).should == [stub_site, stub_site(:another)]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "with a plural lookup method" do
|
|
101
|
+
it "returns an array containing all stubs when no key is given" do
|
|
102
|
+
sites = stub_sites
|
|
103
|
+
sites.size.should == 2
|
|
104
|
+
sites.should == [stub_site, stub_site(:another)]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "returns an array containing all stubs when :all is given as a key" do
|
|
108
|
+
sites = stub_sites(:all)
|
|
109
|
+
sites.size.should == 2
|
|
110
|
+
sites.should == [stub_site, stub_site(:another)]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "returns an array containing the referenced stub when a key is given" do
|
|
114
|
+
sites = stub_sites(:another)
|
|
115
|
+
sites.size.should == 1
|
|
116
|
+
sites.should == [stub_site(:another)]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "returns an array containing all stubs even when a single stub has been looked up before" do
|
|
120
|
+
stub_site
|
|
121
|
+
sites = stub_sites(:all)
|
|
122
|
+
sites.size.should == 2
|
|
123
|
+
sites.should == [stub_site, stub_site(:another)]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe "a has_many_proxy" do
|
|
130
|
+
it "responds to :find" do
|
|
131
|
+
@site.sections.respond_to?(:find).should be_true
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "its find method returns an array of stub instance masquerading as a Site instance" do
|
|
135
|
+
@site.sections.find.should == stub_section
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "its target is an array of stub instances masquerading as a Site instance" do
|
|
139
|
+
@site.sections.should == stub_sections
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "works with rspec mock expectations" do
|
|
143
|
+
@site.sections.should_receive(:foo)
|
|
144
|
+
@site.sections.foo
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "a method on a stub" do
|
|
149
|
+
before :each do
|
|
150
|
+
@another = stub_site(:another)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "returns the same stub during one spec" do
|
|
154
|
+
@site.next.object_id.should == @another.object_id
|
|
155
|
+
@site.next.object_id.should == @another.object_id
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
previous_object_id = nil
|
|
159
|
+
1.upto(2) do # what's a less brittle way to spec something like this?
|
|
160
|
+
it "returns a new stub for each spec" do
|
|
161
|
+
previous_object_id.should_not == @site.next.object_id
|
|
162
|
+
previous_object_id = @site.next.object_id
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
describe "a method on a HasManyProxy" do
|
|
168
|
+
before :each do
|
|
169
|
+
@section = stub_section
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "returns the same stub during one spec" do
|
|
173
|
+
@site.sections.find.object_id.should == @section.object_id
|
|
174
|
+
@site.sections.find.object_id.should == @section.object_id
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
previous_object_id = nil
|
|
178
|
+
1.upto(2) do # what's a less brittle way to spec something like this?
|
|
179
|
+
it "returns a new stub for each spec" do
|
|
180
|
+
previous_object_id.should_not == @site.sections.find.object_id
|
|
181
|
+
previous_object_id = @site.sections.find.object_id
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe "a HasManyProxy's content" do
|
|
187
|
+
before :each do
|
|
188
|
+
@section = stub_section
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it "returns the same stub during one spec" do
|
|
192
|
+
@site.sections.first.object_id.should == @section.object_id
|
|
193
|
+
@site.sections.first.object_id.should == @section.object_id
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
previous_object_id = nil
|
|
197
|
+
1.upto(2) do # what's a less brittle way to spec something like this?
|
|
198
|
+
it "returns a new stub for each spec" do
|
|
199
|
+
previous_object_id.should_not == @site.sections.first.object_id
|
|
200
|
+
previous_object_id = @site.sections.first.object_id
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end
|
data/spec/stubs/site.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
define Section do
|
|
2
|
+
instance :root
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
define User do
|
|
6
|
+
instance :admin
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
define ApiKey do
|
|
10
|
+
instance :default
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
define Site do
|
|
14
|
+
has_many :sections, [:find, :build, :root] => stub_section,
|
|
15
|
+
[:paginate] => stub_sections
|
|
16
|
+
belongs_to :user
|
|
17
|
+
has_one :api_key
|
|
18
|
+
|
|
19
|
+
methods [:save, :destroy] => true,
|
|
20
|
+
:next => stub_site(:another),
|
|
21
|
+
:active? => true
|
|
22
|
+
|
|
23
|
+
instance :site,
|
|
24
|
+
:id => 1,
|
|
25
|
+
:name => 'site'
|
|
26
|
+
|
|
27
|
+
instance :another,
|
|
28
|
+
:id => 2,
|
|
29
|
+
:name => 'another'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
scenario :site do
|
|
33
|
+
@site = stub_site(:site)
|
|
34
|
+
end
|
data/stubby.gemspec
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = "stubby"
|
|
3
|
+
s.version = "0.0.1"
|
|
4
|
+
s.date = "2008-05-29"
|
|
5
|
+
s.summary = "Lightweight and fast stubbing framework"
|
|
6
|
+
s.email = "svenfuchs@artweb-design.de"
|
|
7
|
+
s.homepage = "http://github.com/svenfuchs/stubby"
|
|
8
|
+
s.description = "Stubby is a lightweight and fast stubbing framework that was designed to help with the repetitive work of setting up stub scenarios for specifying/testing and optimized for speed and ease of use."
|
|
9
|
+
s.has_rdoc = false
|
|
10
|
+
s.authors = ["Sven Fuchs"]
|
|
11
|
+
s.files = ["lib/stubby/base.rb", "lib/stubby/class_factory.rb", "lib/stubby/definition.rb", "lib/stubby/handle.rb", "lib/stubby/has_many_proxy.rb", "lib/stubby/instances.rb", "lib/stubby/loader.rb", "lib/stubby.rb", "MIT-LICENSE", "README.markdown", "spec/no_rails.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/stubby_spec.rb", "spec/stubs", "spec/stubs/site.rb", "stubby.gemspec"]
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: svenfuchs-stubby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sven Fuchs
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-05-29 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: Stubby is a lightweight and fast stubbing framework that was designed to help with the repetitive work of setting up stub scenarios for specifying/testing and optimized for speed and ease of use.
|
|
17
|
+
email: svenfuchs@artweb-design.de
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
|
|
24
|
+
files:
|
|
25
|
+
- lib/stubby/base.rb
|
|
26
|
+
- lib/stubby/class_factory.rb
|
|
27
|
+
- lib/stubby/definition.rb
|
|
28
|
+
- lib/stubby/handle.rb
|
|
29
|
+
- lib/stubby/has_many_proxy.rb
|
|
30
|
+
- lib/stubby/instances.rb
|
|
31
|
+
- lib/stubby/loader.rb
|
|
32
|
+
- lib/stubby.rb
|
|
33
|
+
- MIT-LICENSE
|
|
34
|
+
- README.markdown
|
|
35
|
+
- spec/no_rails.rb
|
|
36
|
+
- spec/spec.opts
|
|
37
|
+
- spec/spec_helper.rb
|
|
38
|
+
- spec/stubby_spec.rb
|
|
39
|
+
- spec/stubs
|
|
40
|
+
- spec/stubs/site.rb
|
|
41
|
+
- stubby.gemspec
|
|
42
|
+
has_rdoc: false
|
|
43
|
+
homepage: http://github.com/svenfuchs/stubby
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
|
|
47
|
+
require_paths:
|
|
48
|
+
- lib
|
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: "0"
|
|
54
|
+
version:
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: "0"
|
|
60
|
+
version:
|
|
61
|
+
requirements: []
|
|
62
|
+
|
|
63
|
+
rubyforge_project:
|
|
64
|
+
rubygems_version: 1.0.1
|
|
65
|
+
signing_key:
|
|
66
|
+
specification_version: 2
|
|
67
|
+
summary: Lightweight and fast stubbing framework
|
|
68
|
+
test_files: []
|
|
69
|
+
|