qcourses 0.1.8 → 0.1.9

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/.autotest CHANGED
@@ -1,5 +1,5 @@
1
1
  Autotest.add_hook(:initialize) { |at|
2
- at.add_exception %r{^\.git}
3
- at.add_exception %r{^log}
2
+ at.add_exception %r{^\./\.git}
3
+ at.add_exception %r{^\./log}
4
4
  nil
5
5
  }
data/.rspec CHANGED
@@ -1,2 +1 @@
1
1
  --color
2
- --backtrace
data/Gemfile.lock CHANGED
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- qcourses (0.1.7)
4
+ qcourses (0.1.8)
5
5
  haml (~> 3.1.4)
6
+ pony (~> 1.4)
6
7
  sequel (~> 3.33.0)
7
- sinatra (~> 1.2.6)
8
+ sinatra (~> 1.3)
8
9
  sqlite3 (~> 1.3.5)
9
10
 
10
11
  GEM
@@ -22,14 +23,27 @@ GEM
22
23
  childprocess (0.3.2)
23
24
  ffi (~> 1.0.6)
24
25
  diff-lcs (1.1.3)
26
+ email_spec (1.2.1)
27
+ mail (~> 2.2)
28
+ rspec (~> 2.0)
25
29
  ffi (1.0.11)
26
30
  haml (3.1.4)
31
+ i18n (0.6.0)
27
32
  libwebsocket (0.1.3)
28
33
  addressable
34
+ mail (2.4.4)
35
+ i18n (>= 0.4.0)
36
+ mime-types (~> 1.16)
37
+ treetop (~> 1.4.8)
29
38
  mime-types (1.18)
30
39
  multi_json (1.3.2)
31
40
  nokogiri (1.5.2)
41
+ polyglot (0.3.3)
42
+ pony (1.4)
43
+ mail (> 2.0)
32
44
  rack (1.4.1)
45
+ rack-protection (1.2.0)
46
+ rack
33
47
  rack-test (0.6.1)
34
48
  rack (>= 1.0)
35
49
  rspec (2.9.0)
@@ -48,11 +62,17 @@ GEM
48
62
  multi_json (~> 1.0)
49
63
  rubyzip
50
64
  sequel (3.33.0)
51
- sinatra (1.2.8)
52
- rack (~> 1.1)
53
- tilt (>= 1.2.2, < 2.0)
65
+ shotgun (0.9)
66
+ rack (>= 1.0)
67
+ sinatra (1.3.2)
68
+ rack (~> 1.3, >= 1.3.6)
69
+ rack-protection (~> 1.2)
70
+ tilt (~> 1.3, >= 1.3.3)
54
71
  sqlite3 (1.3.6)
55
72
  tilt (1.3.3)
73
+ treetop (1.4.10)
74
+ polyglot
75
+ polyglot (>= 0.3.1)
56
76
  xpath (0.1.4)
57
77
  nokogiri (~> 1.3)
58
78
 
@@ -62,6 +82,8 @@ PLATFORMS
62
82
  DEPENDENCIES
63
83
  ZenTest
64
84
  capybara
85
+ email_spec
65
86
  qcourses!
66
87
  rack-test
67
88
  rspec (~> 2.8)
89
+ shotgun
data/lib/factories.rb CHANGED
@@ -2,14 +2,14 @@ module Rubory
2
2
  def self.define(name, &definition)
3
3
  factories.define(name, &definition)
4
4
  end
5
- def self.create(name)
5
+ def self.create(name, attribute_overrides = {})
6
6
  factories.create(name)
7
7
  end
8
- def self.build(name)
9
- factories.build(name)
8
+ def self.build(name, attribute_overrides = {})
9
+ factories.build(name, attribute_overrides)
10
10
  end
11
- def self.attributes_for(name)
12
- factories.attributes_for(name)
11
+ def self.attributes_for(name, attribute_overrides = {})
12
+ factories.attributes_for(name, attribute_overrides)
13
13
  end
14
14
  def self.use_default_module(default_module)
15
15
  factories.use_default_module(default_module)
@@ -31,16 +31,16 @@ module Rubory
31
31
  def define(name, &definition)
32
32
  factories[name] = CreationDsl.create_factory(name, self, &definition)
33
33
  end
34
- def create(name)
35
- object = build(name)
34
+ def create(name, attribute_overrides = {})
35
+ object = build(name, attribute_overrides)
36
36
  object.save
37
37
  object
38
38
  end
39
- def build(name)
40
- factory(name).build
39
+ def build(name, attribute_overrides={})
40
+ factory(name).build(attribute_overrides)
41
41
  end
