eaco 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/features/authorization_parse_error.feature +157 -0
  4. data/features/enterprise_authorization.feature +159 -0
  5. data/features/rails_integration.feature +1 -1
  6. data/features/role_based_authorization.feature +30 -7
  7. data/features/step_definitions/actor_steps.rb +29 -25
  8. data/features/step_definitions/enterprise_steps.rb +81 -0
  9. data/features/step_definitions/error_steps.rb +49 -0
  10. data/features/step_definitions/fixture_steps.rb +14 -0
  11. data/features/step_definitions/resource_steps.rb +16 -24
  12. data/features/support/env.rb +4 -2
  13. data/lib/eaco.rb +2 -0
  14. data/lib/eaco/actor.rb +4 -3
  15. data/lib/eaco/adapters/active_record.rb +1 -1
  16. data/lib/eaco/adapters/active_record/compatibility.rb +19 -14
  17. data/lib/eaco/adapters/active_record/compatibility/scoped.rb +25 -0
  18. data/lib/eaco/adapters/active_record/compatibility/v40.rb +11 -3
  19. data/lib/eaco/adapters/active_record/compatibility/v41.rb +14 -3
  20. data/lib/eaco/adapters/active_record/compatibility/v42.rb +10 -2
  21. data/lib/eaco/controller.rb +16 -3
  22. data/lib/eaco/coverage.rb +83 -0
  23. data/lib/eaco/cucumber/active_record.rb +13 -18
  24. data/lib/eaco/cucumber/active_record/department.rb +4 -0
  25. data/lib/eaco/cucumber/active_record/position.rb +2 -0
  26. data/lib/eaco/cucumber/active_record/schema.rb +20 -2
  27. data/lib/eaco/cucumber/active_record/user.rb +9 -0
  28. data/lib/eaco/cucumber/active_record/user/designators.rb +4 -1
  29. data/lib/eaco/cucumber/active_record/user/designators/authenticated.rb +54 -0
  30. data/lib/eaco/cucumber/active_record/user/designators/department.rb +58 -0
  31. data/lib/eaco/cucumber/active_record/user/designators/position.rb +53 -0
  32. data/lib/eaco/cucumber/active_record/user/designators/user.rb +4 -0
  33. data/lib/eaco/cucumber/world.rb +115 -5
  34. data/lib/eaco/designator.rb +7 -2
  35. data/lib/eaco/dsl.rb +9 -1
  36. data/lib/eaco/dsl/acl.rb +2 -2
  37. data/lib/eaco/dsl/actor.rb +6 -3
  38. data/lib/eaco/dsl/base.rb +5 -0
  39. data/lib/eaco/error.rb +10 -1
  40. data/lib/eaco/rake.rb +1 -0
  41. data/lib/eaco/rake/default_task.rb +29 -9
  42. data/lib/eaco/rake/utils.rb +38 -0
  43. data/lib/eaco/version.rb +1 -1
  44. data/spec/eaco/acl_spec.rb +34 -0
  45. data/spec/spec_helper.rb +3 -3
  46. metadata +18 -2
@@ -1,8 +1,9 @@
1
1
  begin
2
2
  require 'active_support/concern'
3
3
  rescue LoadError
4
- # This is falsely true during specs ran by Guard. FIXME.
4
+ # :nocov: This is falsely true during specs ran by Guard. FIXME.
5
5
  abort 'Eaco::Controller requires activesupport. Please add it to Gemfile.'
6
+ # :nocov:
6
7
  end
7
8
 
8
9
  module Eaco
@@ -59,7 +60,14 @@ module Eaco
59
60
  end
60
61
 
61
62
  ##
62
- # @return [Symbol] the permission required to access the given action.
63
+ # Gets the permission required to access the given +action+, falling
64
+ # back on the default +:all+ action, or +nil+ if no permission is
65
+ # defined.
66
+ #
67
+ # @return [Symbol] the required permission or nil
68
+ #
69
+ # @see {Eaco::Resource}
70
+ # @see {Eaco::DSL::Resource}
63
71
  #
64
72
  def permission_for(action)
65
73
  authorization_permissions[action] || authorization_permissions[:all]
@@ -67,7 +75,12 @@ module Eaco
67
75
 
68
76
  protected
69
77
  ##
