cantango-core 0.1.2 → 0.1.3

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.
Files changed (92) hide show
  1. data/Gemfile +7 -1
  2. data/Gemfile.lock +70 -12
  3. data/README.mdown +16 -14
  4. data/VERSION +1 -1
  5. data/cantango-core.gemspec +41 -51
  6. data/lib/cantango/ability/base/callbacks.rb +37 -0
  7. data/lib/cantango/ability/base.rb +21 -8
  8. data/lib/cantango/ability/cache.rb +1 -1
  9. data/lib/cantango/ability/executor/modes.rb +6 -6
  10. data/lib/cantango/ability/executor/no_cache_mode.rb +2 -6
  11. data/lib/cantango/ability/executor.rb +1 -1
  12. data/lib/cantango/ability/helper/account.rb +6 -10
  13. data/lib/cantango/ability/helper/engine.rb +22 -26
  14. data/lib/cantango/ability/helper/user.rb +10 -14
  15. data/lib/cantango/ability/helper.rb +1 -1
  16. data/lib/cantango/ability.rb +7 -2
  17. data/lib/cantango/adaptor/active_record.rb +6 -0
  18. data/lib/cantango/adaptor/data_mapper.rb +5 -0
  19. data/lib/cantango/adaptor/generic.rb +12 -0
  20. data/lib/cantango/adaptor/mongo.rb +13 -0
  21. data/lib/cantango/adaptor/mongo_mapper.rb +5 -0
  22. data/lib/cantango/adaptor/mongoid.rb +5 -0
  23. data/lib/cantango/adaptor/relational.rb +8 -0
  24. data/lib/cantango/{rules/adaptor.rb → adaptor.rb} +4 -4
  25. data/lib/cantango/{rules → cancan}/rule_class.rb +1 -1
  26. data/lib/cantango/cancan.rb +5 -0
  27. data/lib/cantango/core.rb +5 -13
  28. data/lib/cantango/engine.rb +6 -16
  29. data/lib/cantango/filter/base.rb +1 -1
  30. data/lib/cantango/helpers/debug.rb +31 -1
  31. data/lib/cantango/loader/yaml.rb +2 -11
  32. data/lib/cantango/model/guest.rb +3 -1
  33. data/spec/cantango/ability/base_spec.rb +13 -63
  34. data/spec/cantango/ability/cache/simple_key_spec.rb +29 -0
  35. data/spec/cantango/ability/executor/base_spec.rb +12 -52
  36. data/spec/cantango/ability/executor/custom_spec.rb +51 -0
  37. data/spec/cantango/ability/executor/modes_spec.rb +27 -49
  38. data/spec/cantango/ability/executor/no_cache_mode_spec.rb +29 -0
  39. data/spec/cantango/ability/helper/account_spec.rb +37 -0
  40. data/spec/cantango/ability/helper/engine_spec.rb +36 -0
  41. data/spec/cantango/ability/helper/user_spec.rb +38 -0
  42. data/spec/cantango/adaptor/active_record_spec.rb +38 -0
  43. data/spec/cantango/adaptor/data_mapper_spec.rb +39 -0
  44. data/spec/cantango/{rules/adaptor → adaptor}/mongo_mapper_spec.rb +0 -0
  45. data/spec/cantango/{rules/adaptor → adaptor}/mongoid_spec.rb +0 -0
  46. data/spec/cantango/cancan/rule_spec.rb +5 -0
  47. data/spec/cantango/core_spec.rb +0 -2
  48. data/spec/cantango/engine_spec.rb +51 -0
  49. data/spec/cantango/filter/base_spec.rb +25 -0
  50. data/spec/cantango/helpers/debug_spec.rb +29 -0
  51. data/spec/cantango/loader/yaml_spec.rb +5 -0
  52. data/spec/cantango/model/guest_spec.rb +14 -0
  53. data/spec/cantango/rspec/be_allowed_to_spec.rb +5 -0
  54. data/spec/cantango/scope/ability_spec.rb +33 -0
  55. data/spec/fixtures/models/account.rb +7 -0
  56. data/spec/fixtures/models/admin.rb +2 -0
  57. data/spec/fixtures/models/admin_account.rb +8 -0
  58. data/spec/fixtures/models/items.rb +11 -0
  59. data/spec/fixtures/models/user.rb +20 -0
  60. data/spec/fixtures/models.rb +2 -0
  61. data/spec/spec_helper.rb +4 -0
  62. metadata +60 -67
  63. data/lib/cantango/ability/executor/cache_mode.rb +0 -33
  64. data/lib/cantango/ability/helper/role.rb +0 -21
  65. data/lib/cantango/ability/helper/role_group.rb +0 -21
  66. data/lib/cantango/macros/account.rb +0 -14
  67. data/lib/cantango/macros/user.rb +0 -16
  68. data/lib/cantango/macros.rb +0 -11
  69. data/lib/cantango/rules/adaptor/active_record.rb +0 -10
  70. data/lib/cantango/rules/adaptor/data_mapper.rb +0 -11
  71. data/lib/cantango/rules/adaptor/generic.rb +0 -16
  72. data/lib/cantango/rules/adaptor/mongo.rb +0 -19
  73. data/lib/cantango/rules/adaptor/mongo_mapper.rb +0 -10
  74. data/lib/cantango/rules/adaptor/mongoid.rb +0 -9
  75. data/lib/cantango/rules/adaptor/relational.rb +0 -13
  76. data/lib/cantango/rules/dsl.rb +0 -24
  77. data/lib/cantango/rules/relation.rb +0 -67
  78. data/lib/cantango/rules/scope.rb +0 -24
  79. data/lib/cantango/rules.rb +0 -8
  80. data/spec/cantango/ability/cached_spec.rb +0 -0
  81. data/spec/cantango/ability/executor/base2.rb +0 -75
  82. data/spec/cantango/ability/executor/cache_mode_spec.rb +0 -77
  83. data/spec/cantango/macros/account_spec.rb +0 -0
  84. data/spec/cantango/macros/user_spec.rb +0 -0
  85. data/spec/cantango/rules/adaptor/active_record_spec.rb +0 -0
  86. data/spec/cantango/rules/adaptor/data_mapper_spec.rb +0 -0
  87. data/spec/cantango/rules/adaptor_spec.rb +0 -0
  88. data/spec/cantango/rules/dsl_spec.rb +0 -0
  89. data/spec/cantango/rules/relation_spec.rb +0 -0
  90. data/spec/cantango/rules/rule_class_spec.rb +0 -0
  91. data/spec/cantango/rules/scope_spec.rb +0 -0
  92. data/spec/cantango/rules_spec.rb +0 -55
