dynamic_controller 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +80 -80
- data/dynamic_controller.gemspec +27 -27
- data/lib/dynamic_controller/action_controller_extension.rb +18 -16
- data/lib/dynamic_controller/class_methods.rb +41 -41
- data/lib/dynamic_controller/helper_methods.rb +103 -91
- data/lib/dynamic_controller/instance_methods.rb +139 -139
- data/lib/dynamic_controller/resource.rb +21 -21
- data/lib/dynamic_controller/responder.rb +70 -70
- data/lib/dynamic_controller/version.rb +3 -3
- data/lib/dynamic_controller.rb +18 -18
- data/spec/controller_factory.rb +22 -22
- data/spec/controllers/crud_actions_html_spec.rb +111 -111
- data/spec/controllers/crud_actions_json_spec.rb +91 -91
- data/spec/controllers/nested_crud_actions_html_spec.rb +125 -125
- data/spec/controllers/nested_crud_actions_json_spec.rb +97 -97
- data/spec/controllers/ransack_spec.rb +57 -57
- data/spec/controllers/redefined_responders_html_spec.rb +111 -111
- data/spec/controllers/redefined_responders_json_spec.rb +94 -94
- data/spec/controllers/two_level_nested_crud_actions_html_spec.rb +133 -133
- data/spec/controllers/two_level_nested_crud_actions_json_spec.rb +97 -97
- data/spec/custom_responder_format_spec.rb +12 -12
- data/spec/dummy/Gemfile +13 -13
- data/spec/dummy/app/controllers/cities_controller.rb +95 -95
- data/spec/dummy/app/controllers/languages_controller.rb +95 -95
- data/spec/dummy/app/controllers/streets_controller.rb +97 -97
- data/spec/dummy/app/models/city.rb +6 -6
- data/spec/dummy/app/models/language.rb +4 -4
- data/spec/dummy/app/models/street.rb +5 -5
- data/spec/dummy/app/views/cities/_form.html.erb +25 -25
- data/spec/dummy/app/views/cities/index.html.erb +29 -29
- data/spec/dummy/app/views/countries/index.html.erb +25 -25
- data/spec/dummy/app/views/languages/_form.html.erb +21 -21
- data/spec/dummy/app/views/languages/edit.html.erb +6 -6
- data/spec/dummy/app/views/languages/index.html.erb +23 -23
- data/spec/dummy/app/views/languages/new.html.erb +5 -5
- data/spec/dummy/app/views/languages/show.html.erb +10 -10
- data/spec/dummy/app/views/streets/_form.html.erb +25 -25
- data/spec/dummy/app/views/streets/edit.html.erb +6 -6
- data/spec/dummy/app/views/streets/index.html.erb +27 -27
- data/spec/dummy/app/views/streets/new.html.erb +5 -5
- data/spec/dummy/app/views/streets/show.html.erb +15 -15
- data/spec/dummy/config/routes.rb +8 -8
- data/spec/dummy/db/migrate/20120922010743_create_languages.rb +9 -9
- data/spec/dummy/db/migrate/20120929185302_create_streets.rb +11 -11
- data/spec/dummy/db/schema.rb +46 -46
- data/spec/factories.rb +21 -21
- data/spec/has_crud_actions_options_spec.rb +48 -48
- data/spec/spec_helper.rb +35 -35
- metadata +58 -17
data/README.md
CHANGED
@@ -1,80 +1,80 @@
|
|
1
|
-
# DynamicController
|
2
|
-
|
3
|
-
Simple way to add CRUD actions into Rails controllers.
|
4
|
-
|
5
|
-
Suppoted formats HTML and JSON.
|
6
|
-
|
7
|
-
Tested with Ruby 1.9.3 and Rails 3.2.8.
|
8
|
-
|
9
|
-
## Installation
|
10
|
-
|
11
|
-
Add this line to your application's Gemfile:
|
12
|
-
|
13
|
-
gem 'dynamic_controller'
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install dynamic_controller
|
22
|
-
|
23
|
-
## Adding CRUD actions to resource controller
|
24
|
-
|
25
|
-
class UsersController < ApplicationController
|
26
|
-
has_crud_actions
|
27
|
-
end
|
28
|
-
|
29
|
-
has_crud_actions adds index, show, new, edit, create, update and destroy actions to controller
|
30
|
-
|
31
|
-
## Explicit action specification
|
32
|
-
|
33
|
-
class UsersController < ApplicationController
|
34
|
-
has_crud_actions only: [:index, :new, :create]
|
35
|
-
end
|
36
|
-
|
37
|
-
or
|
38
|
-
|
39
|
-
class UsersController < ApplicationController
|
40
|
-
has_crud_actions except: :destroy
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
## Nested resources support
|
45
|
-
|
46
|
-
class ProfilesController < ApplicationController
|
47
|
-
has_crud_actions
|
48
|
-
nested_of User
|
49
|
-
end
|
50
|
-
|
51
|
-
If has more than one nested level should use
|
52
|
-
|
53
|
-
class StreetsController < ApplicationController
|
54
|
-
has_crud_actions
|
55
|
-
nested_of Country
|
56
|
-
nested_of City
|
57
|
-
end
|
58
|
-
|
59
|
-
## Redefining responder
|
60
|
-
|
61
|
-
class LanguagesController < ApplicationController
|
62
|
-
has_crud_actions
|
63
|
-
|
64
|
-
respond_to_create :html do
|
65
|
-
redirect_to action: :index
|
66
|
-
end
|
67
|
-
|
68
|
-
respond_to_update do |format|
|
69
|
-
format.html { redirect_to action: :index }
|
70
|
-
format.json { render json: @language }
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
## Contributing
|
75
|
-
|
76
|
-
1. Fork it
|
77
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
78
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
79
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
80
|
-
5. Create new Pull Request
|
1
|
+
# DynamicController
|
2
|
+
|
3
|
+
Simple way to add CRUD actions into Rails controllers.
|
4
|
+
|
5
|
+
Suppoted formats HTML and JSON.
|
6
|
+
|
7
|
+
Tested with Ruby 1.9.3 and Rails 3.2.8.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'dynamic_controller'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install dynamic_controller
|
22
|
+
|
23
|
+
## Adding CRUD actions to resource controller
|
24
|
+
|
25
|
+
class UsersController < ApplicationController
|
26
|
+
has_crud_actions
|
27
|
+
end
|
28
|
+
|
29
|
+
has_crud_actions adds index, show, new, edit, create, update and destroy actions to controller
|
30
|
+
|
31
|
+
## Explicit action specification
|
32
|
+
|
33
|
+
class UsersController < ApplicationController
|
34
|
+
has_crud_actions only: [:index, :new, :create]
|
35
|
+
end
|
36
|
+
|
37
|
+
or
|
38
|
+
|
39
|
+
class UsersController < ApplicationController
|
40
|
+
has_crud_actions except: :destroy
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
## Nested resources support
|
45
|
+
|
46
|
+
class ProfilesController < ApplicationController
|
47
|
+
has_crud_actions
|
48
|
+
nested_of User
|
49
|
+
end
|
50
|
+
|
51
|
+
If has more than one nested level should use
|
52
|
+
|
53
|
+
class StreetsController < ApplicationController
|
54
|
+
has_crud_actions
|
55
|
+
nested_of Country
|
56
|
+
nested_of City
|
57
|
+
end
|
58
|
+
|
59
|
+
## Redefining responder
|
60
|
+
|
61
|
+
class LanguagesController < ApplicationController
|
62
|
+
has_crud_actions
|
63
|
+
|
64
|
+
respond_to_create :html do
|
65
|
+
redirect_to action: :index
|
66
|
+
end
|
67
|
+
|
68
|
+
respond_to_update do |format|
|
69
|
+
format.html { redirect_to action: :index }
|
70
|
+
format.json { render json: @language }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
1. Fork it
|
77
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
78
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
79
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
80
|
+
5. Create new Pull Request
|
data/dynamic_controller.gemspec
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "dynamic_controller/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = 'dynamic_controller'
|
7
|
-
s.version = DynamicController::VERSION
|
8
|
-
s.authors = ['Gabriel Naiman']
|
9
|
-
s.email = ['gabynaiman@gmail.com']
|
10
|
-
s.homepage = 'https://github.com/gabynaiman/dynamic_controller'
|
11
|
-
s.summary = 'Simple way to add CRUD actions into Rails controllers'
|
12
|
-
s.description = 'Simple way to add CRUD actions into Rails controllers'
|
13
|
-
|
14
|
-
s.files = `git ls-files`.split("\n")
|
15
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
-
s.require_paths = ["lib"]
|
18
|
-
|
19
|
-
s.add_dependency 'ransack'
|
20
|
-
s.add_dependency 'kaminari'
|
21
|
-
s.add_dependency 'nql', '0.0.4'
|
22
|
-
|
23
|
-
s.add_development_dependency 'rails'
|
24
|
-
s.add_development_dependency 'sqlite3'
|
25
|
-
s.add_development_dependency 'rspec-rails'
|
26
|
-
s.add_development_dependency 'factory_girl_rails'
|
27
|
-
end
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dynamic_controller/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'dynamic_controller'
|
7
|
+
s.version = DynamicController::VERSION
|
8
|
+
s.authors = ['Gabriel Naiman']
|
9
|
+
s.email = ['gabynaiman@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/gabynaiman/dynamic_controller'
|
11
|
+
s.summary = 'Simple way to add CRUD actions into Rails controllers'
|
12
|
+
s.description = 'Simple way to add CRUD actions into Rails controllers'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency 'ransack'
|
20
|
+
s.add_dependency 'kaminari'
|
21
|
+
s.add_dependency 'nql', '0.0.4'
|
22
|
+
|
23
|
+
s.add_development_dependency 'rails'
|
24
|
+
s.add_development_dependency 'sqlite3'
|
25
|
+
s.add_development_dependency 'rspec-rails'
|
26
|
+
s.add_development_dependency 'factory_girl_rails'
|
27
|
+
end
|
@@ -1,17 +1,19 @@
|
|
1
|
-
module DynamicController
|
2
|
-
module ActionControllerExtension
|
3
|
-
|
4
|
-
def has_crud_actions(options={})
|
5
|
-
@resource_options = Hash[options.map { |k, v| [:only, :except].include?(k.to_sym) ? [k, [v].flatten.map(&:to_sym)] : [k, v] }].reverse_merge(only: DynamicController::ACTIONS, except: [])
|
6
|
-
send :extend, ClassMethods
|
7
|
-
send :include, InstanceMethods
|
8
|
-
send :include, HelperMethods
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
1
|
+
module DynamicController
|
2
|
+
module ActionControllerExtension
|
3
|
+
|
4
|
+
def has_crud_actions(options={})
|
5
|
+
@resource_options = Hash[options.map { |k, v| [:only, :except].include?(k.to_sym) ? [k, [v].flatten.map(&:to_sym)] : [k, v] }].reverse_merge(only: DynamicController::ACTIONS, except: [])
|
6
|
+
send :extend, ClassMethods
|
7
|
+
send :include, InstanceMethods
|
8
|
+
send :include, HelperMethods
|
9
|
+
|
10
|
+
helper_method :search_query
|
11
|
+
end
|
12
|
+
|
13
|
+
def nested_of(resource_class)
|
14
|
+
before_filter :load_parent_models if parent_resources.empty?
|
15
|
+
parent_resources << Resource.new(resource_class: resource_class)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
17
19
|
end
|
@@ -1,42 +1,42 @@
|
|
1
|
-
module DynamicController
|
2
|
-
module ClassMethods
|
3
|
-
|
4
|
-
def parent_resources
|
5
|
-
@parent_resources ||= []
|
6
|
-
end
|
7
|
-
|
8
|
-
def include_action?(action_name)
|
9
|
-
(@resource_options[:only] - @resource_options[:except]).include?(action_name)
|
10
|
-
end
|
11
|
-
|
12
|
-
def redefined_responders
|
13
|
-
@redefined_responders ||= {}
|
14
|
-
end
|
15
|
-
|
16
|
-
def redefined_responder_to(action, format=nil)
|
17
|
-
redefined_responders[redefined_responder_key(action, format)]
|
18
|
-
end
|
19
|
-
|
20
|
-
def redefined_responder_to?(action, format=nil)
|
21
|
-
redefined_responders.has_key? redefined_responder_key(action, format)
|
22
|
-
end
|
23
|
-
|
24
|
-
def responder_formats
|
25
|
-
@responder_formats ||= [:html, :json]
|
26
|
-
end
|
27
|
-
|
28
|
-
DynamicController::ACTIONS.each do |action|
|
29
|
-
define_method "respond_to_#{action}" do |format=nil, &block|
|
30
|
-
responder_formats << format if format and !responder_formats.include?(format)
|
31
|
-
redefined_responders[redefined_responder_key(action, format)] = block
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def redefined_responder_key(action, format=nil)
|
38
|
-
[action, format].compact.join('_').to_sym
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
1
|
+
module DynamicController
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
def parent_resources
|
5
|
+
@parent_resources ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def include_action?(action_name)
|
9
|
+
(@resource_options[:only] - @resource_options[:except]).include?(action_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def redefined_responders
|
13
|
+
@redefined_responders ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def redefined_responder_to(action, format=nil)
|
17
|
+
redefined_responders[redefined_responder_key(action, format)]
|
18
|
+
end
|
19
|
+
|
20
|
+
def redefined_responder_to?(action, format=nil)
|
21
|
+
redefined_responders.has_key? redefined_responder_key(action, format)
|
22
|
+
end
|
23
|
+
|
24
|
+
def responder_formats
|
25
|
+
@responder_formats ||= [:html, :json]
|
26
|
+
end
|
27
|
+
|
28
|
+
DynamicController::ACTIONS.each do |action|
|
29
|
+
define_method "respond_to_#{action}" do |format=nil, &block|
|
30
|
+
responder_formats << format if format and !responder_formats.include?(format)
|
31
|
+
redefined_responders[redefined_responder_key(action, format)] = block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def redefined_responder_key(action, format=nil)
|
38
|
+
[action, format].compact.join('_').to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
42
|
end
|
@@ -1,92 +1,104 @@
|
|
1
|
-
module DynamicController
|
2
|
-
module HelperMethods
|
3
|
-
|
4
|
-
def resource_class
|
5
|
-
@resource_class ||= (resource_namespace.to_s.split('::') << controller_name.classify).join('::').constantize
|
6
|
-
end
|
7
|
-
|
8
|
-
def resource_namespace
|
9
|
-
self.class.to_s.deconstantize.constantize
|
10
|
-
end
|
11
|
-
|
12
|
-
def collection=(value)
|
13
|
-
instance_variable_set("@#{controller_name}", value)
|
14
|
-
end
|
15
|
-
|
16
|
-
def collection
|
17
|
-
instance_variable_get("@#{controller_name}")
|
18
|
-
end
|
19
|
-
|
20
|
-
def model=(value)
|
21
|
-
instance_variable_set("@#{controller_name.singularize}", value)
|
22
|
-
end
|
23
|
-
|
24
|
-
def model
|
25
|
-
instance_variable_get("@#{controller_name.singularize}")
|
26
|
-
end
|
27
|
-
|
28
|
-
def load_parent_models
|
29
|
-
parent = nil
|
30
|
-
self.class.parent_resources.each do |resource|
|
31
|
-
if parent
|
32
|
-
parent = parent.send(resource.children_name).find(params[resource.param_name])
|
33
|
-
else
|
34
|
-
parent = resource.find params[resource.param_name]
|
35
|
-
end
|
36
|
-
instance_variable_set resource.instance_variable_name, parent
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def parent_models
|
41
|
-
return nil if self.class.parent_resources.empty?
|
42
|
-
self.class.parent_resources.map do |resource|
|
43
|
-
instance_variable_get resource.instance_variable_name
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def parent_model
|
48
|
-
(parent_models || []).last
|
49
|
-
end
|
50
|
-
|
51
|
-
def handle_error(error)
|
52
|
-
respond_to do |format|
|
53
|
-
format.html { raise error }
|
54
|
-
format.json { render json: error.message, status: :internal_server_error }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def model_extended_attributes
|
59
|
-
resource_class.column_names | resource_class.reflections.map { |k, v| v.klass.column_names.map { |c| "#{k}_#{c}" } }.flatten
|
60
|
-
end
|
61
|
-
|
62
|
-
def search_query
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
true
|
89
|
-
|
90
|
-
|
91
|
-
|
1
|
+
module DynamicController
|
2
|
+
module HelperMethods
|
3
|
+
|
4
|
+
def resource_class
|
5
|
+
@resource_class ||= (resource_namespace.to_s.split('::') << controller_name.classify).join('::').constantize
|
6
|
+
end
|
7
|
+
|
8
|
+
def resource_namespace
|
9
|
+
self.class.to_s.deconstantize.constantize
|
10
|
+
end
|
11
|
+
|
12
|
+
def collection=(value)
|
13
|
+
instance_variable_set("@#{controller_name}", value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def collection
|
17
|
+
instance_variable_get("@#{controller_name}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def model=(value)
|
21
|
+
instance_variable_set("@#{controller_name.singularize}", value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def model
|
25
|
+
instance_variable_get("@#{controller_name.singularize}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_parent_models
|
29
|
+
parent = nil
|
30
|
+
self.class.parent_resources.each do |resource|
|
31
|
+
if parent
|
32
|
+
parent = parent.send(resource.children_name).find(params[resource.param_name])
|
33
|
+
else
|
34
|
+
parent = resource.find params[resource.param_name]
|
35
|
+
end
|
36
|
+
instance_variable_set resource.instance_variable_name, parent
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parent_models
|
41
|
+
return nil if self.class.parent_resources.empty?
|
42
|
+
self.class.parent_resources.map do |resource|
|
43
|
+
instance_variable_get resource.instance_variable_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parent_model
|
48
|
+
(parent_models || []).last
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_error(error)
|
52
|
+
respond_to do |format|
|
53
|
+
format.html { raise error }
|
54
|
+
format.json { render json: error.message, status: :internal_server_error }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_extended_attributes
|
59
|
+
resource_class.column_names | resource_class.reflections.map { |k, v| v.klass.column_names.map { |c| "#{k}_#{c}" } }.flatten
|
60
|
+
end
|
61
|
+
|
62
|
+
def search_query
|
63
|
+
return @search_query if @search_query
|
64
|
+
|
65
|
+
query_key = "query_#{params[:controller]}_#{params[:action]}"
|
66
|
+
|
67
|
+
@search_query = if params.has_key?(:q)
|
68
|
+
session[query_key] = params[:q]
|
69
|
+
else
|
70
|
+
session[query_key]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def ransack_query
|
75
|
+
search_query.is_a?(String) ? NQL.to_ransack(search_query) : search_query
|
76
|
+
end
|
77
|
+
|
78
|
+
def search_query_valid?
|
79
|
+
begin
|
80
|
+
search_node_valid? ransack_query, model_extended_attributes
|
81
|
+
rescue NQL::InvalidExpressionError => ex
|
82
|
+
Rails.logger.debug "Invalid search query: #{params[:q]} | Error: #{ex.message}"
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def search_node_valid?(node, valid_attributes)
|
88
|
+
return true unless node
|
89
|
+
node.deep_symbolize_keys.each do |k, v|
|
90
|
+
if k == :a
|
91
|
+
return false unless valid_attributes.include?(v['0'.to_sym][:name])
|
92
|
+
else
|
93
|
+
if v.is_a?(Hash)
|
94
|
+
return false unless search_node_valid?(v, valid_attributes)
|
95
|
+
elsif v.is_a?(Array)
|
96
|
+
v.select { |e| e.is_a?(Hash) }.each { |e| return false unless search_node_valid?(e, valid_attributes) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
92
104
|
end
|