70
- # Permission requirements configured on this controller.
78
+ # Permission requirements configured on this controller, keyed by
79
+ # permission symbol and with role symbols as values.
80
+ #
81
+ # @return [Hash]
82
+ #
83
+ # @see {Eaco::DSL::Resource}
71
84
  #
72
85
  def authorization_permissions
73
86
  @_authorization_permissions ||= {}
@@ -0,0 +1,83 @@
1
+ require 'coveralls'
2
+ require 'simplecov'
3
+ require 'eaco/rake'
4
+
5
+ module Eaco
6
+
7
+ ##
8
+ # Integration with code coverage tools.
9
+ #
10
+ # Loading this module will start collecting coverage data.
11
+ #
12
+ module Coverage
13
+ extend self
14
+
15
+ ##
16
+ # Starts collecting coverage data.
17
+ #
18
+ # @return [nil]
19
+ #
20
+ def start!
21
+ Coveralls.wear_merged!(&simplecov_configuration)
22
+
23
+ nil
24
+ end
25
+
26
+ ##
27
+ # Reports coverage data to the remote service
28
+ #
29
+ # @return [nil]
30
+ #
31
+ def report!
32
+ simplecov
33
+ Coveralls.push!
34
+
35
+ nil
36
+ end
37
+
38
+ ##
39
+ # Formats coverage results using the default formatter.
40
+ #
41
+ # @return [String] Coverage summary
42
+ #
43
+ def format!
44
+ Rake::Utils.capture_stdout do
45
+ result && result.format!
46
+ end.strip
47
+ end
48
+
49
+ private
50
+
51
+ ##
52
+ # The coverage result
53
+ #
54
+ # @return [SimpleCov::Result]
55
+ #
56
+ def result
57
+ simplecov.result
58
+ end
59
+
60
+ ##
61
+ # Configures simplecov using {.simplecov_configuration}
62
+ #
63
+ # @return [Class] +SimpleCov+
64
+ #
65
+ def simplecov
66
+ SimpleCov.configure(&simplecov_configuration)
67
+ end
68
+
69
+ ##
70
+ # Configures +SimpleCov+ to use a different directory
71
+ # for each different appraisal +Gemfile+.
72
+ #
73
+ # @return [Proc] a +SimpleCov+ configuration block.
74
+ #
75
+ def simplecov_configuration
76
+ proc do
77
+ gemfile = Eaco::Rake::Utils.gemfile
78
+ coverage_dir "coverage/#{gemfile}"
79
+ end
80
+ end
81
+ end
82
+
83
+ end
@@ -1,7 +1,9 @@
1
1
  begin
2
2
  require 'active_record'
3
3
  rescue LoadError
4
+ # :nocov:
4
5
  abort "ActiveRecord requires the rails appraisal. Try `appraisal cucumber`"
6
+ # :nocov:
5
7
  end
6
8
 
7
9
  require 'yaml'
@@ -58,15 +60,6 @@ module Eaco
58
60
  active_record.logger
59
61
  end
60
62
 
61
- ##
62
- # @return [ActiveRecord::Connection] the current +ActiveRecord+ connection
63
- # object.
64
- #
65
- def connection
66
- active_record.connection
67
- end
68
- alias adapter connection
69
-
70
63
  ##
71
64
  # Returns an Hash wit the database configuration.
72
65
  #
@@ -80,9 +73,11 @@ module Eaco
80
73
  def configuration
81
74
  @_config ||= YAML.load(config_file.read).tap do |conf|
82
75
  def conf.to_s
76
+ # :nocov:
83
77
  'pgsql://%s:%s@%s/%s' % values_at(
84
78
  :username, :password, :hostname, :database
85
79
  )
80
+ # :nocov:
86
81
  end
87
82
  end
88
83
  end
@@ -104,6 +99,7 @@ module Eaco
104
99
  Pathname.new('features/active_record.yml').realpath
105
100
 
106
101
  rescue Errno::ENOENT => error
102
+ # :nocov:
107
103
  raise error.class.new, <<-EOF.squeeze(' ')
108
104
 
109
105
  #{error.message}.
@@ -112,6 +108,7 @@ module Eaco
112
108
  default location, or specify your configuration file location by