@@ -1,32 +1,28 @@
1
- module CanTango
2
- class Ability
3
- module Helper
4
- module Engine
5
- def execute_engines!
6
- each_engine do |engine|
7
- engine_rules = engine.new(self).execute! if engine
8
- @rules << engine_rules if !engine_rules.blank?
9
- end
10
- end
1
+ module CanTango::Ability::Helper
2
+ module Engine
3
+ def execute_engines!
4
+ each_engine do |engine|
5
+ engine_rules = engine.new(self).execute! if engine
6
+ @rules << engine_rules if !engine_rules.blank?
7
+ end
8
+ end
11
9
 
12
- def each_engine &block
13
- engines.execution_order.each do |name|
14
- yield engines.registered[name] if engines.active? name
15
- end
16
- end
10
+ def each_engine &block
11
+ engines.execution_order.each do |name|
12
+ yield engines.registered[name] if engines.active? name
13
+ end
14
+ end
17
15
 
18
- def opts_engines_off?
19
- options[:engines] == :off
20
- end
16
+ def opts_engines_off?
17
+ options[:engines] == :off
18
+ end
21
19
 
22
- def engines_on?
23
- CanTango.config.engines.any?(:on) && !opts_engines_off?
24
- end
20
+ def engines_on?
21
+ CanTango.config.engines.any?(:on) && !opts_engines_off?
22
+ end
25
23
 
26
- def engines
27
- CanTango.config.engines
28
- end
29
- end
24
+ def engines
25
+ CanTango.config.engines
30
26
  end
31
27
  end
