bricks 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.md ADDED
@@ -0,0 +1,8 @@
1
+ History
2
+ =======
3
+
4
+ 0.4.0
5
+ -----
6
+
7
+ * Blocks passed to attributes now optionally take a second argument (the builder parent).
8
+ * You can now use `?` like `!`, but it will search for an existing record first.
data/README.md CHANGED
@@ -143,6 +143,16 @@ If you prepend a "~" to the association declaration, the record will be initiali
143
143
  ~publication # will search for a record with name "The Caribbean Times"
144
144
  end
145
145
 
146
+ The same effect can be achieved in your tests using
147
+
148
+ ~(build(Publication)).name!("The Daily Bugle")
149
+
150
+ but since this is ugly, you can just use `?` instead of `!` and you'll get (almost) the same effect:
151
+
152
+ build(Publication).name?("The Daily Bugle")
153
+
154
+ There is a slight difference between using `~` and `?`. `~` will permanently change the builder, while `?` will enable searching only when it's used.
155
+
146
156
  #### One-to-many, Many-to-many (has many, has and belongs to many)
147
157
 
148
158
  Bricks do
@@ -160,6 +170,16 @@ Each call to the *-to-many association name will add a new builder, which you ca
160
170
 
161
171
  (Note that you don't use "!" here. That's only when building the records in your tests.)
162
172
 
173
+ ### Passing the parent to association builder blocks
174
+
175
+ If you need access to the parent object when building an associated object, you'll find it as the second argument passed to a deferred block.
176
+
177
+ builder Article do
178
+ # ...
179
+
180
+ publication.name { |_, article| "#{article.title}'s publication" }
181
+ end
182
+
163
183
  ### Builder Inheritance
164
184
 
165
185
  Given the builder:
@@ -230,7 +250,7 @@ Add `gem "bricks"` to your `Gemfile`, or, as a rails plugin:
230
250
 
231
251
  $ rails plugin install git://github.com/mojotech/bricks.git # Rails 3
232
252
 
233
- ### RSpec
253
+ ### RSpec [TODO: add instructions for other frameworks]
234
254
 
235
255
  # you only need to add the following line if you're using the gem
236
256
  require 'bricks/adapters/active_record'
@@ -239,6 +259,8 @@ Add `gem "bricks"` to your `Gemfile`, or, as a rails plugin:
239
259
  # #build, #build!, #create and #create! in your specs
240
260
  config.include Bricks::DSL
241
261
 
262
+ Finally, add a file to spec/support containing your builders. Call it whatever you'd like and make sure it gets loaded (rspec usually loads all .rb files under spec/support).
263
+
242
264
  Copyright
243
265
  ---------
244
266
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
data/bricks.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{bricks}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["David Leal"]
12
- s.date = %q{2011-06-17}
12
+ s.date = %q{2011-06-21}
13
13
  s.email = %q{david@mojotech.com}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE.txt",
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  ".document",
20
20
  ".rspec",
21
21
  "Gemfile",
22
+ "History.md",
22
23
  "LICENSE.txt",
23
24
  "README.md",
24
25
  "Rakefile",
@@ -33,8 +33,8 @@ module Bricks
33
33
  end
34
34
  end
35
35
 
36
- def find(klass, obj)
37
- klass.find(:first, :conditions => obj.attributes)
36
+ def find(klass, attrs)
37
+ klass.find(:first, :conditions => attrs)
38
38
  end
39
39
 
40
40
  Bricks::Builder.adapter = self.new
@@ -23,43 +23,39 @@ module Bricks
23
23
  self
24
24
  end
25
25
 
26
- def derive(*args)
27
- klass, save = case args.size
28
- when 2
29
- args
30
- when 1
31
- case args.first
32
- when Class
33
- [args.first, @save]
34
- else
35
- [@class, args.first]
36
- end
37
- when 0
38
- [@class, @save]
39
- else
40
- raise ArgumentError, "wrong number of arguments " +
41
- "(#{args.size} for 0, 1 or 2)"
42
- end
43
-
44
- Builder.new(klass, @attrs, @traits, save)
45
- end
46
-
47
- def initialize(klass, attrs = nil, traits = nil, save = false, &block)
26
+ def derive(args = {})
27
+ klass = args[:class] || @class
28
+ save = args.has_key?(:save) ? args[:save] : @save
29
+ search = args.has_key?(:search) ? args[:search] : @search
30
+
31
+ Builder.new(klass, @attrs, @traits, save, search)
32
+ end
33
+
34
+ def initialize(
35
+ klass,
36
+ attrs = nil,
37
+ traits = nil,
38
+ save = false,
39
+ search = false,
40
+ &block)
48
41
  @class = klass