113
109
  passing the `EACO_AR_CONFIG' environment variable.
114
110
  EOF
111
+ # :nocov:
115
112
  end
116
113
 
117
114
  ##
@@ -143,23 +140,21 @@ module Eaco
143
140
  protected
144
141
 
145
142
  ##
146
- # Captures stdout and logs it
143
+ # Captures stdout emitted by the given +block+ and logs it
144
+ # as +info+ messages.
147
145
  #
146
+ # @param block [Proc]
148
147
  # @return [nil]
148
+ # @see {Rake::Utils.capture_stdout}
149
149
  #
150
- def log_stdout
151
- stdout, string = $stdout, StringIO.new
152
- $stdout = string
153
-
154
- yield
150
+ def log_stdout(&block)
151
+ stdout = Rake::Utils.capture_stdout(&block)
155
152
 
156
- string.tap(&:rewind).read.split("\n").each do |line|
153
+ stdout.split("\n").each do |line|
157
154
  logger.info line
158
155
  end
159
156
 
160
157
  nil
161
- ensure
162
- $stdout = stdout
163
158
  end
164
159
  end
165
160
 
@@ -12,6 +12,10 @@ module Eaco
12
12
  # @see Eaco::Cucumber::World
13
13
  #
14
14
  class Department < ::ActiveRecord::Base
15
+ has_many :positions
16
+ has_many :users, through: :positions
17
+
18
+ validates :name, uniqueness: true
15
19
  end
16
20
 
17
21
  end
@@ -14,6 +14,8 @@ module Eaco
14
14
  class Position < ::ActiveRecord::Base
15
15
  belongs_to :user
16
16
  belongs_to :department
17
+
18
+ validates :user, :department, presence: :true
17
19
  end
18
20
 
19
21
  end
@@ -2,6 +2,18 @@ module Eaco
2
2
  module Cucumber
3
3
  module ActiveRecord
4
4
 
5
+ # @!method clean
6
+ #
7
+ # Drops all tables currently instantiated in the database.
8
+ #
9
+ # @see Eaco::Cucumber::World
10
+ #
11
+ ::ActiveRecord::Base.connection.tap do |connection|
12
+ connection.tables.each do |table_name|
13
+ connection.drop_table table_name
14
+ end
15
+ end
16
+
5
17
  # @!method schema
6
18
  #
7
19
  # Defines the database schema for the {Eaco::Cucumber::World} scenario.
@@ -9,21 +21,27 @@ module Eaco
9
21
  # @see Eaco::Cucumber::World
10
22
  #
11
23
  ::ActiveRecord::Schema.define(version: '2015022301') do
24
+ # Resource
12
25
  create_table 'documents', force: true do |t|
13
26
  t.string :name
14
27
  t.column :acl, :jsonb
15
28
  end
16
29
 
30
+ # Actor
17
31
  create_table 'users', force: true do |t|
18
32
  t.string :name
33
+ t.boolean :admin, default: false
19
34
  end
20
35
 
36
+ # Designator source
21
37
  create_table 'departments', force: true do |t|
22
- t.string :abbr
38
+ t.string :name
23
39
  end
40
+ add_index :departments, :name, unique: true
24
41
 
42
+ # Designator source
25
43
  create_table 'positions', force: true do |t|
26
- t.string :job_title
44
+ t.string :name
27
45
 
28
46
  t.references :user
29
47
  t.references :department
@@ -17,6 +17,15 @@ module Eaco
17
17
 
18
18
  has_many :positions
19
19
  has_many :departments, through: :positions
20
+
21
+ ##
22
+ # The {Department} names this User has a {Position} in.
23
+ #
24
+ # @return [Array] the {Department} names as +String+s.
25
+ #
26
+ def department_names
27
+ departments.to_set(&:name)
28
+ end
20
29
  end
21
30
 
22
31
  end
@@ -9,7 +9,10 @@ module Eaco
9
9
  # @see World
10
10
  #
11
11
  module Designators
12
- autoload :User, 'eaco/cucumber/active_record/user/designators/user.rb'
12
+ autoload :Authenticated, 'eaco/cucumber/active_record/user/designators/authenticated.rb'
13
+ autoload :Department, 'eaco/cucumber/active_record/user/designators/department.rb'
14
+ autoload :Position, 'eaco/cucumber/active_record/user/designators/position.rb'
15
+ autoload :User, 'eaco/cucumber/active_record/user/designators/user.rb'
13
16
  end
14
17
 
15
18
  end
@@ -0,0 +1,54 @@
1
+ module Eaco
2
+ module Cucumber
3
+ module ActiveRecord
4
+ class User
5
+ module Designators
6
+
7
+ ##
8
+ # A {Designator} based on a the {User} class.
9
+ #
10
+ # This is an example on how to grant rights to all instances
11
+ # of a given model.
12
+ #
13
+ # The class name is available as the {Designator#value}.
14
+ #
15
+ # The String representation for an example User is
16
+ # +"authenticated:User"+.
17
+ #
18
+ class Authenticated < Eaco::Designator
19
+ label "Any user"
20
+
21
+ ##
22
+ # This {Designator} description.
23
+ #
24
+ # @return [String] an hardcoded description
25
+ #
26
+ def describe(*)
27
+ "Any authenticated user"
28
+ end
29
+
30
+ ##
31
+ # {User}s matching this designator.
32
+ #
33
+ # @return [Array] All {User}s.
34
+ #
35
+ def resolve
36
+ klass.all
37
+ end
38
+
39
+ private
40
+ ##
41
+ # Looks up this class by constantizing it.
42
+ #
43
+ # @return [Class]
44
+ #
45
+ def klass
46
+ @_klass ||= self.value.constantize
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,58 @@
1
+ module Eaco
2
+ module Cucumber
3
+ module ActiveRecord
4
+ class User
5
+ module Designators
6
+
7
+ ##
8
+ # A {Designator} based on a the {Department} an {User} occupies
9
+ # a {Position} in. It resolves {Actor}s by id looking them up
10
+ # through the {Position} model.
11
+ #
12
+ # As {Department}s have unique names, their name instead of
13
+ # their ID is used in this example.
14
+ #
15
+ # The Department name is available as the {Designator#value}.
16
+ #
17
+ # The String representation for an example ICT Department is
18
+ # +"department:ICT"+.
19
+ #
20
+ class Department < Eaco::Designator
21
+ ##
22
+ # This {Designator} description.
23
+ #
24
+ # @return [String] the {Department} name, such as ICT or COM
25
+ # or EXE or BAT.
26
+ #
27
+ def describe(*)
28
+ department.name
29
+ end
30
+
31
+ ##
32
+ # {User}s matching this designator.
33
+ #
34
+ # @return [Array] all users currently occupying a position in
35
+ # this Department
36
+ #
37
+ def resolve
38
+ department.users
39
+ end
40
+
41
+ private
42
+ ##
43
+ # Looks up this Department by name, and memoizes it in an
44
+ # instance variable.
45
+ #
46
+ # @return [ActiveRecord::Department] the referenced department
47
+ #
48
+ def department
49
+ @_department ||= ActiveRecord::Department.
50
+ where(name: self.value).first!
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ module Eaco
2
+ module Cucumber
3
+ module ActiveRecord
4
+ class User
5
+ module Designators
6
+
7
+ ##
8
+ # A {Designator} based on a position an {User} occupies in an
9
+ # organigram. It resolves {Actor}s by id looking them up from
10
+ # the +user_id+ field.
11
+ #
12
+ # The Position ID is available as the {Designator#value}.
13
+ #
14
+ # The String representation for an example Position 42 is
15
+ # +"position:42"+.
16
+ #
17
+ class Position < Eaco::Designator
18
+ ##
19
+ # This {Designator} description.
20
+ #
21
+ # @return [String] the {Position} name, such as "Manager" or
22
+ # or "Systems Analyst" or "Consultant".
23
+ #
24
+ def describe(*)
25
+ "#{position.name} in #{position.department.name}"
26
+ end
27
+
28
+ ##
29
+ # {User}s matching this designator.
30
+ #
31
+ # @return [Array] the user currently occupying this Position.
32
+ #
33
+ def resolve
34
+ [position.user]
35
+ end
36
+
37
+ private
38
+ ##
39
+ # Looks up this position by ID, and memoizes it in an instance
40
+ # variable.
41
+ #
42
+ # @return [ActiveRecord::Position] the referenced Position.
43
+ #
44
+ def position
45
+ @_position ||= ActiveRecord::Position.find(self.value)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end