32
- end
28
+ end
@@ -1,18 +1,14 @@
1
- module CanTango
2
- class Ability
3
- module Helper
4
- module User
5
- def user
6
- return subject.user if subject.respond_to? :user
7
- subject
8
- end
1
+ module CanTango::Ability::Helper
2
+ module User
3
+ def user
4
+ return subject.user if subject.respond_to? :user
5
+ subject
6
+ end
9
7
 
10
- def user_key_field
11
- key_field = config.user.unique_key_field
12
- raise "\nModel <#{user.class}> has no ##{key_field} as defined in CanTango.config.user.unique_key_field" if !user.respond_to?(key_field)
13
- key_field
14
- end
15
- end
8
+ def user_key_field
9
+ key_field = CanTango.config.user.unique_key_field
10
+ raise "\nModel <#{user.class}> has no ##{key_field} as defined in CanTango.config.user.unique_key_field" if !user.respond_to?(key_field)
11
+ key_field
16
12
  end
17
13
  end
18
14
  end
@@ -2,7 +2,7 @@ module CanTango
2
2
  module Ability
3
3
  module Helper
4
4
  def self.modules
5
- [:Account, :Cache, :Engine, :Masquerade, :Permit, :PermitStore, :Role, :RoleGroup, :User]
5
+ [:Account, :Engine, :User]
6
6
  end
7
7
 
8
8
  autoload_modules *modules
@@ -1,5 +1,10 @@
1
1
  module CanTango
2
- module Ability
3
- autoload_modules :Base, :Cache, :Cached, :Executor, :Helper
2
+ module Ability
3
+ autoload_modules :Base, :Cache, :Executor, :Helper
4
+
5
+ # by default add callbacks?
6
+ class Base
7
+ include Callbacks
8
+ end
4
9
  end
5
10
  end
@@ -0,0 +1,6 @@
1
+ module CanTango::Adaptor
2
+ module ActiveRecord
3
+ include CanTango::Rules::Adaptor::Relational
4
+ end
5
+ end
6
+
@@ -0,0 +1,5 @@
1
+ module CanTango::Adaptor
2
+ module DataMapper
3
+ include CanTango::Rules::Adaptor::Relational
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module CanTango::Adaptor
2
+ module Generic
3
+
4
+ def attribute_condition attribute, candidate
5
+ Proc.new { |model| model.send(attribute) == candidate }
6
+ end
7
+
8
+ def include_condition attribute, candidate
9
+ Proc.new { |model| model.send(attribute).include? candidate }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module CanTango::Adaptor
2
+ module Mongo
3
+ #include CanTango::Rules::Adaptor::Generic
4
+ # using #in on Hash (Mongoid query)
5
+ def include_condition attribute, candidate
6
+ { attribute.to_sym.in => candidate.send(attribute) }
7
+ end
8
+
9
+ def attribute_condition attribute, user_scope
10
+ { attribute.to_sym => candidate.send(attribute) }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module CanTango::Adaptor
2
+ module MongoMapper
3
+ include module CanTango::Rules::Adaptor::Mongo
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module CanTango::Adaptor
2
+ module Mongoid
3
+ include module CanTango::Rules::Adaptor::Mongo
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module CanTango::Adaptor
2
+ module Relational
3
+ def attribute_condition attribute, candidate
4
+ { attribute.to_sym => candidate.send(attribute) }
5
+ end
6
+ alias_method :include_condition, :attribute_condition
7
+ end
8
+ end
@@ -25,10 +25,10 @@ module CanTango
25
25
 
26
26
  def orm_map
27
27
  {
28
- :active_record => "ActiveRecord::Base",
29
- :data_mapper => "DataMapper::Resource",
30
- :mongoid => "Mongoid::Document",
31
- :mongo_mapper => "MongoMapper::Document"
28
+ :active_record => "ActiveRecord::Base",
29
+ :data_mapper => "DataMapper::Resource",
30
+ :mongoid => "Mongoid::Document",
31
+ :mongo_mapper => "MongoMapper::Document"
32
32
  }
33
33
  end
34
34
  end
@@ -1,5 +1,5 @@
1
1
  module CanTango
2
- module Rules
2
+ module CanCan
3
3
  module RuleClass