42
- def attributes_for(name)
43
- object = build(name)
42
+ def attributes_for(name, attribute_overrides = {})
43
+ object = build(name, attribute_overrides)
44
44
  object.values || object.attributes || raise(NotSupportedException.new(":attributes_for is not supported as your model does not support :values or :attributes methods"))
45
45
  end
46
46
  def factory(name)
@@ -68,7 +68,7 @@ module Rubory
68
68
  end
69
69
  def self.create_factory(name,context, &definition)
70
70
  f = Factory.new(name, context)
71
- new(f,context).instance_eval(&definition)
71
+ definition.call(new(f,context))
72
72
  return f
73
73
  end
74
74
  def method_missing(method, *args, &creation_block)
@@ -92,8 +92,8 @@ module Rubory
92
92
  def value_for(attribute, &block)
93
93
  @attributes[attribute] = block;
94
94
  end
95
- def build
96
- @context.create_instance class_name, Hash[ @attributes.to_a.collect{|attribute| [attribute[0], attribute[1].call(count)]} ]
95
+ def build(attribute_overrides)
96
+ @context.create_instance class_name, Hash[ attributes(attribute_overrides) ]
97
97
  end
98
98
  private
99
99
  def count
@@ -105,6 +105,14 @@ module Rubory
105
105
  def camelize(str)
106
106
  str.to_s.split("_").map {|w| w.capitalize}.join
107
107
  end
108
+ def attributes(attribute_overrides)
109
+ @attributes.merge(attribute_overrides).to_a.map do |attribute|
110
+ [attribute[0], attribute_value(attribute[1])]
111
+ end
112
+ end
113
+ def attribute_value(value)
114
+ value.is_a?(Proc) ? value.call(count) : value
115
+ end
108
116
  end
109
117
  class NoSuchFactoryException < Exception; end
110
118
  class NotSupportedException < Exception; end
data/lib/qcourses.rb CHANGED
@@ -17,6 +17,7 @@ module Qcourses
17
17
  require 'qcourses/date_ex'
18
18
  require 'qcourses/string_ex'
19
19
  require 'qcourses/models'
20
+ Postman.allow_delivery_failure if env == 'development'
20
21
  end
21
22
  end
22
23
  require 'qcourses/renderers'
@@ -1,14 +1,33 @@
1
1
  require 'sinatra/base'
2
+ require 'sinatra/showexceptions'
2
3
  require 'qcourses/resource_paths'
3
4
  require 'haml'
5
+
6
+
4
7
  module Qcourses
5
- class BaseController < Sinatra::Application
8
+ class BaseController < Sinatra::Base
6
9
  include ResourcePaths
7
10
  include Configuration
8
11
  set :root, Proc.new { Qcourses.config.root }
9
12
  set :public_folder, File.expand_path('../../../public/', File.dirname(__FILE__))
10
13
  resource_admin_on '/admin'
11
14
 
15
+ configure :development do
16
+ use Rack::Logger, ::Logger::DEBUG
17
+ end
18
+
19
+ configure :production do
20
+ use Rack::Logger, ::Logger::INFO
21
+ end
22
+
23
+ configure :test do
24
+ disable :logging
25
+ end
26
+
27
+ before do
28
+ logger.info request.path
29
+ end
30
+
12
31
  helpers do
13
32
  include ViewHelpers
14
33
  include Renderers
@@ -47,13 +47,12 @@ module Qcourses
47
47
  end
48
48
 
49
49
  put admin_request_url(:id) do
50
+ @event = Event.find(params[:id])
50
51
  begin
51
- @event = Event.find(params[:id])
52
- if @event.update event_params
53
- redirect admin_request_url
54
- else
55
- haml :'events/edit', layout: :admin
56
- end
52
+ @event.update event_params
53
+ redirect admin_request_url
54
+ rescue ValidationFailed
55
+ haml :'events/edit', layout: :admin
57
56
  rescue Exception => e
58
57
  haml :'trainings/error', locals: { message: e.message }
59
58
  end
@@ -65,7 +64,7 @@ module Qcourses
65
64
  end
66
65
  def filter_event_params
67
66
  result = params[:event]
68
- valid_parameters = ["course_id", "from", "to", "location"]
67
+ valid_parameters = ["course_id", "from", "to", "location", "max_participants"]
69
68
  if (result[:location_id] && result[:location_id] != '0')
70
69
  valid_parameters << "location_id"
71
70
  valid_parameters.delete 'location'
@@ -18,14 +18,17 @@ module Qcourses
18
18
 
19
19
 
20
20
  begin
21
+ registration = nil
21
22
  Registration.db.transaction do
22
23
  @company.save
23
24
  @employees.each do |employee|
24
25
  employee.company = @company
25
26
  employee.save
26
- Registration.create(event: @event, employee:employee)
27
+ registration = Registration.create(event: @event, employee:employee)
27
28
  end