49
42
  @attrs = attrs ? deep_copy(attrs) : []
50
43
  @traits = traits ? Module.new { include traits } : Module.new
51
44
  @save = save
45
+ @search = search
52
46
 
53
47
  extend @traits
54
48
 
55
49
  instance_eval &block if block_given?
56
50
  end
57
51
 
58
- def generate
59
- obj = initialize_object
52
+ def generate(opts = {})
53
+ parent = opts[:parent]
54
+ search = opts.has_key?(:search) ? opts[:search] : @search
55
+ obj = initialize_object(parent)
60
56
 
61
- obj = adapter.find(@class, obj) || obj if @search
62
- save_object(obj) if @save
57
+ obj = adapter.find(@class, Hash[*@attrs.flatten]) || obj if search
58
+ save_object(obj) if @save
63
59
 
64
60
  obj
65
61
  end
@@ -77,8 +73,7 @@ module Bricks
77
73
  end
78
74
 
79
75
  def method_missing(name, *args, &block)
80
- attr = (return_object = name.to_s =~ /!$/) ? name.to_s.chop : name
81
-
76
+ attr = (return_object = name.to_s =~ /[!?]$/) ? name.to_s.chop : name
82
77
  result = if respond_to?(attr)
83
78
  send(attr, *args)
84
79
  elsif settable?(attr)
@@ -88,7 +83,10 @@ module Bricks
88
83
  end
89
84
 
90
85
  if return_object
91
- generate
86
+ opts = {:parent => @parent}
87
+ opts[:search] = name.to_s =~ /\?$/ || @search
88
+
89
+ generate opts
92
90
  else
93
91
  result
94
92
  end
@@ -114,15 +112,15 @@ module Bricks
114
112
  obj.save!
115
113
  end
116
114
 
117
- def initialize_object
115
+ def initialize_object(parent)
118
116
  obj = @class.new
119
117
 
