ixtlan 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/History.txt +49 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest.txt +73 -48
  4. data/Rakefile +1 -1
  5. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/gwt_ixtlan_datamapper_rspec_scaffold_generator.rb +3 -3
  6. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/AbstractApplicationResourceTestGwt.java +1 -1
  7. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Fields.java +1 -1
  8. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Model.java +2 -2
  9. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/ModelFactory.java +2 -2
  10. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Screen.java +2 -2
  11. data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/TestGwt.java +2 -2
  12. data/generators/ixtlan_datamapper_rspec_model/ixtlan_datamapper_rspec_model_generator.rb +1 -1
  13. data/generators/ixtlan_datamapper_rspec_scaffold/ixtlan_datamapper_rspec_scaffold_generator.rb +4 -4
  14. data/generators/ixtlan_datamapper_rspec_scaffold/templates/guard.rb +7 -7
  15. data/generators/ixtlan_datamapper_rspec_scaffold/templates/i18n.rb +1 -1
  16. data/generators/ixtlan_datamapper_rspec_scaffold/templates/layout.html.erb +20 -0
  17. data/ixtlan_rails_templates.rb +537 -0
  18. data/lib/ixtlan/audit_config.rb +5 -5
  19. data/lib/ixtlan/child_path.rb +2 -2
  20. data/lib/ixtlan/cms_script.rb +4 -4
  21. data/lib/ixtlan/controllers/authentications_controller.rb +7 -2
  22. data/lib/ixtlan/controllers/configurations_controller.rb +15 -6
  23. data/lib/ixtlan/controllers/domains_controller.rb +99 -0
  24. data/lib/ixtlan/controllers/groups_controller.rb +105 -0
  25. data/lib/ixtlan/controllers/locales_controller.rb +99 -0
  26. data/lib/ixtlan/controllers/permissions_controller.rb +5 -0
  27. data/lib/ixtlan/controllers/phrases_controller.rb +26 -22
  28. data/lib/ixtlan/controllers/search_query.rb +24 -0
  29. data/lib/ixtlan/controllers/texts_controller.rb +5 -5
  30. data/lib/ixtlan/controllers/users_controller.rb +117 -0
  31. data/lib/ixtlan/controllers/word_bundles_controller.rb +13 -8
  32. data/lib/ixtlan/digest.rb +3 -3
  33. data/lib/ixtlan/guard.rb +11 -12
  34. data/lib/ixtlan/logger_config.rb +11 -11
  35. data/lib/ixtlan/mailer/error_notification.erb +1 -0
  36. data/lib/ixtlan/mailer/password.erb +1 -0
  37. data/lib/ixtlan/mailer.rb +27 -0
  38. data/lib/ixtlan/models/authentication.rb +9 -6
  39. data/lib/ixtlan/models/configuration.rb +21 -37
  40. data/lib/ixtlan/models/configuration_locale.rb +3 -3
  41. data/lib/ixtlan/models/domain.rb +44 -0
  42. data/lib/ixtlan/models/domain_group_user.rb +22 -0
  43. data/lib/ixtlan/models/group.rb +82 -16
  44. data/lib/ixtlan/models/group_locale_user.rb +4 -4
  45. data/lib/ixtlan/models/group_user.rb +7 -7
  46. data/lib/ixtlan/models/i18n_text.rb +26 -26
  47. data/lib/ixtlan/models/locale.rb +17 -5
  48. data/lib/ixtlan/models/permission.rb +3 -2
  49. data/lib/ixtlan/models/phrase.rb +15 -15
  50. data/lib/ixtlan/models/role.rb +5 -5
  51. data/lib/ixtlan/models/translation.rb +9 -9
  52. data/lib/ixtlan/models/update_children.rb +74 -0
  53. data/lib/ixtlan/models/user.rb +108 -16
  54. data/lib/ixtlan/models/word.rb +2 -1
  55. data/lib/ixtlan/models.rb +1 -0
  56. data/lib/ixtlan/modified_by.rb +9 -7
  57. data/lib/ixtlan/monkey_patches.rb +5 -5
  58. data/lib/ixtlan/optimistic_persistence.rb +2 -2
  59. data/lib/ixtlan/optimistic_persistence_module.rb +3 -3
  60. data/lib/ixtlan/optimistic_persistence_validation.rb +2 -2
  61. data/lib/ixtlan/passwords.rb +15 -13
  62. data/lib/ixtlan/rails/error_handling.rb +41 -40
  63. data/lib/ixtlan/rails/guard.rb +0 -1
  64. data/lib/ixtlan/rails/migrations.rb +75 -0
  65. data/lib/ixtlan/rails/session_timeout.rb +16 -14
  66. data/lib/ixtlan/rails/timestamps_modified_by_filter.rb +1 -1
  67. data/lib/ixtlan/rails/unrestful_authentication.rb +4 -4
  68. data/lib/ixtlan/rolling_file.rb +3 -3
  69. data/lib/ixtlan/session.rb +4 -3
  70. data/lib/ixtlan/user_logger.rb +13 -9
  71. data/lib/ixtlan/version.rb +1 -1
  72. data/spec/authentication_spec.rb +1 -1
  73. data/spec/guard_spec.rb +4 -4
  74. data/spec/guards/samples.rb +7 -7
  75. data/spec/modified_by_spec.rb +82 -0
  76. data/spec/optimistic_persistence_spec.rb +58 -0
  77. data/spec/phrase_spec.rb +119 -0
  78. data/spec/session_timeout_spec.rb +59 -0
  79. data/spec/spec_helper.rb +6 -5
  80. data/spec/text_collection_spec.rb +88 -0
  81. data/spec/text_spec.rb +105 -0
  82. data/spec/unrestful_authentication_spec.rb +142 -0
  83. data/spec/user_logger_spec.rb +105 -0
  84. data/spec/user_spec.rb +249 -0
  85. data/whitespace.rb +31 -0
  86. metadata +76 -50
  87. data/lib/ixtlan/error_notifier/error_notification.rhtml +0 -1