28
29
  end
30
+ Postman.deliver_registration_notification(RegistrationNotification.new(@employees.first, @event))
31
+ Postman.deliver_registration_confirmation(@employees.first.email, RegistrationNotification.new(@employees.first, @event))
29
32
  redirect "/registrations/success"
30
33
  rescue Sequel::ValidationFailed
31
34
  haml :'/registrations/new', layout: :registration
@@ -21,7 +21,7 @@ module Qcourses
21
21
  end
22
22
 
23
23
  Qcourses.create_connection
24
-
24
+ require_relative 'models/postman'
25
25
  require_relative 'models/course_repository'
26
26
  require_relative 'models/course'
27
27
  require_relative 'models/location'
@@ -3,7 +3,11 @@ module Qcourses
3
3
  class Event < Sequel::Model
4
4
  plugin :validation_helpers
5
5
  many_to_one :location
6
+ one_to_many :registrations
6
7
 
8
+ def initialize(attributes = {})
9
+ super({:open => true}.merge attributes)
10
+ end
7
11
  def self.opens(object_id)
8
12
  first{ ({id => object_id}) & (from > Time.now) }
9
13
  end
@@ -12,6 +16,10 @@ module Qcourses
12
16
  order(:from).filter { from > Time.now }
13
17
  end
14
18
 
19
+ def self.find(id)
20
+ self[id]
21
+ end
22
+
15
23
  def validate
16
24
  super
17
25
  validates_presence :from
@@ -31,6 +39,10 @@ module Qcourses
31
39
  course.name
32
40
  end
33
41
 
42
+ def open?
43
+ max_participants > registrations.size
44
+ end
45
+
34
46
  def subtitle
35
47
  return '' unless course
36
48
  course.subtitle
@@ -0,0 +1,50 @@
1
+ require 'pony'
2
+ module Qcourses
3
+
4
+ class RegistrationNotification
5
+ def initialize(employee, event)
6
+ @employee = employee
7
+ @event = event
8
+ end
9
+ def event_name
10
+ @event.name
11
+ end
12
+ def inspect
13
+ "RegistrationNotification(for: #{employee.name}, about: #{event.name})"
14
+ end
15
+ def ==(other)
16
+ return false unless other.is_a? RegistrationNotification
17
+ return other.employee.email == employee.email && other.event == event
18
+ end
19
+ protected
20
+ attr_reader :employee, :event
21
+ end
22
+
23
+ class Postman
24
+ FROM_ADDRESS = "info@qwan.it"
25
+ NOTIFICATION_ADDRESS = "info@qwan.it"
26
+ @@allow_delivery_failure = false
27
+ def self.allow_delivery_failure
28
+ @@allow_delivery_failure = true
29
+ end
30
+ def self.dont_allow_delivery_failure
31
+ @@allow_delivery_failure = false
32
+ end
33
+ def self.deliver_registration_notification(registration_confirmation)
34
+ deliver(from: FROM_ADDRESS, to: NOTIFICATION_ADDRESS,
35
+ subject: "Course Registration for #{registration_confirmation.event_name}")
36
+ end
37
+ def self.deliver_registration_confirmation(addressee, registration_confirmation)
38
+ deliver(from: FROM_ADDRESS,
39
+ to: addressee,
40
+ subject: "Thank you for your registration for #{registration_confirmation.event_name}")
41
+ end
42
+ def self.deliver(options)
43
+ begin
44
+ Pony.mail(options)
45
+ rescue Exception => e
46
+ raise e unless @@allow_delivery_failure
47
+ end
48
+ end
49
+ end
50
+ end
@@ -6,7 +6,6 @@ end
6
6
 
7
7
  desc "project environment"
8
8
  task :environment do
9
- p "setting environment"
10
9
  ENV['RACK_ENV'] ||= 'development'
11
10
  end
12
11
 
@@ -1,3 +1,3 @@
1
1
  module Qcourses
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -4,7 +4,7 @@ module Qcourses
4
4
  return '' unless options
5
5
  options.collect do |option|
6
6
  tag_attributes = {:value => option[0]}
7
- tag_attributes[:selected] = "selected" if value == option[0]
7
+ tag_attributes[:selected] = "selected" if value.to_s == option[0].to_s
8
8
  haml "%option#{tag_attributes.inspect} #{option[1]}", :layout => false
9
9
  end.join
10
10
  end
@@ -32,7 +32,7 @@ module Qcourses
32
32
  end
33
33
 
34
34
  def event_registration_link(event)
35
- return "closed" unless event.open
35
+ return "closed" unless event.open?
36
36
  haml "%a{ :href=> url('/registrations/new/#{event.id}') } register"
37
37
  end
