traver 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed508040aa612bbd6bd5755a5078ef5e2254abb0
4
- data.tar.gz: 0aba48d5e505bc9bf9541ad84d3d993400392ecc
3
+ metadata.gz: 009d9a2721da257a5d9bb38e0196d84668b6dc35
4
+ data.tar.gz: 4a8aef2efb71d6f859b61e9fd21e52a8f8da2873
5
5
  SHA512:
6
- metadata.gz: 1f99785aef8bf9e7ff8a4dd4f31c4a0d423465e4f023065037964f6f3b81dbb59a9946415569514cfb4c472cfb336905027419acc89f1cac5330cfb355ec46a4
7
- data.tar.gz: 276b82bd4f7b1f3954dfbc7c14076c17784aadc08f3539c865b9ce55df5b7adb63a46792019e4a2331f79f3164af52681c282c1dabee541cf75acc58e98d80b5
6
+ metadata.gz: 7b866419d6f2dddb32299145968d1f6374d5b5966edad94ba61665c6fbd846ecb05d531ca0c6ebb6ab29a8dede324f1f10205e92961d6e56ef03adca765adc0f
7
+ data.tar.gz: 5736f5f8b098dcff077586255cfb9b211b2f97017df3521c9783a9359ab4e4b4e6c39a2026adca5e7df409c3727bf96fc8ebe888012d4ac4a031573fe2053a69
data/README.md CHANGED
@@ -18,7 +18,7 @@ and run `bundle install` from your shell.
18
18
 
19
19
  ## Usage
20
20
 
21
- Create an object with attributes:
21
+ Create object with attributes:
22
22
 
23
23
  ```ruby
24
24
  blog = Traver.create(blog: { title: "Blog" }) #=> #<Blog @title="Blog">
@@ -27,15 +27,26 @@ blog = Traver.create(blog: { title: "Blog" }) #=> #<Blog @title="Blog">
27
27
  Define and use factories:
28
28
 
29
29
  ```ruby