120
118
  @attrs.each { |(k, v)|
121
119
  val = case v
122
120
  when Proc
123
- v.call *[obj].take([v.arity, 0].max)
121
+ v.call *[obj, parent].take([v.arity, 0].max)
124
122
  when Builder, BuilderSet
125
- v.generate
123
+ v.generate(:parent => obj)
126
124
  else
127
125
  v
128
126
  end
@@ -140,16 +138,21 @@ module Bricks
140
138
  def set(name, val = nil, &block)
141
139
  raise Bricks::BadSyntax, "Block and value given" if val && block_given?
142
140
 
143
- pair = @attrs.assoc(name) || (@attrs << [name, nil]).last
141
+ nsym = name.to_sym
142
+ pair = @attrs.assoc(nsym) || (@attrs << [nsym, nil]).last
144
143
 
145
144
  if block_given?
146
145
  pair[-1] = block
146
+
147
+ self
147
148
  elsif val
148
149
  pair[-1] = val
149
- elsif adapter.association?(@class, name, :one)
150
- pair[-1] = builder(adapter.association(@class, name).klass, @save)
151
- elsif adapter.association?(@class, name, :many)
152
- pair[-1] ||= BuilderSet.new(adapter.association(@class, name).klass)
150
+
151
+ self
152
+ elsif adapter.association?(@class, nsym, :one)
153
+ pair[-1] = builder(adapter.association(@class, nsym).klass, @save)
154
+ elsif adapter.association?(@class, nsym, :many)
155
+ pair[-1] ||= BuilderSet.new(adapter.association(@class, nsym).klass)
153
156
  else
154
157
  raise Bricks::BadSyntax,
155
158
  "No value or block given and not an association: #{name}."
@@ -33,8 +33,8 @@ module Bricks
33
33
  build(@class).send(name, *args)
34
34
  end
35
35
 
36
- def generate
37
- @builders.map { |b| b.generate }
36
+ def generate(parent = nil)
37
+ @builders.map { |b| b.generate(parent) }
38
38
  end
39
39
  end
40
40
  end
data/lib/bricks/dsl.rb CHANGED
@@ -8,6 +8,10 @@ module Bricks
8
8
  build(klass).generate
9
9
  end
10
10
 
11
+ def build?(klass)
12
+ build(klass).generate(:search => true)
13
+ end
14
+
11
15
  def create(klass)
12
16
  builder(klass, true)
13
17
  end
@@ -16,8 +20,12 @@ module Bricks
16
20
  create(klass).generate
17
21
  end
18
22
 
23
+ def create?(klass)
24
+ create(klass).generate(:search => true)
25
+ end
26
+
19
27
  def builder(klass, save)
20
- Bricks.builders[klass].derive(save)
28
+ Bricks.builders[klass].derive(:save => save)
21
29
  end
22
30
  end
23
31
  end
data/lib/bricks.rb CHANGED
@@ -23,7 +23,7 @@ module Bricks
23
23
  @builders[key]
24
24
  elsif Class === key
25
25
  @builders[key] = if builder = @builders[key.superclass]
26
- builder.derive(key)
26
+ builder.derive(:class => key)
27
27
  else
28
28
  builder(key)
29
29
  end
data/spec/bricks_spec.rb CHANGED
@@ -20,12 +20,14 @@ describe Bricks do
20
20
  end
21
21
 
22
22
  builder Article do
23
- author 'Jack Jupiter'
24
- title 'a title'
25
- body 'the body'
23
+ author 'Jack Jupiter'
24
+ title 'a title'
25
+ body 'the body'
26
+ language 'Swahili'
27
+
26
28
  formatted_title { |obj| obj.title + " by " + obj.author }
27
29
  deferred { Time.now }
28
- newspaper
30
+ newspaper.language { |_, article| article.language }
29
31
 
30
32
  %w(Socrates Plato Aristotle).each { |n| readers.name(n) }
31
33
 
@@ -83,6 +85,18 @@ describe Bricks do
83
85
  a.should be_saved
84
86
  end
85
87
 
88
+ it "fetches an existing model instead of initializing it" do
89
+ create(Newspaper).name!("The First in Line")
90
+
91
+ create!(Newspaper).should == build?(Newspaper)
92
+ end
93
+
94
+ it "fetches an existing model instead of creating it" do
95
+ create(Newspaper).name!("The First in Line")
96
+
97
+ create!(Newspaper).should == create?(Newspaper)
98
+ end
99
+
86
100
  describe "with simple fields" do
87
101
  it "initializes model fields" do
88
102
  a = build!(Article)
@@ -101,6 +115,14 @@ describe Bricks do
101
115
  it "uses the object being built in deferred initialization" do
102
116
  build!(Article).formatted_title.should == "a title by Jack Jupiter"
103
117
  end
118
+
119
+ it "fetches an existing model instead of creating it" do
120
+ create!(Newspaper)
121
+
122
+ n = create(Newspaper).name!("The Bugle Planet")
123
+
124
+ create(Newspaper).name?("The Bugle Planet").should == n
125
+ end
104
126
  end
105
127
 
106
128
  describe "with traits" do
@@ -144,6 +166,10 @@ describe Bricks do
144
166
  should == 'The Daily Bugle'
145
167
  end
146
168
 
169
+ it "passes the parent into a deferred block" do
170
+ build(Article).language!("Thai").newspaper.language.should == "Thai"
171
+ end
172
+
147
173
  it "possibly looks for an existing record" do
148
174
  n = create(Newspaper).daily_bugle!
149
175
  a = create(Article).maybe_bugle!
@@ -20,6 +20,7 @@ ActiveRecord::Schema.define(:version => 20110608204150) do
20
20
  end
21
21
 
22
22
  create_table "newspapers", :force => true do |t|
23
+ t.string "language"
23
24
  t.string "name"
24
25
  end
25
26
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: bricks
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.0
5
+ version: 0.4.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - David Leal
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-17 00:00:00 +01:00
13
+ date: 2011-06-21 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -70,6 +70,7 @@ files:
70
70
  - .document
71
71
  - .rspec
72
72
  - Gemfile
73
+ - History.md
73
74
  - LICENSE.txt
74
75
  - README.md
75
76
  - Rakefile
@@ -100,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - ">="
102
103
  - !ruby/object:Gem::Version
103
- hash: -732403827
104
+ hash: -979563633
104
105
  segments:
105
106
  - 0
106
107
  version: "0"