38
38
 
data/qcourses.gemspec CHANGED
@@ -20,13 +20,16 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # specify any dependencies here; for example:
22
22
  # s.add_development_dependency "rspec"
23
- s.add_runtime_dependency "sinatra", "~> 1.2.6"
23
+ s.add_runtime_dependency "sinatra", "~> 1.3"
24
24
  s.add_runtime_dependency "haml", "~> 3.1.4"
25
25
  s.add_runtime_dependency "sequel", "~> 3.33.0"
26
26
  s.add_runtime_dependency "sqlite3", "~> 1.3.5"
27
+ s.add_runtime_dependency "pony", "~> 1.4"
27
28
 
28
29
  s.add_development_dependency "rspec", "~> 2.8"
29
30
  s.add_development_dependency "rack-test"
30
31
  s.add_development_dependency "ZenTest"
31
32
  s.add_development_dependency "capybara"
33
+ s.add_development_dependency "shotgun"
34
+ s.add_development_dependency "email_spec"
32
35
  end
@@ -18,9 +18,9 @@ module Rubory
18
18
  let(:factories) { Factories.new("Rubory") }
19
19
 
20
20
  before do
21
- factories.define(:person) do
22
- name "name"
23
- email { "email" }
21
+ factories.define(:person) do |f|
22
+ f.name "name"
23
+ f.email { "email" }
24
24
  end
25
25
  end
26
26
 
@@ -34,14 +34,26 @@ module Rubory
34
34
  person = factories.build(:person)
35
35
  person.email.should == 'email'
36
36
  end
37
+ it "can override attributes" do
38
+ person = factories.build(:person, :name => "overridden name")
39
+ person.name.should == 'overridden name'
40
+ end
41
+ it "can override attributes with string key" do
42
+ person = factories.build(:person, "name" => "overridden name")
43
+ person.name.should == 'overridden name'
44
+ end
45
+ it "can specify an attribute missing in the factory definition" do
46
+ person = factories.build(:person, sirname: "sirname")
47
+ person.sirname.should == 'sirname'
48
+ end
37
49
  it "throws no such factory if factory does not exist" do
38
50
  expect { factories.build(:bogus) }.to raise_exception Rubory::NoSuchFactoryException
39
51
  end
40
52
  end
41
53
  context "when attribute has n as block param" do
42
54
  it "generates a unique object" do
43
- factories.define(:person) do
44
- name {|n| "name_#{n}" }
55
+ factories.define(:person) do |f|
56
+ f.name {|n| "name_#{n}" }
45
57
  end
46
58
  factories.build(:person).name.should == "name_1"
47
59
  factories.build(:person).name.should == "name_2"
@@ -49,9 +61,9 @@ module Rubory
49
61
  end
50
62
  context "when an object has dependencies" do
51
63
  before do
52
- factories.define(:address) do
53
- city "city"
54
- associate :person
64
+ factories.define(:address) do |f|
65
+ f.city "city"
66
+ f.associate :person
55
67
  end
56
68
  end
57
69
  let(:address) { factories.build :address }
@@ -64,6 +76,16 @@ module Rubory
64
76
  address.person.should be_saved
65
77
  end
66
78
  end
79
+ describe "attribute" do
80
+ it "of type boolean supported" do
81
+ factories.define(:person) { |f| f.male true }
82
+ factories.build(:person).male.should be_true
83
+ end
84
+ it "with name open supported" do
85
+ factories.define(:person) { |f| f.open {true} }
86
+ factories.build(:person).open.should be_true
87
+ end
88
+ end
67
89
  context "when the module is overridden" do
68
90
  before do
69
91
  factories.use_default_module "MyModule"
@@ -90,6 +112,11 @@ module Rubory
90
112
  person = factories.create(:person)
91
113
  person.should be_saved
92
114
  end
115
+ it "can override params" do
116
+ person = factories.create(:person, :name => "overridden")
117
+ person.should be_saved
118
+ person.name.should == "overridden"
119
+ end
93
120
  end
94
121
 
95
122
  describe "valid attributes" do
@@ -100,12 +127,18 @@ module Rubory
100
127
  end
101
128
  end
102
129
  it "returns the attributes from the definition" do
103
- factories.define :sequel_person do
104
- name "name"
130
+ factories.define :sequel_person do |f|
131
+ f.name "name"
105
132
  end
106
133
  factories.attributes_for(:sequel_person).should == {:name => 'name'}
107
134
  end
108
135
 
136
+ it "returns the attributes from the definition" do
137
+ factories.define :sequel_person do |f|
138
+ f.name "name"
139
+ end
140
+ factories.attributes_for(:sequel_person, :name => 'overridden').should == {:name => 'overridden'}
141
+ end
109
142
  end