30
- Traver.factory(:post, {
30
+ Traver.define_factory(:user, {
31
+ full_name: "Walter White"
32
+ })
33
+
34
+ Traver.define_factory(:post, {
31
35
  title: "Hello"
32
36
  })
33
37
 
34
- Traver.factory(:published_post, :post, {
38
+ Traver.create(:user) #=> #<User @full_name="Walter White">
39
+ Traver.create(:post) #=> #<Post @title="Hello">
40
+ ```
41
+
42
+ Define child factories:
43
+
44
+ ```ruby
45
+ Traver.define_factory(:published_post, :post, {
35
46
  published: true
36
47
  })
37
48
 
38
- Traver.factory(:draft_post, :post, {
49
+ Traver.define_factory(:draft_post, :post, {
39
50
  published: false
40
51
  })
41
52
 
@@ -43,7 +54,7 @@ Traver.create(:published_post) #=> #<Post @title="Hello", @published=true>
43
54
  Traver.create(:draft_post) #=> #<Post @title="Hello", @published=false>
44
55
  ```
45
56
 
46
- Create associated object:
57
+ Create associated objects:
47
58
 
48
59
  ```ruby
49
60
  blog = Traver.create(blog: {
@@ -54,56 +65,57 @@ blog = Traver.create(blog: {
54
65
  blog.user #=> #<User @name="Mike">
55
66
  ```
56
67
 
57
- Create object collections:
68
+ Create associated objects using factory names:
58
69
 
59
70
  ```ruby
71
+ Traver.define_factory(:mike, :user, {
72
+ name: "Mike"
73
+ })
74
+
60
75
  blog = Traver.create(blog: {
61
76
  title: "Hello",
62
- posts: [
63
- { title: "Post #1" },
64
- { title: "Post #2" }
65
- ]
77
+ user: :mike
66
78
  })
67
79
 
68
- blog.posts #=> [#<Post @title="Post #1">, #<Post @title="Post #2">]
80
+ ```
69
81
 
70
- # More concise syntax
82
+ Create associated collections:
83
+
84
+ ```ruby
71
85
  blog = Traver.create(blog: {
72
86
  title: "Hello",
73
- posts: [2, title: "Post #${n}"]
87
+ posts: [
88
+ { title: "Post #1" },
89
+ { title: "Post #2" }
90
+ ]
74
91
  })
75
92
 
76
93
  blog.posts #=> [#<Post @title="Post #1">, #<Post @title="Post #2">]
77
94
 
78
- # Having defined factory
79
- Traver.factory(post: { title: "Post #${n}"})
80
-
81
- # Even more concise
82
- blog = Traver.create(blog: { title: "Hello", posts: 2 })
95
+ ```
83
96
 
84
- blog.posts #=> [#<Post @title="Post #1">, #<Post @title="Post #2">]
97
+ Create associated collections using numbers:
85
98
 
99
+ ```ruby
100
+ Traver.create(blog: { title: "Hello", posts: 2 })
101
+ Traver.create(blog: { title: "Hello", posts: [2, title: "Post #${n}"] })
102
+ Traver.create(blog: { title: "Hello", posts: [2, :published_post] })
86
103
  ```
87
104
 
88
- Any level of nesting:
105
+ Create associated collections using factory names:
89
106
 
90
107
  ```ruby
91
- blog = Traver.create(blog: {
92
- title: "Blog",
93
- posts: [{
94
- title: "Hello",
95
- tags: [{ name: "Happy" }]
96
- }]
97
- })
98
-
99
- blog.posts.first.tag.first #=> #<Tag @name="Happy">
108
+ Traver.create(blog: { title: "Hello", posts: [:published_post, :draft_post] })
100
109
  ```
101
110
 
102
- Create lists:
111
+ Create lists with sequences:
103
112
 
104
113
  ```ruby
105
- users = Traver.create_list(:user, 2, email: "user${n}@mail.me")
114
+ users = Traver.create_list(2, user: { email: "user${n}@mail.me" })
106
115
  #=> [#<User @email="user1@mail.me">, #<User @email="user2@mail.me">]
116
+
117
+ users = Traver.create_list(2, :published_post)
118
+ #=> [#<Post @published=true>, #<User @published=true>]
107
119
  ```
108
120
 
109
121
  Graph is a convenient way to reference created objects:
@@ -121,6 +133,34 @@ graph.tag2 #=> #<Tag>
121
133
  blog, post = Traver.create_graph(blog: { posts: 1 })[:blog, :post]
122
134
  ```
123
135
 
136
+ Reference already created objects:
137
+
138
+ ```ruby
139
+ blog = Traver.create(blog: {
140
+ post: { tags: 1 }
141
+ tags: [-> (graph) { graph.tag1 }]
142
+ })
143
+ ```
144
+
145
+ ## Rails
146
+
147
+ By default Traver loads factories from`test/factories.rb` or `spec/factories.rb` for rspec users.
148
+
149
+ Objects for `belongs_to` associations are created automatically:
150
+
151
+ ```ruby
152
+ class Blog < ActiveRecord::Base
153
+ has_many :blogs
154
+ end
155
+
156
+ class Post < ActiveRecord::Base
157
+ belongs_to :blog
158
+ end
159
+
160
+ post = Traver.create(:post) #=> #<Post>
161
+ post.blog #=> #<Blog>
162
+ ```
163
+
124
164
  ## Development
125
165
 
126
166
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,27 @@
1
+ module Traver
2
+ class AttributesResolver
3
+ def select_attributes_params(params, object_class)
4
+ params.select { |name, value| regular_attribute?(object_class, name, value) }
5
+ end
6
+
7
+ def select_objects_params(params, object_class)
8
+ params.select { |name, value| nested_object?(object_class, name, value) }
9
+ end
10
+
11
+ def select_has_one_objects_params(params, object_class)
12
+ params.select { |name, value| has_one_object?(object_class, name, value) }
13
+ end
14
+
15
+ def select_collections_params(object, factory, params)
16
+ params.select { |name, value| nested_collection?(factory.object_class, name, value) }
17
+ end
18
+
19
+ private
20
+
21
+ def regular_attribute?(object_class, name, value)
22
+ !nested_object?(object_class, name, value) &&
23
+ !nested_collection?(object_class, name, value) &&
24
+ !has_one_object?(object_class, name, value)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Traver
2
+ class ActiveRecordAttributesResolver < AttributesResolver
3
+
4
+ private
5
+
6
+ def nested_object?(object_class, name, value)
7
+ reflection = object_class.reflect_on_association(name)
8
+ reflection && reflection.macro == :belongs_to
9
+ end
10
+
11
+ def has_one_object?(object_class, name, value)
12
+ reflection = object_class.reflect_on_association(name)
13
+ reflection && reflection.macro == :has_one
14
+ end
15
+
16
+ def nested_collection?(object_class, name, value)
17
+ reflection = object_class.reflect_on_association(name)
18
+ reflection && reflection.collection?
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module Traver
2
+ class PoroAttributesResolver < AttributesResolver
3
+
4
+ private
5
+
6
+ def nested_object?(object_class, name, value)
7
+ Object.const_defined?(name.to_s.camelize)
8
+ end
9
+
10
+ def has_one_object?(object_class, name, value)
11
+ nested_object?(object_class, name, value)
12
+ end
13
+
14
+ def nested_collection?(object_class, name, value)
15
+ if plural?(name)
16
+ Object.const_defined?(name.to_s.singularize.camelize)
17
+ end
18
+ end
19
+
20
+ def plural?(val)
21
+ val.to_s.pluralize == val.to_s
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Traver
2
+ class ActiveRecordDefaultParamsCreator
3
+ def default_params(object_class)
4
+ associations = object_class.reflect_on_all_associations(:belongs_to)
5
+
6
+ associations.each_with_object({}) do |association, result|
7
+ result[association.name] = :__ref__
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Traver
2
+ class PoroDefaultParamsCreator
3
+ def default_params(object_class)
4
+ {}
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ module Traver
2
+ class FactoriesLoader
3
+ attr_reader :base_dir, :folder_name
4
+ attr_reader :factories_loaded
5
+
6
+ FILE_NAME = "factories.rb"
7
+
8
+ def initialize(base_dir, folder_name = "test")
9
+ @base_dir = base_dir
10
+ @folder_name = folder_name
11
+
12
+ @factories_loaded = false
13
+ end
14
+
15
+ def load_factories
16
+ unless factories_loaded
17
+ factories_file_path = File.join(base_dir, folder_name, FILE_NAME)
18
+
19
+ require factories_file_path
20
+
21
+ @factories_loaded = true
22
+ end
23
+ end
24
+ end
25
+
26
+ class NilFactoriesLoader
27
+ def load_factories
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ module Traver
2
+ class Factory
3
+ attr_reader :name, :params, :parent_factory
4
+
5
+ def initialize(name, params, parent_factory)
6
+ @name = name
7
+ @params = params
8
+ @parent_factory = parent_factory
9
+ end
10
+
11
+ def root_factory
12
+ parent_factory ? parent_factory.root_factory : self
13
+ end
14
+
15
+ def inherited_params
16
+ if parent_factory
17
+ parent_factory.inherited_params.merge(params)
18
+ else
19
+ params
20
+ end
21
+ end
22
+
23
+ def root_name
24
+ root_factory.name
25
+ end
26
+
27
+ def object_class_name
28
+ root_factory.name.to_s.camelize
29
+ end
30
+
31
+ def object_class
32
+ Object.const_get(object_class_name)
33
+ end
34
+ end
35
+ end
@@ -1,51 +1,45 @@
1
1
  module Traver
2
2
  class FactoryDefiner
3
- attr_reader :defined_factories
4
-
5
3
  def initialize
6
- @defined_factories = {}
4
+ undefine_all_factories
7
5
  end
8
6
 
9
- def self.instance
10
- @instance ||= FactoryDefiner.new
7
+ def define_factory(factory_name, parent_name, params)
8
+ factories[factory_name] = Factory.new(factory_name, params, factory_by_name(parent_name))
11
9
  end
12
10
 
13
- def define_factory(factory_name, *options)
14
- parent_name = nil
15
-
16
- if options.size == 1
17
- params = options.first
18
- elsif options.size == 2
19
- parent_name, params = options
20
- end
21
-
22
- defined_factories[factory_name] = { params: params, parent: parent_name }
11
+ alias :factory :define_factory
12
+
13
+ def factory_params(factory_name)
14
+ factory_by_name(factory_name).params
23
15
  end
24
16
 
25
- def get_object_class(factory_name)
26
- if defined_factories[factory_name] && parent_name = defined_factories[factory_name][:parent]
27
- get_object_class(parent_name)
28
- else
29
- Object.const_get(camelize(factory_name.to_s))
30
- end
17
+ def parent_factory_name(factory_name)
18
+ factory_by_name(factory_name).parent_factory &&
19
+ factory_by_name(factory_name).parent_factory.name
20
+ end
21
+
22
+ def parent_factory_params(factory_name)
23
+ factory_by_name(factory_name).parent_factory.params
31
24
  end
32
25
 
33
- def apply_factory_params(factory_name, params)
34
- factory_params = get_factory_params(factory_name)
35
-
36
- factory_params.merge(params)
26
+ def factory_by_name(factory_name)
27
+ factories[factory_name] || default_factory(factory_name) if factory_name
37
28
  end
38
29
 
39
- private
30
+ def undefine_all_factories
31
+ @factories = {}
32
+ end
40
33
 
41
- def get_factory_params(factory_name)
42
- if factory = defined_factories[factory_name]
43
- get_factory_params(factory[:parent]).merge(factory[:params])
44
- end || {}
34
+ def factories_count
35
+ factories.keys.length
45
36
  end
46
37
 
47
- def camelize(str)
48
- str.split('_').map(&:capitalize).join
38
+ private
39
+ attr_reader :factories
40
+
41
+ def default_factory(factory_name)
42
+ Factory.new(factory_name, {}, nil)
49
43
  end
50
44
  end
51
45
  end
data/lib/traver/graph.rb CHANGED
@@ -13,7 +13,7 @@ module Traver
13
13
  key = key.to_s
14
14
 
15
15
  if plural?(key)
16
- vertices[singularize(key).to_sym]
16
+ vertices[key.singularize.to_sym]
17
17
  else
18
18
  name, index = key.split(/(\d+)$/)
19
19
  index = (index || 1).to_i
@@ -30,11 +30,7 @@ module Traver
30
30
  attr_reader :vertices
31
31
 
32
32
  def plural?(value)
33
- value.end_with?("s")
34
- end
35
-
36
- def singularize(value)
37
- value.chomp("s")
33
+ value == value.pluralize
38
34
  end
39
35
  end
40
36
  end
@@ -1,20 +1,28 @@
1
1
  module Traver
2
2
  class GraphCreator
3
- attr_reader :params
3
+ attr_reader :factory_name, :params, :factory_definer, :sequencer
4
4
  attr_reader :graph
5
5
 
6
- def initialize(params = {})
7
- @params = params
6
+ def initialize(factory_name, params, factory_definer, sequencer)
7
+ @factory_name = factory_name
8
+ @params = params
9
+ @factory_definer = factory_definer
10
+ @sequencer = sequencer
11
+
12
+ @object_creator = ObjectCreator.new(factory_name, params, factory_definer, sequencer)
13
+
8
14
  @graph = Graph.new
9
15
  end
10
16
 
11
17
  def create_graph
12
- object_creator = ObjectCreator.new(params)
13
18
  object_creator.after_create = lambda do |creator|
14
- graph.add_vertex(creator.factory_name, creator.created_object)
19
+ graph.add_vertex(creator.factory_name, creator.object)
15
20
  end
16
21
 
17
22
  object_creator.create_object
18
23
  end
24
+
25
+ private
26
+ attr_reader :object_creator
19
27
  end
20
28
  end
@@ -0,0 +1,26 @@
1
+ module Traver
2
+ class ListCreator
3
+ attr_reader :num, :factory_name, :params, :factory_definer, :sequencer
4
+ attr_reader :list
5
+
6
+ def initialize(num, factory_name, params, factory_definer, sequencer)
7
+ @num = num
8
+ @factory_name = factory_name
9
+ @params = params
10
+ @factory_definer = factory_definer
11
+ @sequencer = sequencer
12
+ end
13
+
14
+ def create_list
15
+ @list = num.times.map do
16
+ object_creator.create_object(factory_name, params, factory_definer, sequencer)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def object_creator
23
+ ObjectCreator
24
+ end
25
+ end
26
+ end
@@ -1,92 +1,220 @@
1
+ require "forwardable"
2
+
1
3
  module Traver
2
4
  class ObjectCreator
3
- attr_reader :factory_name, :params
5
+ extend Forwardable
6
+
7
+ attr_reader :factory_name, :params, :factory_definer, :sequencer, :cache, :nesting
8
+ attr_reader :object
9
+
4
10
  attr_accessor :after_create
5
- attr_reader :created_object
6
11
 
7
- def initialize(options)
8
- options = { options => {} } if options.is_a?(Symbol)
12
+ def_delegators :settings, :object_persister,
13
+ :attributes_resolver,
14
+ :default_params_creator
15
+
16
+ def self.create_object(factory_name, params, factory_definer, sequencer, cache = {}, nesting = 1)
17
+ creator = new(factory_name, params, factory_definer, sequencer, cache, nesting)
18
+ creator.create_object
9
19
 
10
- @factory_name, @params = options.first
11
- @factory_definer = FactoryDefiner.instance
20
+ creator.object
21
+ end
22
+
23
+ def initialize(factory_name, params, factory_definer, sequencer, cache = {}, nesting = 1)
24
+ @factory_name = factory_name
25
+ @params = params
26
+ @factory_definer = factory_definer
27
+ @sequencer = sequencer
28
+ @cache = cache
29
+ @nesting = nesting
12
30
  end
13
31
 
14
32
  def create_object
15
- instantiate_object
16
- set_object_state
17
- persist_object
18
- call_after_create_hook
33
+ obtain_factory
34
+
35
+ if obtain_object_from_cache?
36
+ obtain_object_from_cache
37
+ else
38
+ # puts "#{'-' * nesting} #{factory_name}<br/>"
39
+
40
+ change_ref_to_be_empty_hash
41
+ merge_factory_params
42
+ merge_default_params
43
+ instantiate_object
44
+ set_nested_objects
45
+ set_attributes
46
+ persist_object
47
+ set_has_one_objects
48
+ set_nested_collections
49
+ call_after_create_hook
50
+ end
19
51
  end
20
52
 
21
53
  private
22
- attr_reader :factory_definer
54
+ attr_reader :factory
55
+
56
+ def obtain_factory
57
+ @factory = factory_definer.factory_by_name(factory_name)
58
+ end
59
+
60
+ def obtain_object_from_cache?
61
+ params == :__ref__ && cache.has_key?(factory.root_name)
62
+ end
63
+
64
+ def obtain_object_from_cache
65
+ @object = cache[factory.root_name]
66
+ end
67
+
68
+ def change_ref_to_be_empty_hash
69
+ @params = {} if params == :__ref__
70
+ end
71
+
72
+ def merge_factory_params
73
+ @params = factory.inherited_params.merge(params)
74
+ end
75
+
76
+ def merge_default_params
77
+ @params = default_params_creator.default_params(factory.object_class).merge(params)
78
+ end
23
79
 
24
80
  def instantiate_object
25
- @created_object = get_class.new
81
+ @object = factory.object_class.new
26
82
  end
27
83
 
28
- def get_class
29
- factory_definer.get_object_class(factory_name)
84
+ # Attributes
85
+
86
+ def set_attributes
87
+ attributes_resolver.select_attributes_params(params, factory.object_class).each do |name, value|
88
+ set_attribute(name, value)
89
+ end
30
90
  end
31
91
 
32
- def set_object_state
33
- factory_params.each do |attribute, value|
34
- if nested_params?(value)
35
- create_nested_object(attribute, value)
36
- elsif collection_params?(value)
37
- create_collection(attribute, value)
38
- else
39
- set_attribute(attribute, value)
92
+ def set_attribute(attribute, value)
93
+ if value.is_a?(Proc)
94
+ value = object.instance_exec(&value)
95
+ elsif value.is_a?(String)
96
+ if sequencer.value_has_sequence?(value)
97
+ value = sequencer.interpolate_sequence(attribute, value)
40
98
  end
41
99
  end
100
+
101
+ object.public_send("#{attribute}=", value)
42
102
  end
43
-
44
- def factory_params
45
- factory_definer.apply_factory_params(factory_name, params)
46
- end
47
103
 
48
- def nested_params?(params)
49
- params.is_a?(Hash)
104
+ # Nested Objects
105
+
106
+ def set_nested_objects
107
+ attributes_resolver.select_objects_params(params, factory.object_class).each do |name, value|
108
+ set_nested_object(name, value)
109
+ end
50
110
  end
51
111
 
52
- def create_nested_object(k, v)
53
- set_attribute(k, do_create_object(k => v))
112
+ def set_nested_object(name, value)
113
+ if value.is_a?(Integer)
114
+ set_attribute(name, create_nested_object(name, {}))
115
+ elsif value.is_a?(Hash) || value == :__ref__
116
+ set_attribute(name, create_nested_object(name, value))
117
+ elsif value.is_a?(Symbol)
118
+ set_attribute(name, create_nested_object(value, {}))
119
+ else
120
+ set_attribute(name, value)
121
+ end
54
122
  end
55
123
 
56
- def do_create_object(params)
57
- object_creator = ObjectCreator.new(params)
124
+ def create_nested_object(factory_name, params)
125
+ object_creator = ObjectCreator.new(factory_name, params, factory_definer, sequencer, cache, nesting + 1)
58
126
  object_creator.after_create = after_create
59
127
  object_creator.create_object
60
128
 
61
- object_creator.created_object
129
+ object_creator.object
62
130
  end
63
131
 
64
- def set_attribute(attribute, value)
65
- created_object.public_send("#{attribute}=", value)
66
- end
67
132
 
68
- def collection_params?(params)
69
- params.is_a?(Array)
133
+ # Has one objects
134
+
135
+ def set_has_one_objects
136
+ attributes_resolver.select_has_one_objects_params(params, factory.object_class).each do |name, value|
137
+ set_nested_object(name, value)
138
+ end
70
139
  end
71
140
 
72
- def create_collection(attribute, collection_params)
73
- collection = collection_params.map do |params|
74
- do_create_object(singularize(attribute) => params)
141
+ # Nested Collections
142
+
143
+ def set_nested_collections
144
+ attributes_resolver.select_collections_params(object, factory, params).each do |collection_name, params_array|
145
+ set_nested_collection(collection_name, params_array)
75
146
  end
147
+ end
148
+
149
+ def set_nested_collection(collection_name, params_array)
150
+ factory_name = collection_name.to_s.singularize.to_sym
76
151
 
77
- set_attribute(attribute, collection)
152
+ set_attribute(collection_name, create_nested_collection(factory_name, params_array))
78
153
  end
79
154
 
80
- def singularize(val)
81
- val.to_s.chomp("s").to_sym
155
+ def create_nested_collection(factory_name, params_array)
156
+ if params_array.is_a?(Integer)
157
+ params_array = [{}] * params_array
158
+
159
+ params_array.map do |params|
160
+ create_nested_object(factory_name, params)
161
+ end
162
+ elsif params_array.first.is_a?(Symbol)
163
+ params_array.map do |factory_name|
164
+ create_nested_object(factory_name, {})
165
+ end
166
+ else
167
+ if params_array.first.is_a?(Integer)
168
+ num, hash_or_symbol = params_array
169
+
170
+ if hash_or_symbol.nil?
171
+ params_array = [{}] * num
172
+ elsif (hash = hash_or_symbol).is_a?(Hash)
173
+ params_array = [hash] * num
174
+ elsif hash_or_symbol.is_a?(Symbol)
175
+ factory_name = hash_or_symbol
176
+
177
+ params_array = [{}] * num
178
+ else
179
+ raise "Wrong collection params #{params_array}"
180
+ end
181
+ end
182
+
183
+ params_array.map do |params|
184
+ create_nested_object(factory_name, params)
185
+ end
186
+ end
187
+ end
188
+
189
+ # Settings
190
+
191
+
192
+ def settings
193
+ @settings ||=
194
+ if object_is_active_record?
195
+ ActiveRecordSettings.new
196
+ else
197
+ PoroSettings.new
198
+ end
82
199
  end
83
200
 
201
+ def object_is_active_record?
202
+ factory.object_class < ActiveRecord::Base
203
+ end
204
+
205
+
206
+ # Persist Object
207
+
84
208
  def persist_object
85
- created_object.save if defined?(Rails)
209
+ object_persister.persist_object(object)
210
+ cache[factory.root_name] = object
86
211
  end
87
212
 
213
+
214
+ # Hooks
215
+
88
216
  def call_after_create_hook
89
217
  after_create.call(self) if after_create
90
218
  end
91
219
  end
92
- end
220
+ end
@@ -0,0 +1,9 @@
1
+ module Traver
2
+ class ActiveRecordObjectPersister
3
+ def persist_object(object)
4
+ unless object.save
5
+ raise "Unable to save #{object.class}: #{object.errors.full_messages}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Traver
2
+ class PoroObjectPersister
3
+ def persist_object(object)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ class Sequencer
2
+ attr_reader :sequence_store
3
+
4
+ def initialize
5
+ @sequence_store = {}
6
+ end
7
+
8
+ def value_has_sequence?(value)
9
+ !!(value =~ /\$\{n\}/)
10
+ end
11
+
12
+ def interpolate_sequence(name, value)
13
+ value.sub("${n}", next_sequence_value_for(name).to_s)
14
+ end
15
+
16
+ private
17
+
18
+ def next_sequence_value_for(name)
19
+ sequence_store[name] ||= 0
20
+ sequence_store[name] += 1
21
+
22
+ sequence_store[name]
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require "traver/settings"
2
+
3
+ module Traver
4
+ class ActiveRecordSettings < Settings
5
+ def initialize
6
+ super
7
+
8
+ @object_persister = ActiveRecordObjectPersister.new
9
+ @attributes_resolver = ActiveRecordAttributesResolver.new
10
+ @default_params_creator = ActiveRecordDefaultParamsCreator.new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "traver/settings"
2
+
3
+ module Traver
4
+ class PoroSettings < Settings
5
+ def initialize
6
+ super
7
+
8
+ @object_persister = PoroObjectPersister.new
9
+ @attributes_resolver = PoroAttributesResolver.new
10
+ @default_params_creator = PoroDefaultParamsCreator.new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Traver
2
+ class Settings
3
+ attr_reader :object_persister,
4
+ :attributes_resolver,
5
+ :default_params_creator
6
+ end
7
+ end
@@ -0,0 +1,93 @@
1
+ module Traver
2
+ class TraverConstructor
3
+ attr_reader :factory_definer, :sequencer
4
+
5
+ def initialize
6
+ @factory_definer = FactoryDefiner.new
7
+ @sequencer = Sequencer.new
8
+ end
9
+
10
+ def create(*options)
11
+ factory_name, params = parse_create_options(options)
12
+
13
+ ObjectCreator.create_object(factory_name, params, factory_definer, sequencer)
14
+ end
15
+
16
+ def create_graph(*options)
17
+ factory_name, params = parse_create_options(options)
18
+
19
+ graph_creator = GraphCreator.new(factory_name, params, factory_definer, sequencer)
20
+ graph_creator.create_graph
21
+
22
+ graph_creator.graph
23
+ end
24
+
25
+ def create_list(num, *options)
26
+ factory_name, params = parse_create_options(options)
27
+
28
+ list_creator = ListCreator.new(num, factory_name, params, factory_definer, sequencer)
29
+ list_creator.create_list
30
+
31
+ list_creator.list
32
+ end
33
+
34
+ def define_factories(&block)
35
+ instance_exec(&block)
36
+ end
37
+
38
+ alias :factories :define_factories
39
+
40
+ def define_factory(factory_name, *options)
41
+ parent_factory_name, params = parse_factory_options(options)
42
+
43
+ factory_definer.define_factory(factory_name, parent_factory_name, params)
44
+ end
45
+
46
+ alias :factory :define_factory
47
+
48
+ def undefine_all_factories
49
+ factory_definer.undefine_all_factories
50
+ end
51
+
52
+ def factories_count
53
+ factory_definer.factories_count
54
+ end
55
+
56
+ private
57
+
58
+ def parse_create_options(options)
59
+ if factory_girl_options?(options)
60
+ parse_factory_girl_options(options)
61
+ else
62
+ parse_traver_options(options)
63
+ end
64
+ end
65
+
66
+ def factory_girl_options?(options)
67
+ options.first.is_a?(Symbol)
68
+ end
69
+
70
+ def parse_factory_girl_options(options)
71
+ factory_name, params = options
72
+ params ||= {}
73
+
74
+ [factory_name, params]
75
+ end
76
+
77
+ def parse_traver_options(options)
78
+ options.first.first
79
+ end
80
+
81
+ def parse_factory_options(options)
82
+ parent_factory_name = nil
83
+
84
+ if options.size == 1
85
+ params = options.first
86
+ elsif options.size == 2
87
+ parent_factory_name, params = options
88
+ end
89
+
90
+ [parent_factory_name, params]
91
+ end
92
+ end
93
+ end
@@ -1,3 +1,3 @@
1
1
  module Traver
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/traver.rb CHANGED
@@ -1,28 +1,67 @@
1
- require "traver/version"
2
- require "traver/factory_definer"
3
- require "traver/object_creator"
4
- require "traver/graph"
5
- require "traver/graph_creator"
1
+ require "active_support/inflector"
2
+
3
+ Dir["#{File.dirname(__FILE__)}/traver/**/*.rb"].each { |file| require file }
6
4
 
7
5
  module Traver
8
6
  class Error < Exception; end
9
7
 
10
- def self.factory(factory_name, *options)
11
- factory_definer = FactoryDefiner.instance
12
- factory_definer.define_factory(factory_name, *options)
13
- end
8
+ class << self
9
+ attr_reader :factories_loaded
10
+
11
+ def create(*options)
12
+ load_factories
13
+
14
+ traver_constructor.create(*options)
15
+ end
14
16
 
15
- def self.create(options)
16
- object_creator = ObjectCreator.new(options)
17
- object_creator.create_object
17
+ def create_graph(*options)
18
+ load_factories
19
+
20
+ traver_constructor.create_graph(*options)
21
+ end
18
22
 
19
- object_creator.created_object
20
- end
23
+ def create_list(num, *options)
24
+ load_factories
25
+
26
+ traver_constructor.create_list(num, *options)
27
+ end
28
+
29
+ def define_factory(factory_name, *options)
30
+ traver_constructor.define_factory(factory_name, *options)
31
+ end
32
+
33
+ alias :factory :define_factory
34
+
35
+ def define_factories(&block)
36
+ traver_constructor.define_factories(&block)
37
+ end
38
+
39
+ alias :factories :define_factories
21
40
 
22
- def self.create_graph(params = {})
23
- graph_creator = GraphCreator.new(params)
24
- graph_creator.create_graph
41
+ def undefine_all_factories
42
+ traver_constructor.undefine_all_factories
43
+ end
44
+
45
+ private
46
+
47
+ def traver_constructor
48
+ @traver_constructor ||= TraverConstructor.new
49
+ end
50
+
51
+ def load_factories
52
+ unless factories_loaded
53
+ factories_loader.load_factories
54
+
55
+ @factories_loaded = true
56
+ end
57
+ end
25
58
 
26
- graph_creator.graph
59
+ def factories_loader
60
+ if defined?(Rails)
61
+ FactoriesLoader.new(Rails.root, "spec")
62
+ else
63
+ NilFactoriesLoader.new
64
+ end
65
+ end
27
66
  end
28
67
  end
data/traver.gemspec CHANGED
@@ -18,7 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.bindir = "exe"
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
-
21
+
22
+ spec.add_runtime_dependency "activesupport"
23
+
22
24
  spec.add_development_dependency "bundler", "~> 1.11"
23
25
  spec.add_development_dependency "rake", "~> 10.0"
24
26
  spec.add_development_dependency "minitest", "~> 5.0"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Kaspervich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-10 00:00:00.000000000 Z
11
+ date: 2016-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -96,10 +110,25 @@ files:
96
110
  - bin/console
97
111
  - bin/setup
98
112
  - lib/traver.rb
113
+ - lib/traver/attributes_resolver.rb
114
+ - lib/traver/attributes_resolvers/active_record_attributes_resolver.rb
115
+ - lib/traver/attributes_resolvers/poro_attributes_resolver.rb
116
+ - lib/traver/default_params_creators/active_record_default_params_creator.rb
117
+ - lib/traver/default_params_creators/poro_default_params_creator.rb
118
+ - lib/traver/factories_loader.rb
119
+ - lib/traver/factory.rb
99
120
  - lib/traver/factory_definer.rb
100
121
  - lib/traver/graph.rb
101
122
  - lib/traver/graph_creator.rb
123
+ - lib/traver/list_creator.rb
102
124
  - lib/traver/object_creator.rb
125
+ - lib/traver/object_persisters/active_record_object_persister.rb
126
+ - lib/traver/object_persisters/poro_object_persister.rb
127
+ - lib/traver/sequencer.rb
128
+ - lib/traver/settings.rb
129
+ - lib/traver/settings/active_record_settings.rb
130
+ - lib/traver/settings/poro_settings.rb
131
+ - lib/traver/traver_constructor.rb
103
132
  - lib/traver/version.rb
104
133
  - traver.gemspec
105
134
  homepage: http://github.com/yukas/traver