fixjour 0.0.6 → 0.0.7

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.
@@ -1,7 +1,9 @@
1
1
  module Fixjour
2
2
  class << self
3
+ include Definitions
4
+
3
5
  attr_accessor :allow_redundancy
4
-
6
+
5
7
  # The list of classes that have builders defined.
6
8
  def builders
7
9
  @builders ||= Set.new
@@ -19,19 +21,19 @@ module Fixjour
19
21
  klass.new(options.merge(overrides))
20
22
  end
21
23
  end
22
-
24
+
23
25
  name = name_for(klass)
24
-
26
+
25
27
  define_create(name)
26
28
  define_valid_attributes(name)
27
29
  end
28
-
30
+
29
31
  # Adds builders to Fixjour.
30
32
  def evaluate(&block)
31
33
  begin
32
34
  module_eval(&block)
33
35
  rescue NameError => e
34
- if evaluator.respond_to?(e.name)
36
+ if e.name && evaluator.respond_to?(e.name)
35
37
  raise NonBlockBuilderReference.new("You must use a builder block in order to reference other Fixjour creation methods.")
36
38
  else
37
39
  raise e
@@ -46,9 +48,9 @@ module Fixjour
46
48
  when String, Symbol then builders.map(&:name).include?(builder)
47
49
  end
48
50
  end
49
-
51
+
50
52
  private
51
-
53
+
52
54
  # Registers a class' builder. This allows us to make sure
53
55
  # redundant builders aren't defined, which can lead to confusion
54
56
  # when trying to figure out where objects are being created.
@@ -57,53 +59,11 @@ module Fixjour
57
59
  raise RedundantBuilder.new("You already defined a builder for #{klass.inspect}")
58
60
  end
59
61
  end
60
-
62
+
61
63
  def name_for(klass)
62
64
  klass.name.underscore
63
65
  end
64
-
65
- # Defines the new_* method
66
- def define_new(klass, &block)
67
- name = name_for(klass)
68
- define_method("new_#{name}") do |*args|
69
- overrides = OverridesHash.new(args.first || { })
70
-
71
- args = case block.arity
72
- when 1 then [overrides]
73
- when 2 then [MergingProxy.new(klass, overrides), overrides]
74
- end
75
-
76
- result = block.bind(self).call(*args)
77
- result
78
- end
79
- end
80
-
81
- # Defines the create_* method
82
- def define_create(name)
83
- define_method("create_#{name}") do |*args|
84
- model = send("new_#{name}", *args)
85
- model.save!
86
- model
87
- end
88
- end
89
-
90
- # Defines the valid_*_attributes method
91
- def define_valid_attributes(name)
92
- define_method("valid_#{name}_attributes") do |*args|
93
- if instance_variable_get("@__valid_#{name}_attrs").nil?
94
- valid_attributes = send("new_#{name}").attributes
95
- valid_attributes.delete_if { |key, value| value.nil? }
96
- instance_variable_set("@__valid_#{name}_attrs", valid_attributes)
97
- end
98
66
 
99
- overrides = args.extract_options!
100
- attrs = instance_variable_get("@__valid_#{name}_attrs").merge(overrides)
101
- attrs.stringify_keys!
102
- attrs.make_indifferent!
103
- attrs
104
- end
105
- end
106
-
107
67
  # Anonymous class used for reflecting on current Fixjour state.
108
68
  def evaluator
109
69
  @evaluator ||= begin
@@ -0,0 +1,36 @@
1
+ module Fixjour
2
+ module Definitions
3
+ # Defines the new_* method
4
+ def define_new(klass, &block)
5
+ define_method("new_#{name_for(klass)}") do |*args|
6
+ Generator.new(klass, block).call(self, args.extract_options!)
7
+ end
8
+ end
9
+
10
+ # Defines the create_* method
11
+ def define_create(name)
12
+ define_method("create_#{name}") do |*args|
13
+ model = send("new_#{name}", *args)
14
+ model.save!
15
+ model
16
+ end
17
+ end
18
+
19
+ # Defines the valid_*_attributes method
20
+ def define_valid_attributes(name)
21
+ define_method("valid_#{name}_attributes") do |*args|
22
+ if instance_variable_get("@__valid_#{name}_attrs").nil?
23
+ valid_attributes = send("new_#{name}").attributes
24
+ valid_attributes.delete_if { |key, value| value.nil? }
25
+ instance_variable_set("@__valid_#{name}_attrs", valid_attributes)
26
+ end
27
+
28
+ overrides = args.extract_options!
29
+ attrs = instance_variable_get("@__valid_#{name}_attrs").merge(overrides)
30
+ attrs.stringify_keys!
31
+ attrs.make_indifferent!
32
+ attrs
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,11 @@
1
1
  module Fixjour
2
- # Raised when a builder returns an invalid object
2
+ # Raised when a builder returns an invalid object.
3
3
  class InvalidBuilder < StandardError; end
4
4
 
5
+ # Raised when a builder will return an invalid object
6
+ # due to a validates_uniqueness_of validation.
7
+ class DangerousBuilder < StandardError; end
8
+
5
9
  # Raised when a builder returns an object of the wrong type