110
143
  context "object responds to :attributes" do
111
144
  class ArPerson < Model
@@ -114,8 +147,8 @@ module Rubory
114
147
  end
115
148
  end
116
149
  it "returns the attributes from the definition" do
117
- factories.define :ar_person do
118
- name "name"
150
+ factories.define :ar_person do |f|
151
+ f.name "name"
119
152
  end
120
153
  factories.attributes_for(:ar_person).should == {:name => 'name'}
121
154
  end
@@ -13,6 +13,7 @@ module Qcourses
13
13
  describe "get trainings" do
14
14
 
15
15
  context "when no repository contigured" do
16
+ before(:each) { CourseRepository.configure nil }
16
17
  it "shows error" do
17
18
  get url
18
19
  last_response.should be_ok
@@ -21,8 +22,7 @@ module Qcourses
21
22
  end
22
23
 
23
24
  context "when repository configured" do
24
- let!(:courses) { CourseRepository.in_memory }
25
-
25
+ attr_reader :courses
26
26
  it "shows the list of trainings" do
27
27
  courses.create_course identification: 'training1', name: 'training 1'
28
28
  courses.create_course identification: 'training2', name: 'training 2'
@@ -4,8 +4,8 @@ require 'capybara/rspec'
4
4
  module Qcourses
5
5
 
6
6
  describe EventsController, :type => :request do
7
+ attr_reader :courses
7
8
  let(:app) { Capybara.app = EventsController }
8
- let!(:courses) { CourseRepository.in_memory }
9
9
 
10
10
  shared_examples_for "an_event_lister" do
11
11
  context "when course repository not configured" do
@@ -22,7 +22,7 @@ module Qcourses
22
22
  let!(:course1) { courses.create_course identification: 'agile_engineering', name: 'Agile Engineering' }
23
23
 
24
24
  describe "get '/events'" do
25
- context "when events available" do
25
+ context "when events planned" do
26
26
  let!(:planned) { Event.create(course_id: 'agile_engineering', from: Date.tomorrow, to: Date.tomorrow, :location => "Tilburg") }
27
27
  it "renders events" do
28
28
  get url
@@ -69,12 +69,11 @@ module Qcourses
69
69
 
70
70
  describe 'get admin edit' do
71
71
  let!(:course1) { courses.create_course identification: 'agile_engineering', name: 'Agile Engineering' }
72
- let (:event) { Rubory.create :event }
73
- let (:url) { "/admin/events/edit/#{event.id}" }
72
+ let!(:event) { Rubory.create :event }
74
73
 
75
74
  it "should render a form" do
76
75
  event.update :course_id => course1.identification
77
- get url
76
+ get "/admin/events/edit/#{event.id}"
78
77
  last_response.should be_ok
79
78
  last_body.should have_selector("form#events[@method='post']")
80
79
  last_body.find("form#events")['action'].should match("/admin/events/#{event.id}")
@@ -83,12 +82,18 @@ module Qcourses
83
82
  last_body.should have_selector("form#events input[@name='event[to]'][@value='#{event.to.strftime("%d-%m-%Y")}']")
84
83
  last_body.should have_selector("form#events select[@name='event[course_id]'] option[@value='#{event.course_id}']")
85
84
  end
85
+ it "should renders a form for the correct event" do
86
+ event = Rubory.create :event
87
+ event.update :course_id => course1.identification
88
+ get "/admin/events/edit/#{event.id}"
89
+ last_body.find("form#events")['action'].should match("/admin/events/#{event.id}")
90
+ end
86
91
  end
87
92
 
88
93
  describe 'post admin' do
89
94
  let (:url) { '/admin/events' }
90
95
  let (:last_body) { last_response.body }
91
- let (:event_parameters) { {from: Date.a_month_from_now.as_date_string, to: Date.a_month_from_now.as_date_string, location: 'Tilburg' } }
96
+ let (:event_parameters) { Rubory.attributes_for(:event) }
92
97
 
93
98
  context "on valid parameters" do
94
99
  it "should create an event" do
@@ -116,15 +121,16 @@ module Qcourses
116
121
 
117
122
  describe 'put admin' do
118
123
  let!(:course1) { courses.create_course identification: 'agile_engineering', name: 'Agile Engineering' }
119
- let (:event) { Rubory.create :event }
124
+ let!(:event) { Rubory.create :event }
120
125
  let (:url) { "/admin/events/#{event.id}" }
121
- let (:event_parameters) { {from: Date.a_month_from_now.as_date_string, to: Date.a_month_from_now.as_date_string, location: 'Tilburg' } }
126
+ let (:event_parameters) { Rubory.attributes_for(:event).merge(:max_participants => 1) }
122
127
 
123
128
  context "on valid parameters" do
