bricks 0.3.0 → 0.4.0

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.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"