4
4
  # CanCan 1.5 compatibility
5
5
  def rule_class
@@ -0,0 +1,5 @@
1
+ module CanTango
2
+ module CanCan
3
+ autoload_modules :Rule, :RuleClass
4
+ end
5
+ end
data/lib/cantango/core.rb CHANGED
@@ -1,10 +1,3 @@
1
- require 'cantango/rails/railtie' if defined?(Rails)
2
- require 'cantango/rails/engine' if defined?(Rails)
3
-
4
- # essential cantango extensions
5
- # require 'cantango/config'
6
- # require 'cantango/api'
7
-
8
1
  require 'cancan'
9
2
  require 'active_support' # for Delegate module
10
3
  require 'active_support/core_ext/module/delegation'
@@ -15,12 +8,11 @@ require 'sugar-high/blank'
15
8
  require 'hashie'
16
9
  require 'sweetloader'
17
10
 
18
- AutoLoader.namespaces = {:CanTango => 'cantango'}
11
+ SweetLoader.namespaces = {:CanTango => 'cantango'}
12
+ SweetLoader.mode = :require
19
13
 
20
14
  module CanTango
21
- autoload_modules :Ability, :Engine, :Filter
22
- autoload_modules :Helpers, :Loader, :Macros, :Model
23
- autoload_modules :Rules, :Scope
15
+ autoload_modules :Helpers, :Ability, :Engine, :Filter
16
+ autoload_modules :Loader, :Model
17
+ autoload_modules :Scope
24
18
  end
25
-
26
- require 'cantango/macros'
@@ -2,12 +2,12 @@ module CanTango
2
2
  class Engine
3
3
  include CanTango::Helpers::Debug
4
4
 
5
- attr_reader :ability
5
+ attr_reader :ability, :options
6
6
 
7
7
  delegate :session, :user, :subject, :candidate, :cached?, :to => :ability
8
8
 
9
- def initialize ability
10
- @ability = ability
9
+ def initialize ability, options = {}
10
+ @ability, @options = [ability, options]
11
11
  end
12
12
 
13
13
  def execute!
@@ -18,22 +18,12 @@ module CanTango
18
18
  raise NotImplementedError
19
19
  end
20
20
 
21
- protected
22
-
23
- def valid_mode?
24
- valid_cache_mode? || valid_no_cache_mode?
25
- end
26
-
27
- def valid_cache_mode?
28
- modes.include?(:cache) && cached?
29
- end
30
-
31
- def valid_no_cache_mode?
32
- modes.include?(:no_cache) && !cached?
21
+ def valid_mode? mode
22
+ modes.include? mode.to_sym
33
23
  end
34
24
 
35
25
  def modes
36
- CanTango.config.engine(engine_name.to_sym).modes
26
+ @modes ||= options[:modes] || []
37
27
  end
38
28
  end
39
29
  end
@@ -5,7 +5,7 @@ module CanTango
5
5
 
6
6
  def initialize item, list = nil
7
7
  @item = item.to_sym
8
- @include_list = list || []
8
+ @include_list = list.map(&:to_sym) || []
9
9
  end
10
10
 
11
11
  def valid?
@@ -1,9 +1,39 @@
1
1
  module CanTango
2
2
  module Helpers
3
3
  module Debug
4
+ attr_writer :do_debug, :debug_writer
5
+
6
+ def self.included base
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ attr_writer :do_debug, :debug_writer
12
+
13
+ def debug?
14
+ @do_debug ||= false
15
+ end
16
+
17
+ def debug_writer
18
+ @debug_writer ||= lambda {|m| puts m }
19
+ end
20
+ end
21
+
4
22
  # you can set another writer on CanTango.config.debug, fx to spec it!
5
23
  def debug msg
6
- CanTango.config.debug.write msg if CanTango.debug?
24
+ if debug?
25
+ return debug_writer.write msg if debug_writer.respond_to? :write
26
+ return debug_writer.call(msg) if debug_writer.respond_to? :call
27
+ raise "No debug_writer set!"
28
+ end
29
+ end
30
+
31
+ def debug?
32
+ @do_debug ||= self.class.debug?
33
+ end
34
+
35
+ def debug_writer
36
+ @debug_writer ||= self.class.debug_writer
7
37
  end