124
- it "should create an event" do
129
+ it "should update the event" do
125
130
  expect {
126
131
  put url, event: event_parameters
127
- }.to change {Event.count}.from(0).to(1)
132
+ }.not_to change {Event.count}
133
+ event.reload.max_participants.should == 1
128
134
  end
129
135
 
130
136
  it "should redirect back to list" do
@@ -4,9 +4,9 @@ module Qcourses
4
4
 
5
5
  describe RegistrationsController, :type => :request do
6
6
  include Sinatra::Helpers
7
+ attr_reader :courses
7
8
  let(:app) { Capybara.app = RegistrationsController }
8
9
  let(:event) { Rubory.create :event }
9
- let!(:courses) { CourseRepository.in_memory }
10
10
 
11
11
  describe 'get new' do
12
12
  def get_url(id)
@@ -52,6 +52,7 @@ module Qcourses
52
52
  end
53
53
  it "creates a registration" do
54
54
  expect { post post_url(event.id), valid_registration }.to change { Registration.count }.by(1)
55
+ Registration.order(:id).last.event.should == event
55
56
  end
56
57
  it "creates a company" do
57
58
  expect { post post_url(event.id), valid_registration }.to change { Company.count }.by(1)
@@ -59,11 +60,17 @@ module Qcourses
59
60
  it "creates an employee" do
60
61
  expect { post post_url(event.id), valid_registration }.to change { Employee.count }.by(1)
61
62
  end
63
+ it "sends a confirmation to emloyee" do
64
+ Postman.should_receive(:deliver_registration_notification).with(RegistrationNotification.new(Employee.new(valid_participant), event))
65
+ Postman.should_receive(:deliver_registration_confirmation).with(valid_participant[:email], RegistrationNotification.new(Employee.new(valid_participant), event))
66
+ post post_url(event.id), valid_registration
67
+ end
62
68
  context "with more employees" do
63
69
  let!(:another_participant) { Rubory.attributes_for :employee }
64
70
  let(:participants) { [valid_participant, another_participant] }
65
71
  it "creates more registrations" do
66
72
  expect { post post_url(event.id), valid_registration }.to change { Registration.count }.by(2)
73
+ Registration.order(:id).last.event.should == event
67
74
  end
68
75
  it "creates a company" do
69
76
  expect { post post_url(event.id), valid_registration }.to change { Company.count }.by(1)
@@ -39,6 +39,7 @@ module Qcourses
39
39
 
40
40
  describe 'class methods' do
41
41
  it "can't be used when not configured yet" do
42
+ CourseRepository.configure nil
42
43
  expect { CourseRepository.all }.to raise_exception CourseRepository::Error
43
44
  end
44
45
  end
@@ -23,16 +23,32 @@ module Qcourses
23
23
  end
24
24
  end
25
25
 
26
+ describe "querying" do
27
+ context "on id" do
28
+ it "returns the event if it exists" do
29
+ existing = Rubory.create :event
30
+ Event.find(existing.id).should == existing
31
+ end
32
+ end
33
+ end
34
+
26
35
  describe "attributes" do
27
36
 
28
- let(:subject) {Event.new(course_id: 'ae', from: "10-04-2012", to: "12-04-2012") }
37
+ let(:event_attributes) { Rubory.attributes_for :event, course_id: 'ae', location_id: nil }
38
+ let(:subject) { Event.new event_attributes }
29
39
  its(:from) { should be_a(Time) }
30
- its(:from) { should == Time.parse("10-04-2012") }
40
+ its(:from) { should == event_attributes[:from] }
31
41
  its(:to) { should be_a(Time) }
42
+ its(:open?) { should be_true }
43
+ its(:registrations) { should be_empty }
32
44
 
45
+ context "when no more participants allowed" do
46
+ before { subject.max_participants = 0 }
47
+ its(:open?) { should be_false }
48
+ end
33
49
 
34
50
  describe "from course" do
35
- let(:course_repository) { CourseRepository.in_memory }
51
+ let(:course_repository) { @courses }
36
52
  let!(:course) { course_repository.create_course :identification => 'ae', :name => "AE", :subtitle => 'learn ae' }
37
53
  its(:name) { should == course.name }
38
54
  its(:subtitle) { should == course.subtitle }
@@ -44,6 +60,7 @@ module Qcourses
44
60
  end
45
61
  end
46
62
 
63
+
47
64
  describe 'location' do
48
65
  it "is converted to a location object" do