6
10
  class WrongBuilderType < StandardError; end
7
11
 
@@ -0,0 +1,27 @@
1
+ module Fixjour
2
+ # This generates a new instance of a model object for
3
+ # the new_[model] method.
4
+ class Generator
5
+ attr_reader :klass, :block
6
+
7
+ def initialize(klass, block)
8
+ @klass, @block = klass, block
9
+ end
10
+
11
+ def call(context, overrides={})
12
+ overrides = OverridesHash.new(overrides)
13
+ result = block.bind(context).call(*args(overrides))
14
+ case result
15
+ when Hash then klass.new(result.merge(overrides))
16
+ else result
17
+ end
18
+ end
19
+
20
+ def args(overrides)
21
+ case block.arity
22
+ when 1 then [overrides]
23
+ when 2 then [MergingProxy.new(klass, overrides), overrides]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -12,8 +12,8 @@ module Fixjour
12
12
  # Allows for processing of the overrides hash. Deletes
13
13
  # the option when it's present, then yields the value.
14
14
  def process(option)
15
- if value = delete(option)
16
- yield value if block_given?
15
+ delete(option).tap do |value|
16
+ yield value if value and block_given?
17
17
  end
18
18
  end
19
19
  end
@@ -3,6 +3,10 @@ module Fixjour
3
3
  klass.extend(RedundancyChecker)
4
4
  end
5
5
 
6
+ # Uses method_added hook to make sure no redundant builder methods
7
+ # get defined after a builder has already created them. For example,
8
+ # if you have a Comment builder, this hook will ensure that any attempt
9
+ # to define a #new_comment method will raise an exception.
6
10
  module RedundancyChecker
7
11
  BUILDER_METHOD_PATTERN = /^(new|create|valid)_(\w+)(_attributes)?$/
8
12
 
@@ -7,20 +7,40 @@ module Fixjour
7
7
  # * The creation methods return objects of the proper type
8
8
  def verify!
9
9
  builders.each do |klass|
10
- result = evaluator.send("new_#{name_for(klass)}")
10
+ result = new_record(klass)
11
11
 
12
12
  unless result.valid?
13
- raise InvalidBuilder.new("The builder for #{klass} returns an invalid object: #{result.errors.inspect}")
13
+ error(klass, InvalidBuilder, "returns an invalid object: #{result.errors.inspect}")
14
14
  end
15
15
 
16
16
  unless result.new_record?
17
- raise BuilderSavedRecord.new("The builder for #{klass} must not save the object")
17
+ error(klass, BuilderSavedRecord, "must return a new record")
18
18
  end
19
19
 
20
20
  unless result.is_a?(klass)
21
- raise WrongBuilderType.new("The builder for #{klass} must return instance of #{klass}")
21
+ error(klass, WrongBuilderType, "must return an instance of #{klass}")
22
+ end
23
+
24
+ result.save!
25
+
26
+ unless new_record(klass).valid?
27
+ msg = ""
28
+ msg << "returns invalid an invalid object after another object has been saved.\n"
29
+ msg << "This could be caused by a validates_uniqueness_of validation in your model.\n"
30
+ msg << "Use something like the faker gem to alleviate this issue."
31
+ error(klass, DangerousBuilder, msg)
22
32
  end
23
33
  end
24
34
  end
35
+
36
+ private
37
+
38
+ def error(klass, exception, msg)
39
+ raise exception.new("The builder for #{klass} #{msg} ")
40
+ end
41
+
42
+ def new_record(klass)
43
+ evaluator.send("new_#{name_for(klass)}")
44
+ end
25
45
  end
26
46
  end
data/lib/fixjour.rb CHANGED
@@ -3,11 +3,14 @@ $LOAD_PATH << File.dirname(__FILE__)
3
3
  require 'rubygems'
4
4
  require 'activerecord'
5
5
  require 'core_ext/hash'
6
+ require 'core_ext/object'
6
7
  require 'fixjour/merging_proxy'
7
8
  require 'fixjour/redundant_check'
8
9
  require 'fixjour/overrides_hash'
9
10
  require 'fixjour/verify'
10
11
  require 'fixjour/errors'
12
+ require 'fixjour/generator'
13
+ require 'fixjour/definitions'
11
14
  require 'fixjour/builders'
12
15
 
13
16
  # This method is just for prettiness
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixjour
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Nakajima
@@ -35,11 +35,13 @@ files:
35
35
  - lib/core_ext/hash.rb
36
36
  - lib/fixjour
37
37
  - lib/fixjour/builders.rb
38
- - lib/fixjour/merging_proxy.rb
38
+ - lib/fixjour/definitions.rb
39
39
  - lib/fixjour/errors.rb
40
- - lib/fixjour/verify.rb
41
- - lib/fixjour/redundant_check.rb
40
+ - lib/fixjour/generator.rb
41
+ - lib/fixjour/merging_proxy.rb
42
42
  - lib/fixjour/overrides_hash.rb
43
+ - lib/fixjour/redundant_check.rb
44
+ - lib/fixjour/verify.rb
43
45
  - lib/fixjour.rb
44
46
  has_rdoc: true
45
47
  homepage: http://github.com/nakajima/fixjour