@@ -9,7 +9,7 @@ module Ixtlan
9
9
  end
10
10
 
11
11
  def internal_server_error(exception)
12
- dump_error(exception, Object.full_const_get(::Ixtlan::Models.CONFIGURATION).instance)
12
+ dump_error(exception)
13
13
  status = :internal_server_error
14
14
  error_page(:internal_server_error, exception) do |exception|
15
15
  "internal server error: #{exception.class.name}"
@@ -17,19 +17,27 @@ module Ixtlan
17
17
  end
18
18
 
19
19
  def error_page(status, exception, &block)
20
- respond_to do |format|
21
- format.html {
20
+ respond_to do |format|
21
+ format.html {
22
22
  @notice = block.call(exception)
23
23
  if logged_in?
24
- render :template => "errors/error", :status => status
24
+ render_error_page_with_session(status)
25
25
  else
26
- render :template => "sessions/login", :status => status
26
+ render_error_page(status)
27
27
  end
28
28
  }
29
29
  format.xml { head status }
30
30
  end
31
31
  end
32
32
 
33
+ def render_error_page_with_session(status)
34
+ render :template => "errors/error", :status => status
35
+ end
36
+
37
+ def render_error_page(status)
38
+ render :template => "sessions/login", :status => status
39
+ end
40
+
33
41
  def page_not_found(exception)
34
42
  log_user_error(exception)
35
43
  status = rescue_responses[exception.class.name]
@@ -39,44 +47,37 @@ module Ixtlan
39
47
 
40
48
  def stale_resource(exception)
41
49
  log_user_error(exception)
42
- respond_to do |format|
43
- format.html {
50
+ respond_to do |format|
51
+ format.html {
44
52
  render :template => "errors/stale", :status => :conflict
45
53
  }
46
54
  format.xml { head :conflict }
47
55
  end
48
56
  end
49
57
 
50
- def dump_error(exception, config)
58
+ def dump_error(exception)
51
59
  log_user_error(exception)
52
- dumper = DumpError.new(config.notification_sender_email,
53
- config.notification_recipient_email)
60
+ begin
61
+ config = Object.full_const_get(::Ixtlan::Models::CONFIGURATION).instance
62
+ dumper = DumpError.new(config.notification_sender_email,
63
+ config.notification_recipient_emails,
64
+ config.errors_dump_directory)
65
+ rescue => e
66
+ # maybe that gives a double entry in the log file
67
+ log_user_error(e)
68
+ # at least dump the file !!
69
+ dumper = DumpError.new
70
+ end
54
71
  dumper.dump(self, exception)
55
72
  end
56
73
  end
57
-
58
- class ErrorNotifier < ActionMailer::Base
59
- require 'pathname'
60
-
61
- def error_notification(email_from, email_to, exception, error_file)
62
- @subject = exception.to_s
63
- @body = {:text => "#{error_file}"}
64
- @recipients = email_to
65
- @from = email_from
66
- @sent_on = Time.now
67
- @headers = {}
68
- path = Pathname(__FILE__).parent.dirname.to_s
69
- view_paths << path unless view_paths.member? path
70
- @template = "error_notification.rhtml"
71
- end
72
74
 
73
- end
74
-
75
75
  class DumpError
76
-
77
- def initialize(email_from, email_to, errors_dir = "#{RAILS_ROOT}/log/errors")
78
- FileUtils.mkdir_p(errors_dir)
79
- @errors_dir = File.new(errors_dir)
76
+
77
+ def initialize(email_from = nil, email_to = nil, errors_dir = nil)
78
+ errors_dir ||= "#{RAILS_ROOT}/log/errors"
79
+ FileUtils.mkdir_p(errors_dir || "#{RAILS_ROOT}/log/errors")
80
+ @errors_dir = Dir.new(errors_dir)
80
81
  @email_from = email_from
81
82
  @email_to = email_to
82
83
  end
@@ -84,35 +85,35 @@ module Ixtlan
84
85
  def dump(controller, exception)
85
86
  time = Time.now
86
87
  error_log_id = "#{time.tv_sec}#{time.tv_usec}"
87
-
88
+
88
89
  log_file = File.join(@errors_dir.path.to_s, "error-#{error_log_id}.log")
89
90
  logger = Logger.new(log_file)
90
-
91
+
91
92
  dump_environment(logger, exception, controller)
92
- ErrorNotifier.deliver_error_notification(@email_from, @email_to, exception, log_file) unless @email_to.blank?
93
+ Ixtlan::Mailer.deliver_error_notification(@email_from, @email_to, exception, log_file) unless (@email_to.blank? || @email_from.blank?)
93
94
  log_file
94
- end
95
+ end
95
96
 
96
97
  private
97
98
 
98
99
  def dump_environment_header(logger, header)
99
100
  logger.error("\n===================================================================\n#{header}\n===================================================================\n");
100
101
  end
101
-
102
+
102
103
  def dump_environment(logger, exception, controller)
103
104
  dump_environment_header(logger, "REQUEST DUMP");
104
105
  dump_hashmap(logger, controller.request.env)
105
-
106
+
106
107
  dump_environment_header(logger, "RESPONSE DUMP");
107
108
  dump_hashmap(logger, controller.response.headers)
108
-
109
+
109
110
  dump_environment_header(logger, "SESSION DUMP");
110
111
  dump_hashmap(logger, controller.session)
111
-
112
+
112
113
  dump_environment_header(logger, "PARAMETER DUMP");
113
114
  map = {}
114
115
  dump_hashmap(logger, controller.params.each{ |k,v| map[k]=v })
115
-
116
+
116
117
  dump_environment_header(logger, "EXCEPTION");
117
118
  logger.error("#{exception.class}:#{exception.message}")
118
119
  logger.error("\t" + exception.backtrace.join("\n\t"))
@@ -8,4 +8,3 @@ module Erector
8
8
  include Ixtlan::Allowed
9
9
  end
10
10
  end
11
-
@@ -0,0 +1,75 @@
1
+ module Ixtlan
2
+ module Rails
3
+ class Migrations
4
+
5
+ # assume no namespaces !!!
6
+ USER = Object.const_get(Ixtlan::Models::USER)
7
+ GROUP = Object.const_get(Ixtlan::Models::GROUP)
8
+ LOCALE = Object.const_get(Ixtlan::Models::LOCALE)
9
+ DOMAIN = Object.const_get(Ixtlan::Models::DOMAIN)
10
+ CONFIGURATION = Object.const_get(Ixtlan::Models::CONFIGURATION)
11
+
12
+ def self.create_user
13
+ USER.auto_migrate!
14
+ LOCALE.auto_migrate!
15
+ GROUP.auto_migrate!
16
+ Ixtlan::Models::GroupUser.auto_migrate!
17
+ Ixtlan::Models::GroupLocaleUser.auto_migrate!
18
+ Ixtlan::Models::DomainGroupUser.auto_migrate!
19
+
20
+ u = USER.new(:login => 'root', :email => 'root@example.com', :name => 'Superuser', :id => 1)
21
+ u.created_at = DateTime.now
22
+ u.updated_at = u.created_at
23
+ u.created_by_id = 1
24
+ u.updated_by_id = 1
25
+ u.reset_password
26
+ u.save!
27
+
28
+ g = GROUP.create(:name => 'root', :current_user => u)
29
+ u.groups << g
30
+ u.save
31
+
32
+ a = USER.create(:login => 'admin', :email => 'admin@example.com', :name => 'Administrator', :id => 2, :current_user => u)
33
+ a.reset_password
34
+ a.save!
35
+ a.groups << GROUP.create(:name => 'admin', :current_user => u)
36
+ a.save
37
+
38
+ users = GROUP.create(:name => 'users', :current_user => u)
39
+ locales = GROUP.create(:name => 'locales', :current_user => u)
40
+ domains = GROUP.create(:name => 'domains', :current_user => u)
41
+
42
+ File.open("root", 'w') { |f| f.puts "root\n#{u.password}\n\nadmin\n#{a.password}\n\n" }
43
+ end
44
+
45
+ def self.create_configuration
46
+ Ixtlan::Models::ConfigurationLocale.auto_migrate!
47
+ CONFIGURATION.auto_migrate!
48
+ CONFIGURATION.create(:session_idle_timeout => 10, :keep_audit_logs => 3, :current_user => USER.first)
49
+ end
50
+
51
+ def self.create_locale
52
+ LOCALE.auto_migrate!
53
+ # get/create default locale
54
+ LOCALE.create(:code => LOCALE::DEFAULT, :current_user => USER.first)
55
+ # get/create "every" locale
56
+ LOCALE.create(:code => LOCALE::ALL, :current_user => USER.first)
57
+
58
+ # root user has access to ALL locales
59
+ Ixtlan::Models::GroupLocaleUser.create(:group => Group.first, :user => USER.first, :locale => LOCALE.every)
60
+ end
61
+
62
+ def self.create_domain
63
+ DOMAIN.auto_migrate!
64
+ # get/create "every" domain
65
+ DOMAIN.create(:name => DOMAIN::ALL, :current_user => User.first)
66
+
67
+ Ixtlan::Models::DomainGroupUser.create(:group => Group.first, :user => USER.first, :domain => DOMAIN.every)
68
+ end
69
+
70
+ def self.create_text
71
+ I18nText.auto_migrate!
72
+ end
73
+ end
74
+ end
75
+ end
@@ -5,7 +5,7 @@ module Ixtlan
5
5
  def self.included(base)
6
6
  base.send(:include, InstanceMethods)
7
7
  end
8
-
8
+
9
9
  module InstanceMethods
10
10
  private
11
11
 
@@ -14,16 +14,16 @@ module Ixtlan
14
14
  def session_user_logger
15
15
  @session_user_logger ||= UserLogger.new(Ixtlan::Rails::SessionTimeout)
16
16
  end
17
-
17
+
18
18
  def expire_session
19
19
  session.clear
20
20
  # reset_session
21
21
  render_session_timeout
22
22
  return false
23
23
  end
24
-
24
+
25
25
  public
26
-
26
+
27
27
  def check_session_expiry
28
28
  if !session[:expires_at].nil? and session[:expires_at] < DateTime.now
29
29
  # Session has expired.
@@ -35,23 +35,25 @@ module Ixtlan
35
35
  return true
36
36
  end
37
37
  end
38
-
38
+
39
+ # IP binding is not very useful in the wild since some ISP use
40
+ # a different IP for each request, i.e. the session uses many IPs
39
41
  def check_session_ip_binding
40
42
  if !session[:session_ip].nil? and session[:session_ip] != request.headers['REMOTE_ADDR']
41
43
  # client IP has changed
42
44
  session_user_logger.log(self, "IP changed from #{session[:session_ip]} to #{request.headers['REMOTE_ADDR']}")
43
45
  expire_session
44
46
  else
45
- # Assign client IP
47
+ # Assign client IP
46
48
  session[:session_ip] = request.headers['REMOTE_ADDR']
47
49
  return true
48
50
  end
49
51
  end
50
-
52
+
51
53
  def check_session
52
- check_session_ip_binding and check_session_expiry
54
+ check_session_browser_signature && check_session_expiry
53
55
  end
54
-
56
+
55
57
  def check_session_browser_signature
56
58
  if !session[:session_browser_signature].nil? and session[:session_browser_signature] != retrieve_browser_signature
57
59
  # browser signature has changed
@@ -64,24 +66,24 @@ module Ixtlan
64
66
  return true
65
67
  end
66
68
  end
67
-
69
+
68
70
  def retrieve_browser_signature
69
71
  [request.headers['HTTP_USER_AGENT'],
70
72
  request.headers['HTTP_ACCEPT_LANGUAGE'],
71
73
  request.headers['HTTP_ACCEPT_ENCODING'],
72
74
  request.headers['HTTP_ACCEPT']].join('|')
73
75
  end
74
-
76
+
75
77
  def render_session_timeout
76
- respond_to do |format|
77
- format.html {
78
+ respond_to do |format|
79
+ format.html {
78
80
  @notice = "session timeout" unless @notice
79
81
  render :template => "sessions/login"
80
82
  }
81
83
  format.xml { head :unauthorized }
82
84
  end
83
85
  end
84
-
86
+
85
87
  def session_timeout
86
88
  CONFIG.instance.session_idle_timeout
87
89
  end
@@ -6,7 +6,7 @@ module Ixtlan
6
6
  base.prepend_around_filter(Filter)
7
7
  end
8
8
  end
9
-
9
+
10
10
  class Filter
11
11
  def self.filter(controller)
12
12
  name = controller.params[:controller]
@@ -21,7 +21,7 @@ module Rails
21
21
  def current_user
22
22
  session[:user] = login_from_session if logged_in? and session[:user].nil?
23
23
  session[:user]
24
- end
24
+ end
25
25
 
26
26
  def current_user=(new_user)
27
27
  session[:user_id] = new_user ? new_user.id : nil
@@ -45,13 +45,13 @@ module Rails
45
45
  end
46
46
  else
47
47
  case(request.method)
48
- when :get
48
+ when :get
49
49
  session.clear
50
50
  render_login_page
51
51
  when :post
52
52
  user = login_from_params
53
53
  if user.instance_of? String
54
- authentication_logger.log_user(params[:login], user + " from IP #{request.headers['REMOTE_ADDR']}")
54
+ authentication_logger.log_user(params[:login] || params[:authentication][:login], user + " from IP #{request.headers['REMOTE_ADDR']}")
55
55
  session.clear
56
56
  render_access_denied
57
57
  else
@@ -128,7 +128,7 @@ module Rails
128
128
  end
129
129
  format.xml { head :ok }
130
130
  end
131
- end
131
+ end
132
132
  end
133
133
  end
134
134
  end
@@ -1,9 +1,9 @@
1
1
  require 'logging'
2
2
 
3
3
  module Ixtlan
4
-
4
+
5
5
  class RollingFile < ::Logging::Appenders::RollingFile
6
-
6
+
7
7
  def current_logfile
8
8
  "#{@filename_base}_#{Time.now.strftime(@date_pattern)}.#{@extension}"
9
9
  end
@@ -31,7 +31,7 @@ module Ixtlan
31
31
  ::File.delete file
32
32
  end
33
33
  end
34
- end
34
+ end
35
35
 
36
36
  def write( event )
37
37
  str = event.instance_of?(::Logging::LogEvent) ?
@@ -1,7 +1,7 @@
1
1
  require 'rack_datamapper/session/abstract/store'
2
2
  module Ixtlan
3
3
  class Session < DataMapper::Session::Abstract::Session
4
-
4
+
5
5
  def data=(data)
6
6
  d = {}
7
7
  data.each{|k,v| d[k.to_sym] = v}
@@ -10,9 +10,10 @@ module Ixtlan
10
10
  @expires_at = d.delete(:expires_at)
11
11
  attribute_set(:data, ::Base64.encode64(Marshal.dump(d)))
12
12
  end
13
-
13
+
14
14
  def data
15
- Marshal.load(::Base64.decode64(attribute_get(:data))).merge({:user => @user, :flash => @flash, :expires_at => @expires_at})
15
+ # user string for flash entry to allow the rails falsh to work properly !
16
+ Marshal.load(::Base64.decode64(attribute_get(:data))).merge({:user => @user, "flash" => @flash, :expires_at => @expires_at})
16
17
  end
17
18
  end
18
19
  end
@@ -1,20 +1,20 @@
1
1
  require 'slf4r'
2
2
  module Ixtlan
3
-
3
+
4
4
  class UserLogger
5
-
5
+
6
6
  def initialize(arg)
7
7
  @logger = Slf4r::LoggerFacade.new(arg)
8
8
  end
9
9
 
10
- private
10
+ private
11
11
 
12
12
  def login_from(controller)
13
13
  user = controller.respond_to?(:current_user) ? controller.send(:current_user) : nil
14
14
  user.nil? ? nil: user.login
15
15
  end
16
16
 
17
- public
17
+ public
18
18
 
19
19
  def log(controller, message = nil, &block)
20
20
  log_user(login_from(controller), message, &block)
@@ -30,20 +30,24 @@ module Ixtlan
30
30
  else
31
31
  audit = controller.instance_variable_get("@#{controller.params[:controller].singular.to_sym}")
32
32
  if(audit)
33
- "#{controller.params[:controller]}##{controller.params[:action]} #{audit.model}(#{audit.key})#{as_xml}#{message}"
33
+ errors = if(audit.respond_to?(:errors) && !audit.errors.empty?)
34
+ " - errors: " + audit.errors.full_messages.join(", ")
35
+ end
36
+ audit_log = audit.respond_to?(:to_log) ? audit.to_log : "#{audit.model}(#{audit.key})"
37
+ "#{controller.params[:controller]}##{controller.params[:action]} #{audit_log}#{as_xml}#{message}#{errors}"
34
38
  else
35
39
  "#{controller.params[:controller]}##{controller.params[:action]}#{as_xml}#{message}"
36
40
  end
37
41
  end
38
42
  else
39
- "params=#{controller.params.inspect}"
43
+ "params=#{controller.params.inspect}#{message}"
40
44
  end
41
45
  end
42
46
  end
43
-
47
+
44
48
  def log_user(user, message = nil, &block)
45
- user = "???" unless user
46
- @logger.info {"[#{user}] #{message}#{block.call if block}" }
49
+ user ||= "???"
50
+ @logger.info {"[#{user}] #{message}#{block.call if block}" }
47
51
  end
48
52
  end
49
53
  end
@@ -1,3 +1,3 @@
1
1
  class Ixtlan
2
- VERSION = '0.2.4'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -24,7 +24,7 @@ describe Ixtlan::Models::Authentication do
24
24
 
25
25
  it "should" do
26
26
  xml = @authentication.to_xml
27
- xml.gsub!(/[0-9-]{10}T[0-9+-:]{14}/, "").gsub!(/ type='[^']+'/, '').gsub!(/<created_at><\/created_at>/, "<created_at/>").should == "<authentication><login>marvin2</login><user><id>1356</id><login>marvin2</login><name>marvin the robot</name><email>marvin@universe.example.com</email><language>xx</language><created_at/><updated_at></updated_at><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><groups><group><id>1356</id><name>marvin2_root</name><created_at/><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><locales><locale><code>DEFAULT</code><created_at/></locale><locale><code>en</code><created_at/></locale></locales></group></groups></user></authentication>"
27
+ xml.gsub!(/[0-9-]{10}T[0-9+-:]{14}/, "").gsub!(/ type='[^']+'/, '').gsub!(/<created_at><\/created_at>/, "<created_at/>").gsub!(/<locale><id>[0-9]*<\/id>/, "<locale>").should == "<authentication><id>1</id><login>marvin2</login><user><id>1356</id><login>marvin2</login><name>marvin the robot</name><email>marvin@universe.example.com</email><language>xx</language><created_at/><updated_at></updated_at><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><groups><group><id>1356</id><name>marvin2_root</name><created_at/><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><locales><locale><code>DEFAULT</code><created_at/></locale><locale><code>en</code><created_at/></locale></locales></group></groups></user></authentication>"
28
28
  end
29
29
 
30
30
  end
data/spec/guard_spec.rb CHANGED
@@ -7,13 +7,13 @@ module ActionView
7
7
  end
8
8
  module Erector
9
9
  class Widget
10
-
10
+
11
11
  attr_writer :controller
12
-
12
+
13
13
  class Helpers
14
14
  attr_accessor :controller
15
15
  end
16
-
16
+
17
17
  def helpers
18
18
  @helpers ||= Helpers.new
19
19
  @helpers.controller = @controller
@@ -38,7 +38,7 @@ describe Ixtlan::Models::Permission do
38
38
 
39
39
  end
40
40
 
41
- class Controller
41
+ class Controller
42
42
  include ActionController::Base
43
43
  end
44
44
 
@@ -1,12 +1,12 @@
1
- Ixtlan::Guard.initialize(:permissions,
2
- { :show => [:guest],
3
- :edit => [:root, :admin],
4
- :update => [:root, :admin],
1
+ Ixtlan::Guard.initialize(:permissions,
2
+ { :show => [:guest],
3
+ :edit => [:root, :admin],
4
+ :update => [:root, :admin],
5
5
  :destroy => [:root] }
6
6
  )
7
7
 
8
- Ixtlan::Guard.initialize(:configurations,
9
- { :show => [:admin],
10
- :edit => [:root],
8
+ Ixtlan::Guard.initialize(:configurations,
9
+ { :show => [:admin],
10
+ :edit => [:root],
11
11
  :update => [:root] }
12
12
  )
@@ -0,0 +1,82 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname + 'spec_helper.rb'
3
+ require 'ixtlan' / 'modified_by'
4
+
5
+ class User
6
+ include DataMapper::Resource
7
+
8
+ property :login, String, :key => true
9
+ end
10
+
11
+ class AuditedName
12
+ include DataMapper::Resource
13
+
14
+ # TODO remove serial
15
+ property :id, Serial
16
+ property :name, String, :length => 2..255#, :key => true
17
+
18
+ modified_by User
19
+ end
20
+
21
+ describe Ixtlan::ModifiedBy do
22
+
23
+ before :each do
24
+ @user = User.create(:login => 'spock')
25
+ @second = User.create(:login => 'dr pill')
26
+ @name = AuditedName.create(:name => 'kirk', :current_user => @user)
27
+ end
28
+
29
+ it 'should create resource with modified by properties' do
30
+ @name.new?.should be_false
31
+ @name.instance_variable_get(:@current_user).should be_nil
32
+ @name.created_by.should == @user
33
+ @name.updated_by.should == @user
34
+ end
35
+
36
+ it 'should new/save resource with modified by properties' do
37
+ name = AuditedName.new(:name => 'seven of nine')
38
+ name.current_user = @user
39
+ name.save.should be_true
40
+ name.created_by.should == @user
41
+ name.updated_by.should == @user
42
+ end
43
+
44
+ it 'should modify updated_by on change' do
45
+ @name.current_user = @second
46
+ @name.name = "scotty"
47
+ @name.save.should be_true
48
+ @name.created_by.should == @user
49
+ @name.updated_by.should == @second
50
+ end
51
+
52
+ it 'should modify updated_by on update' do
53
+ @name.update(:name => "captain kirk", :current_user => @second)
54
+ @name.instance_variable_get(:@current_user).should be_nil
55
+ @name.updated_by.should == @second
56
+ end
57
+
58
+ it 'should not modify updated_by on update without change' do
59
+ @name.update(:name => "kirk", :current_user => @second)
60
+ @name.updated_by.should == @user
61
+ @name.instance_variable_get(:@current_user).should be_nil
62
+ end
63
+
64
+ it 'should modify updated_by on update with current_user' do
65
+ @name.current_user = @second
66
+ @name.update(:name => "captain kirk")
67
+ @name.updated_by.should == @second
68
+ end
69
+
70
+ it 'should fail create without current_user' do
71
+ lambda { AuditedName.create(:name => "ohura") }.should raise_error(DataMapper::MissingCurrentUserError)
72
+ end
73
+
74
+ it 'should fail save without current_user' do
75
+ @name.name = "ohura"
76
+ lambda { @name.save }.should raise_error(DataMapper::MissingCurrentUserError)
77
+ end
78
+
79
+ it 'should fail update without current_user' do
80
+ lambda { @name.update(:name => "ohura") }.should raise_error(DataMapper::MissingCurrentUserError)
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname + 'spec_helper.rb'
3
+
4
+ require 'ixtlan/optimistic_persistence'
5
+
6
+ class Name
7
+ include DataMapper::Resource
8
+
9
+ property :id, Serial
10
+ property :name, String, :length => 2..255
11
+
12
+ timestamps :updated_at
13
+ end
14
+
15
+ class Number
16
+ include DataMapper::Resource
17
+
18
+ property :id, Serial
19
+ property :number, Integer
20
+ end
21
+
22
+ describe "Ixtlan::OptimisticPersistence" do
23
+
24
+ before :each do
25
+ @name = Name.create(:name => "frodo")
26
+ @second = Name.get(@name.id)
27
+ @number = Number.first_or_create(:number => 123)
28
+ @other = Number.first
29
+ end
30
+
31
+ it 'should save' do
32
+ @name.name = "gandalf"
33
+ @name.save.should be_true
34
+ end
35
+
36
+ it 'should fail' do
37
+ @name.name = "gandalf"
38
+ sleep 1
39
+ @name.save.should be_true
40
+ @second.name = "saroman"
41
+ lambda { @second.save }.should raise_error(DataMapper::StaleResourceError)
42
+ end
43
+
44
+ it 'should fail on key change' do
45
+ pending "maybe bug in datamapper"
46
+ @name.id = 11
47
+ @name.save.should be_true
48
+ @second.id = 111
49
+ lambda { @second.save }.should raise_error(DataMapper::StaleResourceError)
50
+ end
51
+
52
+ it 'should treat non optimistic resources as usual' do
53
+ @number.number = 345
54
+ @number.save.should be_true
55
+ @other.number = 987
56
+ @other.save.should be_true
57
+ end
58
+ end