49
66
  subject.location = "Tilburg"
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Qcourses
4
+ describe Postman do
5
+ before { deliveries.clear }
6
+
7
+ let(:employee) { Rubory.build(:employee) }
8
+ let(:event) { Rubory.build(:event) }
9
+
10
+ shared_examples_for "a_problematic_delivery" do
11
+ it "throws an exception if pony throws one" do
12
+ Pony.stub(:mail).and_raise "some exception"
13
+ expect {
14
+ delivery
15
+ }.to raise_exception RuntimeError
16
+ end
17
+ it "swallows exception if set to do so" do
18
+ Postman.allow_delivery_failure
19
+ Pony.stub(:mail).and_raise "some exception"
20
+ expect {
21
+ delivery
22
+ }.to_not raise_exception
23
+ Postman.dont_allow_delivery_failure
24
+ end
25
+ end
26
+
27
+ describe "registration confirmation" do
28
+
29
+ subject do
30
+ Postman.deliver_registration_confirmation("rob@blah.nl", RegistrationNotification.new(employee, event))
31
+ deliveries.last
32
+ end
33
+
34
+ it { should deliver_to("rob@blah.nl") }
35
+ it { should deliver_from("info@qwan.it") }
36
+ it { should have_subject(/Thank you.*registration.*#{event.name}/) }
37
+
38
+ it_should_behave_like "a_problematic_delivery" do
39
+ let(:delivery) { subject }
40
+ end
41
+ end
42
+
43
+ describe "registration notification" do
44
+
45
+ subject do
46
+ Postman.deliver_registration_notification(RegistrationNotification.new(employee, event))
47
+ deliveries.last
48
+ end
49
+
50
+ it { should deliver_to(Postman::NOTIFICATION_ADDRESS) }
51
+ it { should deliver_from(Postman::FROM_ADDRESS) }
52
+ it { should have_subject(/Registration.*#{event.name}/) }
53
+
54
+ it_should_behave_like "a_problematic_delivery" do
55
+ let(:delivery) { subject }
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
62
+
@@ -13,8 +13,14 @@ module Qcourses
13
13
  reloaded.should == subject
14
14
  end
15
15
 
16
+ it "adds the registration to the event" do
17
+ subject.save
18
+ event.registrations.should include(subject)
19
+ end
20
+
16
21
  it { should reject_values(nil).for(:employee) }
17
22
  it { should reject_values(nil).for(:event) }
23
+
18
24
  end
19
25
  end
20
26
 
@@ -101,13 +101,19 @@ module Qcourses
101
101
  html_for(select_for(:employee, :company_id)).should have_selector("select[name='employee[company_id]']")
102
102
  html_for(select_for(@employee, :company_id)).should have_selector("select[name='employee[company_id]']")
103
103
  end
104
- it 'creates a options' do
104
+ it 'creates options' do
105
105
  options = [["99", "Other Company"]]
106
106
  options << [@employee.company_id.to_s, "Company"]
107
107
  html_for(select_for(:employee, :company_id, options)).should have_selector("select[name='employee[company_id]'] option[value='99']", :text => "Other Company")
108
108
  html_for(select_for(:employee, :company_id, options)).should have_selector("select[name='employee[company_id]'] option[value='#{@employee.company_id}'][selected='selected']", :text => "Company")
109
109
  end
110
- it 'can have a class and or id ' do
110
+ it 'accepts integers for option values' do
111
+ options = [[99, "Other Company"]]
112
+ options << [@employee.company_id, "Company"]
113
+ html_for(select_for(:employee, :company_id, options)).should have_selector("select[name='employee[company_id]'] option[value='99']", :text => "Other Company")
114
+ html_for(select_for(:employee, :company_id, options)).should have_selector("select[name='employee[company_id]'] option[value='#{@employee.company_id}'][selected='selected']", :text => "Company")
115
+ end
116
+ it 'can have a class and or id' do
111
117
  html_for(select_for(:employee, :company_id, nil, :class => 'short')).should have_selector("select[name='employee[company_id]'][class='short']")
112
118
  html_for(select_for(:employee, :company_id, nil, :id => 'my_id')).should have_selector("select#my_id")
113
119
  end
@@ -119,7 +125,7 @@ module Qcourses
119
125
  end
120
126
  end
121
127
 
122
- describe "event_registration_link", :focus => true do
128
+ describe "event_registration_link" do
123
129
  def html_for(result)
124
130
  Capybara.string(result)
125
131
  end
@@ -128,11 +134,11 @@ module Qcourses
128
134
  end
129
135
  let(:event) { Rubory.build :event }
130
136
  it "contains a registration link" do
131
- p event
132
- html_for(event_registration_link(event)).should have_selector("a[href='/registrations/new/#{event.id}']")
137
+ result = html_for(event_registration_link(event))
138
+ result.should have_selector("a[href='/registrations/new/#{event.id}']")
133
139
  end
134
140
  it "contains no registration link if event is closed" do
135
- event.open = false
141
+ event.stub :open? => false
136
142
  html_for(event_registration_link(event)).should_not have_selector("a[href='/registrations/new/#{event.id}']")
137
143
  html_for(event_registration_link(event)).should have_content("closed")
138
144
  end
data/spec/spec_helper.rb CHANGED
@@ -8,6 +8,8 @@ require 'rspec'
8
8
  require 'qcourses'
9
9
  require 'rack/test'
10
10
  require 'capybara'
11
+ require 'pony'
12
+ require 'email_spec'
11
13
 
12
14
  Qcourses.configure do |config|
13
15
  config.root = File.expand_path('..',File.dirname(__FILE__))
@@ -24,6 +26,7 @@ class RSpec::Core::ExampleGroup
24
26
  super
25
27
  subclass.around do |example|
26
28
  Qcourses::CourseRepository.destroy
29
+
27
30
  Sequel::Model.db.transaction(:rollback=>:always){example.call}
28
31
  end
29
32
  end
@@ -32,7 +35,10 @@ end
32
35
  RSpec.configure do |config|
33
36
  config.include Rack::Test::Methods
34
37
  config.include TestFiles
38
+ config.include EmailSpec::Matchers
39
+ config.include EmailSpec::Helpers
35
40
  config.filter_run_excluding :broken => true
36
41
  config.filter_run :focus => true
37
42
  config.run_all_when_everything_filtered = true
43
+ config.before(:each) { @courses = Qcourses::CourseRepository.in_memory }
38
44
  end
@@ -2,23 +2,25 @@ require 'factories'
2
2
  require 'qcourses/date_ex'
3
3
  Rubory.use_default_module('Qcourses')
4
4
 
5
- Rubory.define(:company) do
6
- name { |n| "company_#{n}" }
7
- contact_person "some contact"
8
- contact_email { |n| "contact_#{n}@email.com" }
9
- invoice_address "some address"
10
- postal_code "1234 AA"
11
- city "Amsterdam"
5
+ Rubory.define(:company) do |f|
6
+ f.name { |n| "company_#{n}" }
7
+ f.contact_person "some contact"
8
+ f.contact_email { |n| "contact_#{n}@email.com" }
9
+ f.invoice_address "some address"
10
+ f.postal_code "1234 AA"
11
+ f.city "Amsterdam"
12
12
  end
13
13
 
14
- Rubory.define(:employee) do
15
- name "some employee"
16
- email { |n| "email_#{n}@qwan.it" }
17
- associate :company
14
+ Rubory.define(:employee) do |f|
15
+ f.name "some employee"
16
+ f.email { |n| "email_#{n}@qwan.it" }
17
+ f.associate :company
18
18
  end
19
19
 
20
- Rubory.define(:event) do
21
- from { Date.tomorrow }
22
- to { Date.tomorrow.tomorrow }
23
- location "Tilburg"
20
+ Rubory.define(:event) do |f|
21
+ f.from { Date.tomorrow }
22
+ f.to { Date.tomorrow.tomorrow }
23
+ f.open true
24
+ f.max_participants 14
25
+ f.location "Tilburg"
24
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qcourses
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-25 00:00:00.000000000 Z
12
+ date: 2012-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.2.6
21
+ version: '1.3'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.2.6
29
+ version: '1.3'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: haml
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
77
  version: 1.3.5
78
+ - !ruby/object:Gem::Dependency
79
+ name: pony
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.4'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.4'
78
94
  - !ruby/object:Gem::Dependency
79
95
  name: rspec
80
96
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +155,38 @@ dependencies:
139
155
  - - ! '>='
140
156
  - !ruby/object:Gem::Version
141
157
  version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: shotgun
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: email_spec
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
142
190
  description: Small course event moduel for qinatra site - added as middleware
143
191
  email:
144
192
  - rob@gqwan.it
@@ -179,6 +227,7 @@ files:
179
227
  - lib/qcourses/models/course_repository.rb
180
228
  - lib/qcourses/models/event.rb
181
229
  - lib/qcourses/models/location.rb
230
+ - lib/qcourses/models/postman.rb
182
231
  - lib/qcourses/models/registration.rb
183
232
  - lib/qcourses/qcourses.rake.rb
184
233
  - lib/qcourses/renderers.rb
@@ -201,6 +250,7 @@ files:
201
250
  - spec/qcourses/models/course_repository_spec.rb
202
251
  - spec/qcourses/models/event_spec.rb
203
252
  - spec/qcourses/models/location_spec.rb
253
+ - spec/qcourses/models/postman_spec.rb
204
254
  - spec/qcourses/models/registration_spec.rb
205
255
  - spec/qcourses/renderers_spec.rb
206
256
  - spec/qcourses/string_ex_spec.rb