qcourses 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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