8
38
  end
9
39
  end
@@ -14,19 +14,10 @@ module CanTango
14
14
  def yml_content
15
15
  YAML.load_file(file_name)
16
16
  rescue
17
- raise "Couldn't load permissions file: #{file_name}. Either disable Permission engine or add this file."
17
+ raise "Couldn't YAML file: #{file_name}"
18
18
  end
19
-
19
+
20
20
  module ClassMethods
21
- protected
22
-
23
- def config_file name
24
- File.join(config_path, "#{name}.yml") if rails?
25
- end
26
-
27
- def config_path
28
- CanTango.config.permissions.config_path
29
- end
30
21
  end
31
22
  end
32
23
  end
@@ -5,12 +5,14 @@ module CanTango
5
5
  module Guest
6
6
  def self.included(base)
7
7
  base.extend ::ActiveModel::Naming
8
+ base.extend ::ActiveModel::Callbacks
9
+
8
10
  base.send :include, ::ActiveModel::Conversion
9
11
  base.send :include, ::ActiveModel::Validations
10
12
  end
11
13
 
12
14
  def email
13
- 'guest@info.com'
15
+ 'guest@example.com'
14
16
  end
15
17
 
16
18
  def has_role? role
@@ -1,73 +1,23 @@
1
- require 'rspec'
2
- require 'cantango'
1
+ require 'spec_helper'
3
2
  require 'fixtures/models'
4
- require 'cantango/rspec'
5
3
 
6
- def config_folder
7
- File.dirname(__FILE__)+ "/../fixtures/config/"
8
- end
9
-
10
- CanTango.configure do |config|
11
- config.clear!
12
- # config.permissions.set :on
13
- config.engines.all :on
14
-
15
- config.permission_engine.config_path = config_folder
16
- config.categories.register :blog_items => [Article, Post]
17
- end
18
-
19
- class PublishersRoleGroupPermit < CanTango::RoleGroupPermit
20
- def initialize ability
21
- super
22
- end
23
-
24
- protected
25
-
26
- def static_rules
27
- can :write, category(:blog_items)
28
- cannot :write, Post
4
+ module CanTango::Ability
5
+ class Base
6
+ include CanTango::Ability::Helper::Account
7
+
8
+ def permit_rules
9
+ can :edit, Project
10
+ end
29
11
  end
30
12
  end
31
13
 
32
- class EditorRolePermit < CanTango::RolePermit
33
- def initialize ability
34
- super
35
- end
36
-
37
- protected
38
-
39
- def static_rules
40
- can :read, Comment
41
- end
42
- end
43
-
44
-
45
- describe CanTango::Ability do
46
- let (:user) do
47
- User.new 'krisy', 'krisy@gmail.com'
48
- end
49
-
50
- let (:user_account) do
51
- ua = UserAccount.new user, :roles => [:editor], :role_groups => [:publishers]
52
- user.account = ua
53
- end
54
-
14
+ describe CanTango::Ability::Base do
55
15
  before do
56
- CanTango.config.clear!
57
- @ability = CanTango::Ability.new user_account
16
+ user = User.new 'krisy', 'krisy@gmail.com'
17
+ @account = Account.new user
58
18
  end
59
19
 
60
- specify { CanTango.config.roles.excluded.should be_empty }
61
- specify { CanTango.config.role_groups.excluded.should be_empty }
62
-
63
- subject { @ability }
64
- specify { @ability.should be_allowed_to(:read, Comment) }
65
- specify { @ability.should be_allowed_to(:write, Article) }
66
- specify { @ability.should_not be_allowed_to(:write, Post) }
67
-
68
- its(:user_account) { should be_a(UserAccount) }
69
- its(:user) { should be_a(User) }
20
+ subject { CanTango::Ability::Base.new @account }
70
21
 
