judit-pickle 0.4.2
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/.gitignore +6 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +98 -0
- data/History.txt +409 -0
- data/License.txt +20 -0
- data/README.rdoc +367 -0
- data/Rakefile +20 -0
- data/Rakefile.d/cucumber.rake +24 -0
- data/Rakefile.d/jeweler.rake +23 -0
- data/Rakefile.d/rcov.rake +18 -0
- data/Rakefile.d/rspec.rake +7 -0
- data/Rakefile.d/yard.rake +5 -0
- data/Todo.txt +3 -0
- data/VERSION +1 -0
- data/features/app/app.rb +122 -0
- data/features/app/blueprints.rb +11 -0
- data/features/app/factories.rb +23 -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 +76 -0
- data/features/pickle/create_from_factory_girl.feature +63 -0
- data/features/pickle/create_from_machinist.feature +39 -0
- data/features/step_definitions/email_steps.rb +63 -0
- data/features/step_definitions/extra_email_steps.rb +7 -0
- data/features/step_definitions/fork_steps.rb +4 -0
- data/features/step_definitions/generator_steps.rb +46 -0
- data/features/step_definitions/path_steps.rb +14 -0
- data/features/step_definitions/pickle_steps.rb +100 -0
- data/features/step_definitions/raise_error_steps.rb +3 -0
- data/features/support/email.rb +21 -0
- data/features/support/env.rb +52 -0
- data/features/support/paths.rb +47 -0
- data/features/support/pickle.rb +26 -0
- data/features/support/pickle_app.rb +4 -0
- data/init.rb +0 -0
- data/lib/generators/pickle_generator.rb +69 -0
- data/lib/pickle/adapter.rb +137 -0
- data/lib/pickle/adapters/active_record.rb +57 -0
- data/lib/pickle/adapters/data_mapper.rb +42 -0
- data/lib/pickle/adapters/mongoid.rb +44 -0
- data/lib/pickle/config.rb +49 -0
- data/lib/pickle/email/parser.rb +18 -0
- data/lib/pickle/email/world.rb +13 -0
- data/lib/pickle/email.rb +77 -0
- data/lib/pickle/parser/matchers.rb +87 -0
- data/lib/pickle/parser.rb +65 -0
- data/lib/pickle/path/world.rb +5 -0
- data/lib/pickle/path.rb +45 -0
- data/lib/pickle/session/parser.rb +34 -0
- data/lib/pickle/session.rb +205 -0
- data/lib/pickle/version.rb +9 -0
- data/lib/pickle/world.rb +14 -0
- data/lib/pickle.rb +26 -0
- data/pickle.gemspec +134 -0
- data/rails_generators/pickle/pickle_generator.rb +33 -0
- data/rails_generators/pickle/templates/email.rb +21 -0
- data/rails_generators/pickle/templates/email_steps.rb +63 -0
- data/rails_generators/pickle/templates/paths.rb +47 -0
- data/rails_generators/pickle/templates/pickle.rb +28 -0
- data/rails_generators/pickle/templates/pickle_steps.rb +100 -0
- data/spec/pickle/adapter_spec.rb +186 -0
- data/spec/pickle/config_spec.rb +109 -0
- data/spec/pickle/email/parser_spec.rb +51 -0
- data/spec/pickle/email_spec.rb +166 -0
- data/spec/pickle/parser/matchers_spec.rb +70 -0
- data/spec/pickle/parser_spec.rb +161 -0
- data/spec/pickle/path_spec.rb +101 -0
- data/spec/pickle/session_spec.rb +434 -0
- data/spec/pickle_spec.rb +24 -0
- data/spec/spec_helper.rb +8 -0
- metadata +199 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
2
|
+
# It is recommended to regenerate this file in the future when you upgrade to a
|
3
|
+
# newer version of cucumber-rails. Consider adding your own code to a new file
|
4
|
+
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
5
|
+
# files.
|
6
|
+
|
7
|
+
ENV["RAILS_ENV"] ||= "cucumber"
|
8
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../cucumber_test_app/config/environment')
|
9
|
+
|
10
|
+
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
|
11
|
+
require 'cucumber/rails/world'
|
12
|
+
require 'cucumber/rails/active_record'
|
13
|
+
require 'cucumber/web/tableish'
|
14
|
+
|
15
|
+
require 'webrat'
|
16
|
+
require 'webrat/core/matchers'
|
17
|
+
|
18
|
+
Webrat.configure do |config|
|
19
|
+
config.mode = :rails
|
20
|
+
config.open_error_files = false # Set to true if you want error pages to pop up in the browser
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# If you set this to false, any error raised from within your app will bubble
|
25
|
+
# up to your step definition and out to cucumber unless you catch it somewhere
|
26
|
+
# on the way. You can make Rails rescue errors and render error pages on a
|
27
|
+
# per-scenario basis by tagging a scenario or feature with the @allow-rescue tag.
|
28
|
+
#
|
29
|
+
# If you set this to true, Rails will rescue all errors and render error
|
30
|
+
# pages, more or less in the same way your application would behave in the
|
31
|
+
# default production environment. It's not recommended to do this for all
|
32
|
+
# of your scenarios, as this makes it hard to discover errors in your application.
|
33
|
+
ActionController::Base.allow_rescue = false
|
34
|
+
|
35
|
+
# If you set this to true, each scenario will run in a database transaction.
|
36
|
+
# You can still turn off transactions on a per-scenario basis, simply tagging
|
37
|
+
# a feature or scenario with the @no-txn tag. If you are using Capybara,
|
38
|
+
# tagging with @culerity or @javascript will also turn transactions off.
|
39
|
+
#
|
40
|
+
# If you set this to false, transactions will be off for all scenarios,
|
41
|
+
# regardless of whether you use @no-txn or not.
|
42
|
+
#
|
43
|
+
# Beware that turning transactions off will leave data in your database
|
44
|
+
# after each scenario, which can lead to hard-to-debug failures in
|
45
|
+
# subsequent scenarios. If you do this, we recommend you create a Before
|
46
|
+
# block that will explicitly put your database in a known state.
|
47
|
+
Cucumber::Rails::World.use_transactional_fixtures = true
|
48
|
+
|
49
|
+
# How to clean your database when transactions are turned off. See
|
50
|
+
# http://github.com/bmabey/database_cleaner for more info.
|
51
|
+
require 'database_cleaner'
|
52
|
+
DatabaseCleaner.strategy = :truncation
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
# Maps a name to a path. Used by the
|
3
|
+
#
|
4
|
+
# When /^I go to (.+)$/ do |page_name|
|
5
|
+
#
|
6
|
+
# step definition in web_steps.rb
|
7
|
+
#
|
8
|
+
def path_to(page_name)
|
9
|
+
case page_name
|
10
|
+
|
11
|
+
when /the home\s?page/
|
12
|
+
'/'
|
13
|
+
|
14
|
+
# the following are examples using path_to_pickle
|
15
|
+
|
16
|
+
when /^#{capture_model}(?:'s)? page$/ # eg. the forum's page
|
17
|
+
path_to_pickle $1
|
18
|
+
|
19
|
+
when /^#{capture_model}(?:'s)? #{capture_model}(?:'s)? page$/ # eg. the forum's post's page
|
20
|
+
path_to_pickle $1, $2
|
21
|
+
|
22
|
+
when /^#{capture_model}(?:'s)? #{capture_model}'s (.+?) page$/ # eg. the forum's post's comments page
|
23
|
+
path_to_pickle $1, $2, :extra => $3 # or the forum's post's edit page
|
24
|
+
|
25
|
+
when /^#{capture_model}(?:'s)? (.+?) page$/ # eg. the forum's posts page
|
26
|
+
path_to_pickle $1, :extra => $2 # or the forum's edit page
|
27
|
+
|
28
|
+
# Add more mappings here.
|
29
|
+
# Here is an example that pulls values out of the Regexp:
|
30
|
+
#
|
31
|
+
# when /^(.*)'s profile page$/i
|
32
|
+
# user_profile_path(User.find_by_login($1))
|
33
|
+
|
34
|
+
else
|
35
|
+
begin
|
36
|
+
page_name =~ /the (.*) page/
|
37
|
+
path_components = $1.split(/\s+/)
|
38
|
+
self.send(path_components.push('path').join('_').to_sym)
|
39
|
+
rescue Object => e
|
40
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
41
|
+
"Now, go and add a mapping in #{__FILE__}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
World(NavigationHelpers)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# this file generated by script/generate pickle [paths] [email]
|
2
|
+
#
|
3
|
+
# Make sure that you are loading your factory of choice in your cucumber environment
|
4
|
+
#
|
5
|
+
# For machinist add: features/support/machinist.rb
|
6
|
+
#
|
7
|
+
# require 'machinist/active_record' # or your chosen adaptor
|
8
|
+
# require File.dirname(__FILE__) + '/../../spec/blueprints' # or wherever your blueprints are
|
9
|
+
# Before { Sham.reset } # to reset Sham's seed between scenarios so each run has same random sequences
|
10
|
+
#
|
11
|
+
# For FactoryGirl add: features/support/factory_girl.rb
|
12
|
+
#
|
13
|
+
# require 'factory_girl'
|
14
|
+
# require File.dirname(__FILE__) + '/../../spec/factories' # or wherever your factories are
|
15
|
+
#
|
16
|
+
# You may also need to add gem dependencies on your factory of choice in <tt>config/environments/cucumber.rb</tt>
|
17
|
+
|
18
|
+
require 'pickle/world'
|
19
|
+
# Example of configuring pickle:
|
20
|
+
#
|
21
|
+
# Pickle.configure do |config|
|
22
|
+
# config.adapters = [:machinist]
|
23
|
+
# config.map 'I', 'myself', 'me', 'my', :to => 'user: "me"'
|
24
|
+
# end
|
25
|
+
require 'pickle/path/world'
|
26
|
+
require 'pickle/email/world'
|
data/init.rb
ADDED
File without changes
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class PickleGenerator < Rails::Generators::Base
|
4
|
+
desc "Generates Pickle step files."
|
5
|
+
|
6
|
+
# Use the same templates as Rails 2 generator
|
7
|
+
source_root File.expand_path("../../../rails_generators/pickle/templates", __FILE__)
|
8
|
+
|
9
|
+
class_option :paths, :desc => "Generate features/support/paths.rb file.", :type => :boolean
|
10
|
+
class_option :email, :desc => "Generate features/step_definitions/email_steps.rb file", :type => :boolean
|
11
|
+
|
12
|
+
def initialize(args = [], options = {}, config = {})
|
13
|
+
super
|
14
|
+
|
15
|
+
if self.options.paths? && !File.exists?("features/support/paths.rb")
|
16
|
+
say "features/support/paths.rb not found, is your cucumber up to date?", :red
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_directories
|
22
|
+
empty_directory "features/step_definitions"
|
23
|
+
empty_directory "features/support"
|
24
|
+
end
|
25
|
+
|
26
|
+
def copy_pickle_steps_file
|
27
|
+
template "pickle_steps.rb", "features/step_definitions/pickle_steps.rb"
|
28
|
+
template "pickle.rb", "features/support/pickle.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_paths_file
|
32
|
+
return unless options.paths?
|
33
|
+
|
34
|
+
current_paths = File.read("features/support/paths.rb")
|
35
|
+
unless current_paths.include?('#{capture_model}')
|
36
|
+
if current_paths =~ /^(.*)(\n\s+else\n\s+raise "Can't find.*".*$)/m
|
37
|
+
@current_paths_header = $1
|
38
|
+
@current_paths_footer = $2
|
39
|
+
end
|
40
|
+
template "paths.rb", "features/support/paths.rb"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def copy_email_steps_file
|
45
|
+
return unless options.email?
|
46
|
+
template "email_steps.rb", "features/step_definitions/email_steps.rb"
|
47
|
+
template "email.rb", "features/support/email.rb"
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Compatibility methods for Rails 2 templates
|
54
|
+
def pickle_path
|
55
|
+
options.paths?
|
56
|
+
end
|
57
|
+
|
58
|
+
def pickle_email
|
59
|
+
options.email?
|
60
|
+
end
|
61
|
+
|
62
|
+
def current_paths_header
|
63
|
+
@current_paths_header
|
64
|
+
end
|
65
|
+
|
66
|
+
def current_paths_footer
|
67
|
+
@current_paths_footer
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module Pickle
|
4
|
+
# Abstract Factory adapter class, if you have a factory type setup, you
|
5
|
+
# can easily create an adaptor to make it work with Pickle.
|
6
|
+
#
|
7
|
+
# The factory adaptor must have a #factories class method that returns
|
8
|
+
# its instances, and each instance must respond to:
|
9
|
+
#
|
10
|
+
# #name : identifies the factory by name (default is attr_reader)
|
11
|
+
# #klass : returns the associated model class for this factory (default is attr_reader)
|
12
|
+
# #create(attrs = {}) : returns a newly created object
|
13
|
+
class Adapter
|
14
|
+
attr_reader :name, :klass
|
15
|
+
|
16
|
+
def create(attrs = {})
|
17
|
+
raise NotImplementedError, "create and return an object with the given attributes"
|
18
|
+
end
|
19
|
+
|
20
|
+
if respond_to?(:class_attribute)
|
21
|
+
class_attribute :model_classes
|
22
|
+
else
|
23
|
+
cattr_writer :model_classes
|
24
|
+
end
|
25
|
+
|
26
|
+
self.model_classes = nil
|
27
|
+
|
28
|
+
# Include this module into your ORM adapter
|
29
|
+
# this will register the adapter with pickle and it will be picked up for you
|
30
|
+
# To create an adapter you should create an inner constant "PickleAdapter"
|
31
|
+
#
|
32
|
+
# e.g. ActiveRecord::Base::PickleAdapter
|
33
|
+
#
|
34
|
+
# @see pickle/adapters/active_record
|
35
|
+
# @see pickle/adapters/datamapper
|
36
|
+
# @see pickle/adapters/mongoid
|
37
|
+
module Base
|
38
|
+
def self.included(base)
|
39
|
+
adapters << base
|
40
|
+
end
|
41
|
+
|
42
|
+
# A collection of registered adapters
|
43
|
+
def self.adapters
|
44
|
+
@@adapters ||= []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def factories
|
50
|
+
raise NotImplementedError, "return an array of factory adapter objects"
|
51
|
+
end
|
52
|
+
|
53
|
+
def model_classes
|
54
|
+
@@model_classes ||= self::Base.adapters.map{ |a| a.model_classes }.flatten
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the column names for the given ORM model class.
|
58
|
+
def column_names(klass)
|
59
|
+
klass.const_get(:PickleAdapter).column_names(klass)
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_model(klass, id)
|
63
|
+
klass.const_get(:PickleAdapter).get_model(klass, id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_first_model(klass, conditions)
|
67
|
+
klass.const_get(:PickleAdapter).find_first_model(klass, conditions)
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_all_models(klass, conditions)
|
71
|
+
klass.const_get(:PickleAdapter).find_all_models(klass, conditions)
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_model(klass, attributes)
|
75
|
+
klass.const_get(:PickleAdapter).create_model(klass, attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# machinist adapter
|
80
|
+
class Machinist < Adapter
|
81
|
+
def self.factories
|
82
|
+
factories = []
|
83
|
+
model_classes.each do |klass|
|
84
|
+
if blueprints = klass.instance_variable_get('@blueprints')
|
85
|
+
blueprints.keys.each {|blueprint| factories << new(klass, blueprint)}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
factories
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize(klass, blueprint)
|
92
|
+
@klass, @blueprint = klass, blueprint
|
93
|
+
@name = @klass.name.underscore.gsub('/','_')
|
94
|
+
@name = "#{@blueprint}_#{@name}" unless @blueprint == :master
|
95
|
+
end
|
96
|
+
|
97
|
+
def create(attrs = {})
|
98
|
+
if @klass.respond_to?('make!')
|
99
|
+
@klass.send(:make!, @blueprint, attrs)
|
100
|
+
else
|
101
|
+
@klass.send(:make, @blueprint, attrs)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# factory-girl adapter
|
107
|
+
class FactoryGirl < Adapter
|
108
|
+
def self.factories
|
109
|
+
(::Factory.factories.values rescue []).map {|factory| new(factory)}
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize(factory)
|
113
|
+
@klass, @name = factory.build_class, factory.factory_name.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def create(attrs = {})
|
117
|
+
Factory(@name, attrs)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# ORM adapter. If you have no factory adapter, you can use this adapter to
|
122
|
+
# use your orm as 'factory' - ie create objects
|
123
|
+
class Orm < Adapter
|
124
|
+
def self.factories
|
125
|
+
model_classes.map{|k| new(k)}
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize(klass)
|
129
|
+
@klass, @name = klass, klass.name.underscore.gsub('/','_')
|
130
|
+
end
|
131
|
+
|
132
|
+
def create(attrs = {})
|
133
|
+
Pickle::Adapter.create_model(@klass, attrs)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
begin
|
2
|
+
require 'activerecord'
|
3
|
+
rescue LoadError
|
4
|
+
require 'active_record'
|
5
|
+
end
|
6
|
+
|
7
|
+
class ActiveRecord::Base
|
8
|
+
module PickleAdapter
|
9
|
+
include Pickle::Adapter::Base
|
10
|
+
|
11
|
+
# Do not consider these to be part of the class list
|
12
|
+
def self.except_classes
|
13
|
+
@@except_classes ||= [
|
14
|
+
"CGI::Session::ActiveRecordStore::Session",
|
15
|
+
"ActiveRecord::SessionStore::Session"
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets a list of the available models for this adapter
|
20
|
+
def self.model_classes
|
21
|
+
begin
|
22
|
+
klasses = ::ActiveRecord::Base.__send__(:descendants) # Rails 3
|
23
|
+
rescue
|
24
|
+
klasses = ::ActiveRecord::Base.__send__(:subclasses) # Rails 2
|
25
|
+
end
|
26
|
+
|
27
|
+
klasses.select do |klass|
|
28
|
+
!klass.abstract_class? && klass.table_exists? && !except_classes.include?(klass.name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# get a list of column names for a given class
|
33
|
+
def self.column_names(klass)
|
34
|
+
klass.column_names
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get an instance by id of the model
|
38
|
+
def self.get_model(klass, id)
|
39
|
+
klass.find(id)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Find the first instance matching conditions
|
43
|
+
def self.find_first_model(klass, conditions)
|
44
|
+
klass.find(:first, :conditions => conditions)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Find all models matching conditions
|
48
|
+
def self.find_all_models(klass, conditions)
|
49
|
+
klass.find(:all, :conditions => conditions)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a model using attributes
|
53
|
+
def self.create_model(klass, attributes)
|
54
|
+
klass.create!(attributes)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
|
3
|
+
module DataMapper::Resource
|
4
|
+
module PickleAdapter
|
5
|
+
include Pickle::Adapter::Base
|
6
|
+
|
7
|
+
# Do not consider these to be part of the class list
|
8
|
+
def self.except_classes
|
9
|
+
@@except_classes ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Gets a list of the available models for this adapter
|
13
|
+
def self.model_classes
|
14
|
+
::DataMapper::Model.descendants.to_a.select{|k| !except_classes.include?(k.name)}
|
15
|
+
end
|
16
|
+
|
17
|
+
# get a list of column names for a given class
|
18
|
+
def self.column_names(klass)
|
19
|
+
klass.properties.map(&:name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get an instance by id of the model
|
23
|
+
def self.get_model(klass, id)
|
24
|
+
klass.get(id)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Find the first instance matching conditions
|
28
|
+
def self.find_first_model(klass, conditions)
|
29
|
+
klass.first(conditions)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Find all models matching conditions
|
33
|
+
def self.find_all_models(klass, conditions)
|
34
|
+
klass.all(conditions)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create a model using attributes
|
38
|
+
def self.create_model(klass, attributes)
|
39
|
+
klass.create(attributes)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Document
|
5
|
+
module PickleAdapter
|
6
|
+
include Pickle::Adapter::Base
|
7
|
+
|
8
|
+
# Do not consider these to be part of the class list
|
9
|
+
def self.except_classes
|
10
|
+
@@except_classes ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Gets a list of the available models for this adapter
|
14
|
+
def self.model_classes
|
15
|
+
ObjectSpace.each_object(Class).to_a.select {|klass| klass.ancestors.include? Mongoid::Document}
|
16
|
+
end
|
17
|
+
|
18
|
+
# get a list of column names for a given class
|
19
|
+
def self.column_names(klass)
|
20
|
+
klass.fields.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get an instance by id of the model
|
24
|
+
def self.get_model(klass, id)
|
25
|
+
klass.find(id)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Find the first instance matching conditions
|
29
|
+
def self.find_first_model(klass, conditions)
|
30
|
+
klass.first(:conditions => conditions)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find all models matching conditions
|
34
|
+
def self.find_all_models(klass, conditions)
|
35
|
+
klass.all(:conditions => conditions)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create a model with given attributes
|
39
|
+
def self.create_model(klass, attributes)
|
40
|
+
klass.create!(attributes)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Pickle
|
2
|
+
class Config
|
3
|
+
attr_writer :adapters, :factories, :mappings, :predicates
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
configure(&block) if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def configure(&block)
|
10
|
+
yield(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def adapters
|
14
|
+
@adapters ||= [:machinist, :factory_girl, :orm]
|
15
|
+
end
|
16
|
+
|
17
|
+
def adapter_classes
|
18
|
+
adapters.map {|a| a.is_a?(Class) ? a : "pickle/adapter/#{a}".classify.constantize}
|
19
|
+
end
|
20
|
+
|
21
|
+
def factories
|
22
|
+
@factories ||= adapter_classes.reverse.inject({}) do |factories, adapter|
|
23
|
+
factories.merge(adapter.factories.inject({}){|h, f| h.merge(f.name => f)})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def predicates
|
28
|
+
@predicates ||= Pickle::Adapter.model_classes.map do |k|
|
29
|
+
k.public_instance_methods.select {|m| m =~ /\?$/} + Pickle::Adapter.column_names(k)
|
30
|
+
end.flatten.uniq
|
31
|
+
end
|
32
|
+
|
33
|
+
class Mapping < Struct.new(:search, :replacement)
|
34
|
+
end
|
35
|
+
|
36
|
+
def mappings
|
37
|
+
@mappings ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
# Usage: map 'me', 'myself', 'I', :to => 'user: "me"'
|
41
|
+
def map(*args)
|
42
|
+
options = args.extract_options!
|
43
|
+
raise ArgumentError, "Usage: map 'search' [, 'search2', ...] :to => 'replace'" unless args.any? && options[:to].is_a?(String)
|
44
|
+
args.each do |search|
|
45
|
+
self.mappings << Mapping.new(search, options[:to])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
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
|
data/lib/pickle/email.rb
ADDED
@@ -0,0 +1,77 @@
|
|
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::Browser.run(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 match_data = email.encoded.match(%r{<a[^>]*href=['"]?([^'"]*)['"]?[^>]*?>[^<]*?#{link_text}[^<]*?</a>})
|
68
|
+
match_data[1]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def links_in_email(email, protos=['http', 'https'])
|
73
|
+
URI.extract(email.encoded.to_s, protos)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|