pickle 0.5.0 → 0.5.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock.development +33 -35
- data/History.txt +15 -0
- data/README.md +566 -0
- data/features/step_definitions/email_steps.rb +11 -9
- data/features/step_definitions/pickle_steps.rb +9 -4
- data/lib/pickle.rb +3 -3
- data/lib/pickle/adapter.rb +16 -4
- data/lib/pickle/adapters/active_record.rb +17 -7
- data/lib/pickle/adapters/data_mapper.rb +1 -1
- data/lib/pickle/adapters/mongoid.rb +13 -3
- data/lib/pickle/config.rb +8 -8
- data/lib/pickle/email.rb +13 -3
- data/lib/pickle/email/parser.rb +2 -2
- data/lib/pickle/parser.rb +9 -9
- data/lib/pickle/parser/matchers.rb +18 -18
- data/lib/pickle/path.rb +3 -3
- data/lib/pickle/session.rb +72 -39
- data/lib/pickle/session/parser.rb +3 -3
- data/lib/pickle/version.rb +1 -1
- data/pickle.gemspec +3 -3
- data/rails_generators/pickle/templates/email_steps.rb +11 -9
- data/rails_generators/pickle/templates/pickle_steps.rb +9 -4
- data/spec/pickle/adapter_spec.rb +40 -52
- data/spec/pickle/email_spec.rb +37 -16
- data/spec/pickle/session_spec.rb +21 -5
- metadata +4 -6
- data/README.rdoc +0 -483
@@ -27,37 +27,39 @@ When(/^(?:I|they) click the first link in #{capture_email}$/) do |email_ref|
|
|
27
27
|
end
|
28
28
|
|
29
29
|
Then(/^(\d)+ emails? should be delivered to (.*)$/) do |count, to|
|
30
|
-
|
30
|
+
actual = emails("to: \"#{email_for(to)}\"").size
|
31
|
+
expect(actual).to eq(count.to_i), "Expected #{count} emails but encountered #{actual} delivered to #{to}"
|
31
32
|
end
|
32
33
|
|
33
34
|
Then(/^(\d)+ emails? should be delivered with #{capture_fields}$/) do |count, fields|
|
34
|
-
|
35
|
+
actual = emails(fields).size
|
36
|
+
expect(actual).to eq(count.to_i), "Expected #{count} emails but encountered #{actual} to be delivered with #{fields}"
|
35
37
|
end
|
36
38
|
|
37
39
|
Then(/^#{capture_email} should be delivered to (.+)$/) do |email_ref, to|
|
38
|
-
expect(email(email_ref, "to: \"#{email_for(to)}\"")).not_to be_nil
|
40
|
+
expect(email(email_ref, "to: \"#{email_for(to)}\"")).not_to be_nil, "Failed to find #{email_ref} delivered to: #{to}"
|
39
41
|
end
|
40
42
|
|
41
43
|
Then(/^#{capture_email} should not be delivered to (.+)$/) do |email_ref, to|
|
42
|
-
expect(email(email_ref, "to: \"#{email_for(to)}\"")).to be_nil
|
44
|
+
expect(email(email_ref, "to: \"#{email_for(to)}\"")).to be_nil, "Unexpectedly found #{email_ref} delivered to: #{to}"
|
43
45
|
end
|
44
46
|
|
45
47
|
Then(/^#{capture_email} should have #{capture_fields}$/) do |email_ref, fields|
|
46
|
-
expect(email(email_ref, fields)).not_to be_nil
|
48
|
+
expect(email(email_ref, fields)).not_to be_nil, "Failed to find #{fields} in #{email_ref}"
|
47
49
|
end
|
48
50
|
|
49
51
|
Then(/^#{capture_email} should contain "(.*)"$/) do |email_ref, text|
|
50
|
-
expect(email(email_ref).body).to match(/#{text}/)
|
52
|
+
expect(email(email_ref).body).to match(/#{text}/), "Failed to find \"#{text}\" in #{email_ref}"
|
51
53
|
end
|
52
54
|
|
53
55
|
Then(/^#{capture_email} should not contain "(.*)"$/) do |email_ref, text|
|
54
|
-
expect(email(email_ref).body).not_to match(/#{text}/)
|
56
|
+
expect(email(email_ref).body).not_to match(/#{text}/), "Unexpectedly found \"#{text}\" in #{email_ref}"
|
55
57
|
end
|
56
58
|
|
57
59
|
Then(/^#{capture_email} should link to (.+)$/) do |email_ref, page|
|
58
|
-
expect(email(email_ref).body).to match(/#{path_to(page)}/)
|
60
|
+
expect(email(email_ref).body).to match(/#{path_to(page)}/), "Failed to find link to #{page} in #{email_ref}"
|
59
61
|
end
|
60
62
|
|
61
63
|
Then(/^show me the emails?$/) do
|
62
|
-
|
64
|
+
save_and_open_emails
|
63
65
|
end
|
@@ -7,7 +7,7 @@ end
|
|
7
7
|
|
8
8
|
# create n models
|
9
9
|
Given(/^(\d+) #{capture_plural_factory} exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
|
10
|
-
count
|
10
|
+
create_models(count, plural_factory.singularize, fields)
|
11
11
|
end
|
12
12
|
|
13
13
|
# create models from a table
|
@@ -26,10 +26,15 @@ Then(/^#{capture_model} should not exist(?: with #{capture_fields})?$/) do |name
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# find models with a table
|
29
|
-
Then(/^the following #{capture_plural_factory} should
|
29
|
+
Then(/^the following #{capture_plural_factory} should exist:?$/) do |plural_factory, table|
|
30
30
|
expect(find_models_from_table(plural_factory, table)).not_to be_any(&:nil?)
|
31
31
|
end
|
32
32
|
|
33
|
+
# not find models with a table
|
34
|
+
Then(/^the following #{capture_plural_factory} should not exists?:?$/) do |plural_factory, table|
|
35
|
+
find_models_from_table(plural_factory, table).should be_all(&:nil?)
|
36
|
+
end
|
37
|
+
|
33
38
|
# find exactly n models
|
34
39
|
Then(/^(\d+) #{capture_plural_factory} should exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
|
35
40
|
expect(find_models(plural_factory.singularize, fields).size).to eq(count.to_i)
|
@@ -60,7 +65,7 @@ Then(/^#{capture_model} should not be #{capture_model}(?:'s)? (\w+)$/) do |targe
|
|
60
65
|
expect(model!(owner).send(association)).not_to eq(model!(target))
|
61
66
|
end
|
62
67
|
|
63
|
-
# assert model.predicate?
|
68
|
+
# assert model.predicate?
|
64
69
|
Then(/^#{capture_model} should (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
|
65
70
|
if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
|
66
71
|
expect(model!(name)).to send("have_#{predicate.gsub(' ', '_')}")
|
@@ -83,7 +88,7 @@ end
|
|
83
88
|
Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
|
84
89
|
actual_value = model(name).send(attribute)
|
85
90
|
expectation = expectation.gsub("should", "to").gsub(" ", "_")
|
86
|
-
|
91
|
+
|
87
92
|
case expected
|
88
93
|
when 'nil', 'true', 'false'
|
89
94
|
expect(actual_value).send(expectation, eq(eval(expected)))
|
data/lib/pickle.rb
CHANGED
@@ -14,13 +14,13 @@ module Pickle
|
|
14
14
|
def config
|
15
15
|
@config ||= Config.new
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def configure(&block)
|
19
19
|
config.configure(&block)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def parser(options = {})
|
23
23
|
@parser ||= Parser.new({:config => config}.merge(options))
|
24
24
|
end
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
data/lib/pickle/adapter.rb
CHANGED
@@ -70,7 +70,7 @@ module Pickle
|
|
70
70
|
def find_all_models(klass, conditions)
|
71
71
|
klass.const_get(:PickleAdapter).find_all_models(klass, conditions)
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
def create_model(klass, attributes)
|
75
75
|
klass.const_get(:PickleAdapter).create_model(klass, attributes)
|
76
76
|
end
|
@@ -104,16 +104,20 @@ module Pickle
|
|
104
104
|
def self.factories
|
105
105
|
if defined? ::FactoryGirl
|
106
106
|
factories = []
|
107
|
-
::FactoryGirl.factories.each
|
107
|
+
::FactoryGirl.factories.each do |factory|
|
108
|
+
factory.names.each do |name|
|
109
|
+
factories << new(factory, name)
|
110
|
+
end
|
111
|
+
end
|
108
112
|
factories
|
109
113
|
else
|
110
114
|
(::Factory.factories.values rescue []).map {|factory| new(factory)}
|
111
115
|
end
|
112
116
|
end
|
113
117
|
|
114
|
-
def initialize(factory)
|
118
|
+
def initialize(factory, factory_name)
|
115
119
|
if defined? ::FactoryGirl
|
116
|
-
@klass, @name = factory.build_class,
|
120
|
+
@klass, @name = factory.build_class, factory_name.to_s
|
117
121
|
else
|
118
122
|
@klass, @name = factory.build_class, factory.factory_name.to_s
|
119
123
|
end
|
@@ -126,6 +130,14 @@ module Pickle
|
|
126
130
|
Factory(@name, attrs)
|
127
131
|
end
|
128
132
|
end
|
133
|
+
|
134
|
+
def build(attrs = {})
|
135
|
+
if defined? ::FactoryGirl
|
136
|
+
::FactoryGirl.build(@name, attrs)
|
137
|
+
else
|
138
|
+
Factory.build(@name, attrs)
|
139
|
+
end
|
140
|
+
end
|
129
141
|
end
|
130
142
|
|
131
143
|
# fabrication adapter
|
@@ -18,10 +18,10 @@ class ActiveRecord::Base
|
|
18
18
|
|
19
19
|
# Gets a list of the available models for this adapter
|
20
20
|
def self.model_classes
|
21
|
-
|
22
|
-
klasses = ::ActiveRecord::Base.
|
23
|
-
|
24
|
-
klasses = ::ActiveRecord::Base.
|
21
|
+
if ::ActiveRecord::Base.respond_to?(:descendants)
|
22
|
+
klasses = ::ActiveRecord::Base.descendants # Rails 3, 4
|
23
|
+
else
|
24
|
+
klasses = ::ActiveRecord::Base.subclasses # Rails 2
|
25
25
|
end
|
26
26
|
|
27
27
|
klasses.select do |klass|
|
@@ -41,14 +41,24 @@ class ActiveRecord::Base
|
|
41
41
|
|
42
42
|
# Find the first instance matching conditions
|
43
43
|
def self.find_first_model(klass, conditions)
|
44
|
-
|
44
|
+
if defined? ::ActiveRecord::Relation
|
45
|
+
klass.where(conditions).first # Rails 3, 4
|
46
|
+
else
|
47
|
+
klass.find(:first,
|
48
|
+
:conditions => conditions) # Rails 2
|
49
|
+
end
|
45
50
|
end
|
46
51
|
|
47
52
|
# Find all models matching conditions
|
48
53
|
def self.find_all_models(klass, conditions)
|
49
|
-
|
54
|
+
if defined? ::ActiveRecord::Relation
|
55
|
+
klass.where(conditions) # Rails 3, 4
|
56
|
+
else
|
57
|
+
klass.find(:all,
|
58
|
+
:conditions => conditions) # Rails 2
|
59
|
+
end
|
50
60
|
end
|
51
|
-
|
61
|
+
|
52
62
|
# Create a model using attributes
|
53
63
|
def self.create_model(klass, attributes)
|
54
64
|
klass.create!(attributes)
|
@@ -12,7 +12,9 @@ module Mongoid
|
|
12
12
|
|
13
13
|
# Gets a list of the available models for this adapter
|
14
14
|
def self.model_classes
|
15
|
-
ObjectSpace.each_object(Class).to_a.select
|
15
|
+
ObjectSpace.each_object(Class).to_a.select do |klass|
|
16
|
+
klass.name && klass.ancestors.include?(Mongoid::Document)
|
17
|
+
end
|
16
18
|
end
|
17
19
|
|
18
20
|
# get a list of column names for a given class
|
@@ -27,12 +29,20 @@ module Mongoid
|
|
27
29
|
|
28
30
|
# Find the first instance matching conditions
|
29
31
|
def self.find_first_model(klass, conditions)
|
30
|
-
|
32
|
+
if defined? ::Mongoid::Criteria
|
33
|
+
klass.where(conditions).first
|
34
|
+
else
|
35
|
+
klass.first(:conditions => conditions)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
# Find all models matching conditions
|
34
40
|
def self.find_all_models(klass, conditions)
|
35
|
-
|
41
|
+
if defined? ::Mongoid::Criteria
|
42
|
+
klass.where(conditions).to_a
|
43
|
+
else
|
44
|
+
klass.all(:conditions => conditions)
|
45
|
+
end
|
36
46
|
end
|
37
47
|
|
38
48
|
# Create a model with given attributes
|
data/lib/pickle/config.rb
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
module Pickle
|
2
2
|
class Config
|
3
3
|
attr_writer :adapters, :factories, :mappings, :predicates
|
4
|
-
|
4
|
+
|
5
5
|
def initialize(&block)
|
6
6
|
configure(&block) if block_given?
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def configure(&block)
|
10
10
|
yield(self)
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def adapters
|
14
14
|
@adapters ||= [:machinist, :factory_girl, :fabrication, :orm]
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def adapter_classes
|
18
18
|
adapters.map {|a| a.is_a?(Class) ? a : "pickle/adapter/#{a}".classify.constantize}
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def factories
|
22
22
|
@factories ||= adapter_classes.reverse.inject({}) do |factories, adapter|
|
23
23
|
factories.merge(adapter.factories.inject({}){|h, f| h.merge(f.name => f)})
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def predicates
|
28
28
|
@predicates ||= Pickle::Adapter.model_classes.map do |k|
|
29
29
|
k.public_instance_methods.select {|m| m =~ /\?$/} + Pickle::Adapter.column_names(k)
|
@@ -32,11 +32,11 @@ module Pickle
|
|
32
32
|
|
33
33
|
class Mapping < Struct.new(:search, :replacement)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def mappings
|
37
37
|
@mappings ||= []
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
# Usage: map 'me', 'myself', 'I', :to => 'user: "me"'
|
41
41
|
def map(*args)
|
42
42
|
options = args.extract_options!
|
data/lib/pickle/email.rb
CHANGED
@@ -31,7 +31,7 @@ module Pickle
|
|
31
31
|
protected
|
32
32
|
def open_in_browser(path) # :nodoc
|
33
33
|
require "launchy"
|
34
|
-
Launchy
|
34
|
+
Launchy.open(path)
|
35
35
|
rescue LoadError
|
36
36
|
warn "Sorry, you need to install launchy to open emails: `gem install launchy`"
|
37
37
|
end
|
@@ -64,13 +64,23 @@ module Pickle
|
|
64
64
|
|
65
65
|
# e.g. Click here in <a href="http://confirm">Click here</a>
|
66
66
|
def parse_email_for_anchor_text_link(email, link_text)
|
67
|
-
if
|
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>})
|
68
73
|
match_data[1]
|
69
74
|
end
|
70
75
|
end
|
71
76
|
|
72
77
|
def links_in_email(email, protos=['http', 'https'])
|
73
|
-
|
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)
|
74
84
|
end
|
75
85
|
|
76
86
|
end
|
data/lib/pickle/email/parser.rb
CHANGED
data/lib/pickle/parser.rb
CHANGED
@@ -3,13 +3,13 @@ require 'pickle/parser/matchers'
|
|
3
3
|
module Pickle
|
4
4
|
class Parser
|
5
5
|
include Matchers
|
6
|
-
|
6
|
+
|
7
7
|
attr_reader :config
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(options = {})
|
10
10
|
@config = options[:config] || raise(ArgumentError, "Parser.new requires a :config")
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
# given a string like 'foo: "bar", bar: "baz"' returns {"foo" => "bar", "bar" => "baz"}
|
14
14
|
def parse_fields(fields)
|
15
15
|
if fields.blank?
|
@@ -19,10 +19,10 @@ module Pickle
|
|
19
19
|
m.merge(parse_field(match[0]))
|
20
20
|
end
|
21
21
|
else
|
22
|
-
raise ArgumentError, "The fields string is not in the correct format.\n\n'#{fields}' did not match: #{match_fields}"
|
22
|
+
raise ArgumentError, "The fields string is not in the correct format.\n\n'#{fields}' did not match: #{match_fields}"
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# given a string like 'foo: expr' returns {key => value}
|
27
27
|
def parse_field(field)
|
28
28
|
if field =~ /^#{capture_key_and_value_in_field}$/
|
@@ -31,12 +31,12 @@ module Pickle
|
|
31
31
|
raise ArgumentError, "The field argument is not in the correct format.\n\n'#{field}' did not match: #{match_field}"
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# returns really underscored name
|
36
36
|
def canonical(str)
|
37
37
|
str.to_s.underscore.gsub(' ','_').gsub('/','_')
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
# return [factory_name, name or integer index]
|
41
41
|
def parse_model(model_name)
|
42
42
|
apply_mappings!(model_name)
|
@@ -46,7 +46,7 @@ module Pickle
|
|
46
46
|
[canonical($1), canonical($2)]
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def parse_index(index)
|
51
51
|
case index
|
52
52
|
when nil, '', 'last' then -1
|
@@ -62,4 +62,4 @@ module Pickle
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
65
|
-
end
|
65
|
+
end
|
@@ -4,19 +4,19 @@ module Pickle
|
|
4
4
|
def match_ordinal
|
5
5
|
'(?:\d+(?:st|nd|rd|th))'
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def match_index
|
9
9
|
"(?:first|last|#{match_ordinal})"
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def match_prefix
|
13
13
|
'(?:(?:a|an|another|the|that) )'
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def match_quoted
|
17
17
|
'(?:\\\\"|[^\\"]|\\.)*'
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def match_label
|
21
21
|
"(?::? \"#{match_quoted}\")"
|
22
22
|
end
|
@@ -28,60 +28,60 @@ module Pickle
|
|
28
28
|
def match_field
|
29
29
|
"(?:\\w+: #{match_value})"
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def match_fields
|
33
33
|
"(?:#{match_field}, )*#{match_field}"
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def match_mapping
|
37
37
|
"(?:#{config.mappings.map(&:search).join('|')})"
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
def match_factory
|
41
41
|
"(?:#{config.factories.keys.map{|n| n.gsub('_','[_ ]')}.join('|')})"
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def match_plural_factory
|
45
45
|
"(?:#{config.factories.keys.map{|n| n.pluralize.gsub('_','[_ ]')}.join('|')})"
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def match_indexed_model
|
49
49
|
"(?:(?:#{match_index} )?#{match_factory})"
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def match_labeled_model
|
53
53
|
"(?:#{match_factory}#{match_label})"
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def match_model
|
57
57
|
"(?:#{match_mapping}|#{match_prefix}?(?:#{match_indexed_model}|#{match_labeled_model}))"
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def match_predicate
|
61
61
|
"(?:#{config.predicates.map{|m| m.to_s.sub(/^has_/,'').sub(/\?$/,'').gsub('_','[_ ]')}.join('|')})"
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# create capture analogues of match methods
|
65
65
|
instance_methods.select{|m| m =~ /^match_/}.each do |method|
|
66
|
-
eval <<-end_eval
|
66
|
+
eval <<-end_eval
|
67
67
|
def #{method.to_s.sub('match_', 'capture_')} # def capture_field
|
68
68
|
"(" + #{method} + ")" # "(" + match_field + ")"
|
69
69
|
end # end
|
70
70
|
end_eval
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
# special capture methods
|
74
74
|
def capture_number_in_ordinal
|
75
75
|
'(?:(\d+)(?:st|nd|rd|th))'
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
def capture_name_in_label
|
79
79
|
"(?::? \"(#{match_quoted})\")"
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
def capture_key_and_value_in_field
|
83
83
|
"(?:(\\w+): #{capture_value})"
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
|
-
end
|
87
|
+
end
|