71
- its(:roles) { should_not be_empty }
72
- its(:role_groups) { should_not be_empty }
22
+ specify { subject.should be_allowed_to(:edit, Project) }
73
23
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/models'
3
+
4
+ describe CanTango::Ability::Cache::SimpleKey do
5
+ before do
6
+ @user = User.new 'krisy', 'krisy@gmail.com'
7
+ @account = Account.new user
8
+ @ability = CanTango::Ability::Base.new @account
9
+ end
10
+
11
+ context 'SimpleKey for user' do
12
+ subject { CanTango::Ability::Cache::SimpleKey.new @user }
13
+
14
+ specify do
15
+ subject.value.should == @user.email.hash
16
+ end
17
+
18
+ specify do
19
+ subject.to_s.should match /#{@user.email.hash}/
20
+ end
21
+
22
+ context '#Simplekey #create_for Ability' do
23
+ subject { CanTango::Ability::Cache::SimpleKey.create_for @ability }
24
+
25
+ specify do
26
+ subject.value.should == @user.email.hash
27
+ end
28
+ end
29
+ end
@@ -1,67 +1,27 @@
1
- require 'rspec'
2
- require 'cantango'
1
+ require 'spec_helper'
3
2
  require 'fixtures/models'
4
- require 'cantango/rspec'
5
3
 
6
- def config_folder
7
- File.dirname(__FILE__)+ "/../fixtures/config/"
8
- end
9
-
10
- CanTango.configure do |config|
11
- config.clear!
12
- config.ability.mode = :no_cache
13
- end
14
-
15
- class MyExecutor
16
- include CanTango::Ability::Executor
17
-
18
- attr_reader :ability
19
-
20
- def initialize ability
21
- @ability = ability
22
- end
23
-
24
- def valid?
25
- true
26
- end
27
-
28
- def cache_key
29
- :my_exec
30
- end
31
-
32
- def permit_rules
33
- ability.permit_rules
34
- end
35
- end
36
-
37
- module CanTango
38
- class Ability
4
+ module CanTango::Ability
5
+ class Base
39
6
  def permit_rules
40
- can :edit, Project
7
+ can :read, Post
41
8
  end
42
9
  end
43
10
  end
44
11
 
45
- describe CanTango::Ability::Executor do
46
- context 'no-cache' do
47
- let (:ability) do
48
- CanTango::Ability.new @user
49
- end
50
-
12
+ describe CanTango::Ability::Executor::Base do
13
+ context 'non-cached only' do
51
14
  before do
52
- @user = User.new 'kris'
15
+ @user = User.new 'admin', 'admin@mail.ru'
53
16
  end
54
17
 
55
- subject { MyExecutor.new ability }
18
+ subject { CanTango::Ability::Executor::Base.new @user }
56
19
 
57
- describe '#execute!' do
58
- before do
59
- subject.execute!
60
- end
20
+ its(:non_cached_rules) { should_not be_empty }
61
21
 
62
- specify { subject.ability.send(:rules).should_not be_empty }
22
+ describe 'rules contain only non-cached rules' do
23
+ specify { subject.rules.size.should == @abil.non_cached_rules.size }
24
+ specify { subject.rules.size.should == 1 }
63
25
  end
64
26
  end
65
27
  end
66
-
67
-
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/models'
3
+
4
+ class MyExecutor < CanTango::Ability::Executor::Base
5
+ attr_reader :ability
6
+
7
+ def initialize ability
8
+ super
9
+ end
10
+
11
+ def valid?
12
+ true
13
+ end
14
+
15
+ def cache_key
16
+ :my_exec
17
+ end
18
+
19
+ def permit_rules
20
+ ability.permit_rules
21
+ end
22
+ end
23
+
24
+ module CanTango::Ability
25
+ class Base
26
+ def permit_rules
27
+ can :edit, Project
28
+ end
29
+ end
30
+ end
31
+
32
+ describe CanTango::Ability::Executor::Base do
33
+ context 'Custom executor inheriting from Base' do
34
+ before do
35
+ @user = User.new 'kris'
36
+ @ability = CanTango::Ability::Base.new @user
37
+ end
38
+
39
+ subject { MyExecutor.new @ability }
40
+
41
+ describe '#execute!' do
42
+ before do
43
+ subject.execute!
44
+ end
45
+
46
+ specify { subject.ability.send(:rules).should_not be_empty }
47
+ end
48
+ end
49
+ end
50
+
51
+