spbtv_pickle 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +3 -0
- data/Gemfile.lock.development +158 -0
- data/History.txt +499 -0
- data/License.txt +20 -0
- data/README.md +566 -0
- data/Rakefile +20 -0
- data/Rakefile.d/cucumber.rake +27 -0
- data/Rakefile.d/release.rake +44 -0
- data/Rakefile.d/rspec.rake +3 -0
- data/Rakefile.d/yard.rake +5 -0
- data/Todo.txt +3 -0
- data/autotest/discover.rb +9 -0
- data/features/app/app.rb +128 -0
- data/features/app/blueprints.rb +6 -0
- data/features/app/fabricators.rb +6 -0
- data/features/app/factories.rb +25 -0
- data/features/app/views/notifier/email.erb +1 -0
- data/features/app/views/notifier/user_email.erb +6 -0
- data/features/email/email.feature +64 -0
- data/features/generator/generators.feature +59 -0
- data/features/path/models_page.feature +44 -0
- data/features/path/named_route_page.feature +10 -0
- data/features/pickle/create_from_active_record.feature +83 -0
- data/features/pickle/create_from_fabrication.feature +46 -0
- data/features/pickle/create_from_factory_girl.feature +66 -0
- data/features/pickle/create_from_machinist.feature +46 -0
- data/features/step_definitions/email_steps.rb +1 -0
- data/features/step_definitions/extra_email_steps.rb +12 -0
- data/features/step_definitions/fork_steps.rb +4 -0
- data/features/step_definitions/generator_steps.rb +52 -0
- data/features/step_definitions/path_steps.rb +14 -0
- data/features/step_definitions/pickle_steps.rb +1 -0
- data/features/step_definitions/raise_error_steps.rb +7 -0
- data/features/support/email.rb +1 -0
- data/features/support/env.rb +14 -0
- data/features/support/paths.rb +47 -0
- data/features/support/pickle.rb +27 -0
- data/features/support/pickle_app.rb +4 -0
- data/init.rb +0 -0
- data/lib/generators/pickle_generator.rb +44 -0
- data/lib/pickle.rb +26 -0
- data/lib/pickle/adapter.rb +183 -0
- data/lib/pickle/adapters/active_record.rb +67 -0
- data/lib/pickle/adapters/data_mapper.rb +42 -0
- data/lib/pickle/adapters/mongoid.rb +54 -0
- data/lib/pickle/config.rb +49 -0
- data/lib/pickle/email.rb +87 -0
- data/lib/pickle/email/parser.rb +18 -0
- data/lib/pickle/email/world.rb +13 -0
- data/lib/pickle/parser.rb +65 -0
- data/lib/pickle/parser/matchers.rb +87 -0
- data/lib/pickle/path.rb +45 -0
- data/lib/pickle/path/world.rb +5 -0
- data/lib/pickle/session.rb +244 -0
- data/lib/pickle/session/parser.rb +34 -0
- data/lib/pickle/version.rb +3 -0
- data/lib/pickle/world.rb +14 -0
- data/lib/spbtv_pickle.rb +1 -0
- data/rails_generators/pickle/pickle_generator.rb +31 -0
- data/rails_generators/pickle/templates/email.rb +21 -0
- data/rails_generators/pickle/templates/email_steps.rb +65 -0
- data/rails_generators/pickle/templates/paths.rb +47 -0
- data/rails_generators/pickle/templates/pickle.rb +29 -0
- data/rails_generators/pickle/templates/pickle_steps.rb +105 -0
- data/spbtv_pickle.gemspec +38 -0
- data/spec/pickle/adapter_spec.rb +203 -0
- data/spec/pickle/config_spec.rb +112 -0
- data/spec/pickle/email/parser_spec.rb +51 -0
- data/spec/pickle/email_spec.rb +187 -0
- data/spec/pickle/parser/matchers_spec.rb +70 -0
- data/spec/pickle/parser_spec.rb +165 -0
- data/spec/pickle/path_spec.rb +120 -0
- data/spec/pickle/session_spec.rb +448 -0
- data/spec/pickle_spec.rb +24 -0
- data/spec/spec_helper.rb +78 -0
- metadata +370 -0
data/lib/pickle/email.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Pickle
|
2
|
+
module Email
|
3
|
+
# return the deliveries array, optionally selected by the passed fields
|
4
|
+
def emails(fields = nil)
|
5
|
+
@emails = ActionMailer::Base.deliveries.select {|m| email_has_fields?(m, fields)}
|
6
|
+
end
|
7
|
+
|
8
|
+
def email(ref, fields = nil)
|
9
|
+
(match = ref.match(/^#{capture_index_in_email}$/)) or raise ArgumentError, "argument should match #{match_email}"
|
10
|
+
@emails or raise RuntimeError, "Call #emails before calling #email"
|
11
|
+
index = parse_index(match[1])
|
12
|
+
email_has_fields?(@emails[index], fields) ? @emails[index] : nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def email_has_fields?(email, fields)
|
16
|
+
parse_fields(fields).each do |key, val|
|
17
|
+
return false unless (Array(email.send(key)) & Array(val)).any?
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_in_email(email, link_text)
|
23
|
+
visit(parse_email_for_link(email, link_text))
|
24
|
+
end
|
25
|
+
|
26
|
+
def click_first_link_in_email(email)
|
27
|
+
link = links_in_email(email).first
|
28
|
+
visit link
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def open_in_browser(path) # :nodoc
|
33
|
+
require "launchy"
|
34
|
+
Launchy.open(path)
|
35
|
+
rescue LoadError
|
36
|
+
warn "Sorry, you need to install launchy to open emails: `gem install launchy`"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Saves the emails out to RAILS_ROOT/tmp/ and opens it in the default
|
40
|
+
# web browser if on OS X. (depends on webrat)
|
41
|
+
def save_and_open_emails
|
42
|
+
emails_to_open = @emails || emails
|
43
|
+
filename = "pickle-email-#{Time.now.to_i}.html"
|
44
|
+
File.open(filename, "w") do |f|
|
45
|
+
emails_to_open.each_with_index do |e, i|
|
46
|
+
f.write "<h1>Email #{i+1}</h1><pre>#{e}</pre><hr />"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
open_in_browser(filename)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_email_for_link(email, text_or_regex)
|
53
|
+
url = parse_email_for_explicit_link(email, text_or_regex)
|
54
|
+
url ||= parse_email_for_anchor_text_link(email, text_or_regex)
|
55
|
+
raise "No link found matching #{text_or_regex.inspect} in #{email}" unless url
|
56
|
+
url
|
57
|
+
end
|
58
|
+
|
59
|
+
# e.g. confirm in http://confirm
|
60
|
+
def parse_email_for_explicit_link(email, regex)
|
61
|
+
regex = /#{Regexp.escape(regex)}/ unless regex.is_a?(Regexp)
|
62
|
+
links_in_email(email).detect { |link| link =~ regex }
|
63
|
+
end
|
64
|
+
|
65
|
+
# e.g. Click here in <a href="http://confirm">Click here</a>
|
66
|
+
def parse_email_for_anchor_text_link(email, link_text)
|
67
|
+
if email.multipart?
|
68
|
+
body = email.html_part.body
|
69
|
+
else
|
70
|
+
body = email.body
|
71
|
+
end
|
72
|
+
if match_data = body.match(%r{<a[^>]*href=['"]?([^'"]*)['"]?[^>]*?>[^<]*?#{link_text}[^<]*?</a>})
|
73
|
+
match_data[1]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def links_in_email(email, protos=['http', 'https'])
|
78
|
+
if email.multipart?
|
79
|
+
body = email.html_part.body
|
80
|
+
else
|
81
|
+
body = email.body
|
82
|
+
end
|
83
|
+
URI.extract(body.to_s, protos)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Pickle
|
2
|
+
module Email
|
3
|
+
# add ability to parse emails
|
4
|
+
module Parser
|
5
|
+
def match_email
|
6
|
+
"(?:#{match_prefix}?(?:#{match_index} )?email)"
|
7
|
+
end
|
8
|
+
|
9
|
+
def capture_email
|
10
|
+
"(#{match_email})"
|
11
|
+
end
|
12
|
+
|
13
|
+
def capture_index_in_email
|
14
|
+
"(?:#{match_prefix}?(?:#{capture_index} )?email)"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'pickle'
|
2
|
+
require 'pickle/email'
|
3
|
+
require 'pickle/email/parser'
|
4
|
+
|
5
|
+
# add email parser expressions
|
6
|
+
Pickle::Parser.send :include, Pickle::Email::Parser
|
7
|
+
|
8
|
+
World(Pickle::Email)
|
9
|
+
|
10
|
+
# shortcuts for use in step regexps
|
11
|
+
class << self
|
12
|
+
delegate :capture_email, :to => 'Pickle.parser'
|
13
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'pickle/parser/matchers'
|
2
|
+
|
3
|
+
module Pickle
|
4
|
+
class Parser
|
5
|
+
include Matchers
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@config = options[:config] || raise(ArgumentError, "Parser.new requires a :config")
|
11
|
+
end
|
12
|
+
|
13
|
+
# given a string like 'foo: "bar", bar: "baz"' returns {"foo" => "bar", "bar" => "baz"}
|
14
|
+
def parse_fields(fields)
|
15
|
+
if fields.blank?
|
16
|
+
{}
|
17
|
+
elsif fields =~ /^#{match_fields}$/
|
18
|
+
fields.scan(/(#{match_field})(?:,|$)/).inject({}) do |m, match|
|
19
|
+
m.merge(parse_field(match[0]))
|
20
|
+
end
|
21
|
+
else
|
22
|
+
raise ArgumentError, "The fields string is not in the correct format.\n\n'#{fields}' did not match: #{match_fields}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# given a string like 'foo: expr' returns {key => value}
|
27
|
+
def parse_field(field)
|
28
|
+
if field =~ /^#{capture_key_and_value_in_field}$/
|
29
|
+
{ $1 => eval($2) }
|
30
|
+
else
|
31
|
+
raise ArgumentError, "The field argument is not in the correct format.\n\n'#{field}' did not match: #{match_field}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns really underscored name
|
36
|
+
def canonical(str)
|
37
|
+
str.to_s.underscore.gsub(' ','_').gsub('/','_')
|
38
|
+
end
|
39
|
+
|
40
|
+
# return [factory_name, name or integer index]
|
41
|
+
def parse_model(model_name)
|
42
|
+
apply_mappings!(model_name)
|
43
|
+
if /#{capture_index} #{capture_factory}$/ =~ model_name
|
44
|
+
[canonical($2), parse_index($1)]
|
45
|
+
elsif /#{capture_factory}#{capture_name_in_label}?$/ =~ model_name
|
46
|
+
[canonical($1), canonical($2)]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_index(index)
|
51
|
+
case index
|
52
|
+
when nil, '', 'last' then -1
|
53
|
+
when /#{capture_number_in_ordinal}/ then $1.to_i - 1
|
54
|
+
when 'first' then 0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def apply_mappings!(string)
|
60
|
+
config.mappings.each do |mapping|
|
61
|
+
string.sub! /^#{mapping.search}$/, mapping.replacement
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Pickle
|
2
|
+
class Parser
|
3
|
+
module Matchers
|
4
|
+
def match_ordinal
|
5
|
+
'(?:\d+(?:st|nd|rd|th))'
|
6
|
+
end
|
7
|
+
|
8
|
+
def match_index
|
9
|
+
"(?:first|last|#{match_ordinal})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def match_prefix
|
13
|
+
'(?:(?:a|an|another|the|that) )'
|
14
|
+
end
|
15
|
+
|
16
|
+
def match_quoted
|
17
|
+
'(?:\\\\"|[^\\"]|\\.)*'
|
18
|
+
end
|
19
|
+
|
20
|
+
def match_label
|
21
|
+
"(?::? \"#{match_quoted}\")"
|
22
|
+
end
|
23
|
+
|
24
|
+
def match_value
|
25
|
+
"(?:\"#{match_quoted}\"|nil|true|false|[+-]?[0-9_]+(?:\\.\\d+)?)"
|
26
|
+
end
|
27
|
+
|
28
|
+
def match_field
|
29
|
+
"(?:\\w+: #{match_value})"
|
30
|
+
end
|
31
|
+
|
32
|
+
def match_fields
|
33
|
+
"(?:#{match_field}, )*#{match_field}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def match_mapping
|
37
|
+
"(?:#{config.mappings.map(&:search).join('|')})"
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_factory
|
41
|
+
"(?:#{config.factories.keys.map{|n| n.gsub('_','[_ ]')}.join('|')})"
|
42
|
+
end
|
43
|
+
|
44
|
+
def match_plural_factory
|
45
|
+
"(?:#{config.factories.keys.map{|n| n.pluralize.gsub('_','[_ ]')}.join('|')})"
|
46
|
+
end
|
47
|
+
|
48
|
+
def match_indexed_model
|
49
|
+
"(?:(?:#{match_index} )?#{match_factory})"
|
50
|
+
end
|
51
|
+
|
52
|
+
def match_labeled_model
|
53
|
+
"(?:#{match_factory}#{match_label})"
|
54
|
+
end
|
55
|
+
|
56
|
+
def match_model
|
57
|
+
"(?:#{match_mapping}|#{match_prefix}?(?:#{match_indexed_model}|#{match_labeled_model}))"
|
58
|
+
end
|
59
|
+
|
60
|
+
def match_predicate
|
61
|
+
"(?:#{config.predicates.map{|m| m.to_s.sub(/^has_/,'').sub(/\?$/,'').gsub('_','[_ ]')}.join('|')})"
|
62
|
+
end
|
63
|
+
|
64
|
+
# create capture analogues of match methods
|
65
|
+
instance_methods.select{|m| m =~ /^match_/}.each do |method|
|
66
|
+
eval <<-end_eval
|
67
|
+
def #{method.to_s.sub('match_', 'capture_')} # def capture_field
|
68
|
+
"(" + #{method} + ")" # "(" + match_field + ")"
|
69
|
+
end # end
|
70
|
+
end_eval
|
71
|
+
end
|
72
|
+
|
73
|
+
# special capture methods
|
74
|
+
def capture_number_in_ordinal
|
75
|
+
'(?:(\d+)(?:st|nd|rd|th))'
|
76
|
+
end
|
77
|
+
|
78
|
+
def capture_name_in_label
|
79
|
+
"(?::? \"(#{match_quoted})\")"
|
80
|
+
end
|
81
|
+
|
82
|
+
def capture_key_and_value_in_field
|
83
|
+
"(?:(\\w+): #{capture_value})"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/pickle/path.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Pickle
|
2
|
+
module Path
|
3
|
+
# given args of pickle model name, and an optional extra action, or segment, will attempt to find
|
4
|
+
# a matching named route
|
5
|
+
#
|
6
|
+
# path_to_pickle 'the user', :action => 'edit' # => /users/3/edit
|
7
|
+
# path_to_pickle 'the user', 'the comment' # => /users/3/comments/1
|
8
|
+
# path_to_pickle 'the user', :segment => 'comments' # => /users/3/comments
|
9
|
+
#
|
10
|
+
# If you don;t know if the 'extra' part of the path is an action or a segment, then just
|
11
|
+
# pass it as 'extra' and this method will run through the possibilities
|
12
|
+
#
|
13
|
+
# path_to_pickle 'the user', :extra => 'new comment' # => /users/3/comments/new
|
14
|
+
def path_to_pickle(*pickle_names)
|
15
|
+
options = pickle_names.extract_options!
|
16
|
+
resources = pickle_names.map{|n| model(n) || n.to_sym}
|
17
|
+
if options[:extra]
|
18
|
+
parts = options[:extra].underscore.gsub(' ','_').split("_")
|
19
|
+
find_pickle_path_using_action_segment_combinations(resources, parts)
|
20
|
+
else
|
21
|
+
pickle_path_for_resources_action_segment(resources, options[:action], options[:segment])
|
22
|
+
end or raise "Could not figure out a path for #{pickle_names.inspect} #{options.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def find_pickle_path_using_action_segment_combinations(resources, parts)
|
27
|
+
path = nil
|
28
|
+
(0..parts.length).each do |idx|
|
29
|
+
action = parts.slice(0, idx).join('_')
|
30
|
+
segment = parts.slice(idx, parts.length).join('_')
|
31
|
+
path = pickle_path_for_resources_action_segment(resources, action, segment) and break
|
32
|
+
end
|
33
|
+
path
|
34
|
+
end
|
35
|
+
|
36
|
+
def pickle_path_for_resources_action_segment(resources, action, segment)
|
37
|
+
action.blank? or action = action.downcase.gsub(' ','_')
|
38
|
+
segment.blank? or segment = segment.downcase.gsub(' ','_')
|
39
|
+
resource_names = resources.map{|s| s.is_a?(Symbol) ? s.to_s : s.class.name.underscore.gsub('/', '_')}.join("_")
|
40
|
+
models = resources.reject{|s| s.is_a?(Symbol)}
|
41
|
+
parts = [action, resource_names, segment].reject(&:blank?)
|
42
|
+
send("#{parts.join('_')}_path", *models) rescue nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
module Pickle
|
2
|
+
module Session
|
3
|
+
class ModelNotKnownError < RuntimeError
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name, message = nil)
|
7
|
+
@name = name
|
8
|
+
@message = message.presence || "The model: '#{name}' is not known in this scenario. Use #create_model to create, or #find_model to find, and store a reference in this scenario."
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
@message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ModelNotFoundError < RuntimeError
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def included(world_class)
|
21
|
+
proxy_to_pickle_parser(world_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
def extended(world_object)
|
25
|
+
proxy_to_pickle_parser(class << world_object; self; end) # metaclass is not 2.1 compatible
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def proxy_to_pickle_parser(world_class)
|
30
|
+
world_class.class_eval do
|
31
|
+
unless methods.include?('method_missing_with_pickle_parser')
|
32
|
+
alias_method_chain :method_missing, :pickle_parser
|
33
|
+
alias_method_chain :respond_to?, :pickle_parser
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_model(pickle_ref, fields = nil)
|
40
|
+
create_or_build_model(:create, 1, pickle_ref, fields)
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_models(count, pickle_ref, fields = nil)
|
44
|
+
create_or_build_model(:create, count, pickle_ref, fields)
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_model(pickle_ref, fields = nil)
|
48
|
+
create_or_build_model(:build, 1, pickle_ref, fields)
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_models(count, pickle_ref, fields = nil)
|
52
|
+
create_or_build_model(:build, count, pickle_ref, fields)
|
53
|
+
end
|
54
|
+
|
55
|
+
# if a column exists in the table which matches the singular factory name, this is used as the pickle ref
|
56
|
+
def create_models_from_table(plural_factory, table)
|
57
|
+
factory = plural_factory.singularize
|
58
|
+
table.hashes.map do |hash|
|
59
|
+
pickle_ref = factory + (hash[factory] ? " \"#{hash.delete(factory)}\"" : "")
|
60
|
+
create_model(pickle_ref, hash)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_model(a_model_name, fields = nil)
|
65
|
+
factory, name = *parse_model(a_model_name)
|
66
|
+
|
67
|
+
raise ArgumentError, "Can't find a model with an ordinal (e.g. 1st user)" if name.is_a?(Integer)
|
68
|
+
|
69
|
+
model_class = pickle_config.factories[factory].klass
|
70
|
+
fields = fields.is_a?(Hash) ? parse_hash(fields) : parse_fields(fields)
|
71
|
+
conditions = convert_models_to_attributes(model_class, fields)
|
72
|
+
record = Pickle::Adapter.find_first_model(model_class, conditions)
|
73
|
+
|
74
|
+
store_model(factory, name, record) if record
|
75
|
+
|
76
|
+
record
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_model!(name, fields = nil)
|
80
|
+
find_model(name, fields) or raise ModelNotFoundError, "Can't find #{name}#{" with #{fields}" if fields.present?} from the orm in this scenario"
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_models(factory, fields = nil)
|
84
|
+
factory = pickle_parser.canonical(factory)
|
85
|
+
|
86
|
+
models_by_index(factory).clear
|
87
|
+
|
88
|
+
model_class = pickle_config.factories[factory].klass
|
89
|
+
conditions = convert_models_to_attributes(model_class, parse_fields(fields))
|
90
|
+
records = Pickle::Adapter.find_all_models(model_class, conditions)
|
91
|
+
|
92
|
+
records.each {|record| store_model(factory, nil, record)}
|
93
|
+
end
|
94
|
+
|
95
|
+
# if a column exists in the table which matches the singular factory name, this is used as the pickle ref
|
96
|
+
def find_models_from_table(plural_factory, table)
|
97
|
+
factory = plural_factory.singularize
|
98
|
+
table.hashes.map do |hash|
|
99
|
+
pickle_ref = factory + (hash[factory] ? " \"#{hash.delete(factory)}\"" : "")
|
100
|
+
find_model(pickle_ref, hash)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# return the original model stored by create_model or find_model
|
105
|
+
def created_model(name)
|
106
|
+
factory, name_or_index = *parse_model(name)
|
107
|
+
|
108
|
+
if name_or_index.blank?
|
109
|
+
models_by_index(factory).last
|
110
|
+
elsif name_or_index.is_a?(Integer)
|
111
|
+
models_by_index(factory)[name_or_index]
|
112
|
+
else
|
113
|
+
models_by_name(factory)[name_or_index] or raise ModelNotKnownError, name
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# predicate version which raises no errors
|
118
|
+
def created_model?(name)
|
119
|
+
(created_model(name) rescue nil) ? true : false
|
120
|
+
end
|
121
|
+
|
122
|
+
# return a newly selected model
|
123
|
+
def model(name)
|
124
|
+
model = created_model(name)
|
125
|
+
return nil unless model
|
126
|
+
Pickle::Adapter.get_model(model.class, model.id)
|
127
|
+
end
|
128
|
+
|
129
|
+
# predicate version which raises no errors
|
130
|
+
def model?(name)
|
131
|
+
(model(name) rescue nil) ? true : false
|
132
|
+
end
|
133
|
+
|
134
|
+
# like model, but raise an error if it can't be found
|
135
|
+
def model!(name)
|
136
|
+
model(name) or raise ModelNotKnownError, name
|
137
|
+
end
|
138
|
+
|
139
|
+
# like created_model, but raise an error if it can't be found
|
140
|
+
def created_model!(name)
|
141
|
+
created_model(name) or raise ModelNotKnownError, name
|
142
|
+
end
|
143
|
+
|
144
|
+
# return all original models of specified type
|
145
|
+
def created_models(factory)
|
146
|
+
models_by_index(factory)
|
147
|
+
end
|
148
|
+
|
149
|
+
# return all models of specified type (freshly selected from the database)
|
150
|
+
def models(factory)
|
151
|
+
created_models(factory).map do |model|
|
152
|
+
Pickle::Adapter.get_model(model.class, model.id)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def respond_to_with_pickle_parser?(method, include_private = false)
|
157
|
+
respond_to_without_pickle_parser?(method, include_private) || pickle_parser.respond_to?(method, include_private)
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
def create_or_build_model(method, count, pickle_ref, fields = nil)
|
162
|
+
factory, label = *parse_model(pickle_ref)
|
163
|
+
raise ArgumentError, "Can't #{method} with an ordinal (e.g. 1st user)" if label.is_a?(Integer)
|
164
|
+
fields = fields.is_a?(Hash) ? parse_hash(fields) : parse_fields(fields)
|
165
|
+
|
166
|
+
count.to_i.times.map do
|
167
|
+
record = pickle_config.factories[factory].send(method, fields)
|
168
|
+
store_model(factory, label, record)
|
169
|
+
return record if count == 1
|
170
|
+
record
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def method_missing_with_pickle_parser(method, *args, &block)
|
175
|
+
if pickle_parser.respond_to?(method)
|
176
|
+
pickle_parser.send(method, *args, &block)
|
177
|
+
else
|
178
|
+
method_missing_without_pickle_parser(method, *args, &block)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def pickle_parser=(parser)
|
183
|
+
parser.session = self
|
184
|
+
@pickle_parser = parser
|
185
|
+
end
|
186
|
+
|
187
|
+
def pickle_parser
|
188
|
+
@pickle_parser or self.pickle_parser = Pickle.parser
|
189
|
+
end
|
190
|
+
|
191
|
+
def pickle_config
|
192
|
+
pickle_parser.config
|
193
|
+
end
|
194
|
+
|
195
|
+
def convert_models_to_attributes(klass, attrs)
|
196
|
+
columns = nil
|
197
|
+
conditions = {}
|
198
|
+
|
199
|
+
attrs.each do |key, val|
|
200
|
+
if supported = supported_association_model_type?(val) or val.nil?
|
201
|
+
columns ||= Pickle::Adapter.column_names(klass)
|
202
|
+
end
|
203
|
+
|
204
|
+
if supported && columns.include?("#{key}_id")
|
205
|
+
conditions["#{key}_id"] = val.id
|
206
|
+
conditions["#{key}_type"] = val.class.base_class.name if columns.include?("#{key}_type")
|
207
|
+
elsif val.nil? && columns.include?("#{key}_id") && !columns.include?("#{key}")
|
208
|
+
# NOOP
|
209
|
+
else
|
210
|
+
conditions[key] = val
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
conditions
|
215
|
+
end
|
216
|
+
|
217
|
+
def supported_association_model_type?(associated_model)
|
218
|
+
(defined?(ActiveRecord::Base) && associated_model.is_a?(ActiveRecord::Base)) ||
|
219
|
+
(defined?(DataMapper::Model) && associated_model.is_a?(DataMapper::Model)) ||
|
220
|
+
(defined?(Mongoid::Document) && associated_model.is_a?(Mongoid::Document))
|
221
|
+
end
|
222
|
+
|
223
|
+
def models_by_name(factory)
|
224
|
+
@models_by_name ||= {}
|
225
|
+
@models_by_name[pickle_parser.canonical(factory)] ||= {}
|
226
|
+
end
|
227
|
+
|
228
|
+
def models_by_index(factory)
|
229
|
+
@models_by_index ||= {}
|
230
|
+
@models_by_index[pickle_parser.canonical(factory)] ||= []
|
231
|
+
end
|
232
|
+
|
233
|
+
# if the factory name != the model name, store under both names
|
234
|
+
def store_model(factory, name, record)
|
235
|
+
store_record(record.class.name, name, record) unless pickle_parser.canonical(factory) == pickle_parser.canonical(record.class.name)
|
236
|
+
store_record(factory, name, record)
|
237
|
+
end
|
238
|
+
|
239
|
+
def store_record(factory, name, record)
|
240
|
+
models_by_name(factory)[name] = record
|
241
|
+
models_by_index(factory) << record
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|