api_maker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +476 -0
  4. data/Rakefile +27 -0
  5. data/app/channels/api_maker/subscriptions_channel.rb +80 -0
  6. data/app/controllers/api_maker/base_controller.rb +32 -0
  7. data/app/controllers/api_maker/commands_controller.rb +26 -0
  8. data/app/controllers/api_maker/devise_controller.rb +60 -0
  9. data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
  10. data/app/services/api_maker/application_service.rb +7 -0
  11. data/app/services/api_maker/collection_command_service.rb +24 -0
  12. data/app/services/api_maker/command_response.rb +67 -0
  13. data/app/services/api_maker/command_service.rb +31 -0
  14. data/app/services/api_maker/create_command.rb +62 -0
  15. data/app/services/api_maker/create_command_service.rb +18 -0
  16. data/app/services/api_maker/destroy_command.rb +39 -0
  17. data/app/services/api_maker/destroy_command_service.rb +22 -0
  18. data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
  19. data/app/services/api_maker/index_command.rb +96 -0
  20. data/app/services/api_maker/index_command_service.rb +22 -0
  21. data/app/services/api_maker/js_method_namer_service.rb +11 -0
  22. data/app/services/api_maker/member_command_service.rb +25 -0
  23. data/app/services/api_maker/model_content_generator_service.rb +108 -0
  24. data/app/services/api_maker/models_finder_service.rb +22 -0
  25. data/app/services/api_maker/models_generator_service.rb +104 -0
  26. data/app/services/api_maker/update_command.rb +43 -0
  27. data/app/services/api_maker/update_command_service.rb +21 -0
  28. data/app/services/api_maker/valid_command.rb +35 -0
  29. data/app/services/api_maker/valid_command_service.rb +21 -0
  30. data/app/views/api_maker/_data.html.erb +15 -0
  31. data/config/rails_best_practices.yml +55 -0
  32. data/config/routes.rb +7 -0
  33. data/lib/api_maker.rb +36 -0
  34. data/lib/api_maker/ability.rb +39 -0
  35. data/lib/api_maker/ability_loader.rb +21 -0
  36. data/lib/api_maker/action_controller_base_extensions.rb +5 -0
  37. data/lib/api_maker/base_command.rb +81 -0
  38. data/lib/api_maker/base_resource.rb +78 -0
  39. data/lib/api_maker/collection_serializer.rb +69 -0
  40. data/lib/api_maker/command_spec_helper.rb +57 -0
  41. data/lib/api_maker/configuration.rb +34 -0
  42. data/lib/api_maker/engine.rb +5 -0
  43. data/lib/api_maker/individual_command.rb +37 -0
  44. data/lib/api_maker/javascript/api.js +92 -0
  45. data/lib/api_maker/javascript/base-model.js +543 -0
  46. data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
  47. data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
  48. data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
  49. data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
  50. data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
  51. data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
  52. data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
  53. data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
  54. data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
  55. data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
  56. data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
  57. data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
  58. data/lib/api_maker/javascript/cable-subscription.js +33 -0
  59. data/lib/api_maker/javascript/collection.js +186 -0
  60. data/lib/api_maker/javascript/commands-pool.js +123 -0
  61. data/lib/api_maker/javascript/custom-error.js +14 -0
  62. data/lib/api_maker/javascript/deserializer.js +35 -0
  63. data/lib/api_maker/javascript/devise.js.erb +113 -0
  64. data/lib/api_maker/javascript/error-logger.js +119 -0
  65. data/lib/api_maker/javascript/event-connection.jsx +24 -0
  66. data/lib/api_maker/javascript/event-created.jsx +26 -0
  67. data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
  68. data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
  69. data/lib/api_maker/javascript/event-listener.jsx +41 -0
  70. data/lib/api_maker/javascript/event-updated.jsx +26 -0
  71. data/lib/api_maker/javascript/form-data-to-object.js +70 -0
  72. data/lib/api_maker/javascript/included.js +39 -0
  73. data/lib/api_maker/javascript/key-value-store.js +47 -0
  74. data/lib/api_maker/javascript/logger.js +23 -0
  75. data/lib/api_maker/javascript/model-name.js +21 -0
  76. data/lib/api_maker/javascript/model-template.js.erb +110 -0
  77. data/lib/api_maker/javascript/models-response-reader.js +43 -0
  78. data/lib/api_maker/javascript/paginate.jsx +128 -0
  79. data/lib/api_maker/javascript/params.js +68 -0
  80. data/lib/api_maker/javascript/resource-route.jsx +75 -0
  81. data/lib/api_maker/javascript/resource-routes.jsx +36 -0
  82. data/lib/api_maker/javascript/result.js +25 -0
  83. data/lib/api_maker/javascript/session-status-updater.js +113 -0
  84. data/lib/api_maker/javascript/sort-link.jsx +88 -0
  85. data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
  86. data/lib/api_maker/loader.rb +14 -0
  87. data/lib/api_maker/memory_storage.rb +65 -0
  88. data/lib/api_maker/model_extensions.rb +96 -0
  89. data/lib/api_maker/permitted_params_argument.rb +12 -0
  90. data/lib/api_maker/preloader.rb +91 -0
  91. data/lib/api_maker/preloader_belongs_to.rb +58 -0
  92. data/lib/api_maker/preloader_has_many.rb +69 -0
  93. data/lib/api_maker/preloader_has_one.rb +70 -0
  94. data/lib/api_maker/preloader_through.rb +101 -0
  95. data/lib/api_maker/railtie.rb +14 -0
  96. data/lib/api_maker/relationship_includer.rb +42 -0
  97. data/lib/api_maker/resource_routing.rb +8 -0
  98. data/lib/api_maker/result_parser.rb +50 -0
  99. data/lib/api_maker/serializer.rb +86 -0
  100. data/lib/api_maker/spec_helper.rb +100 -0
  101. data/lib/api_maker/version.rb +3 -0
  102. data/lib/tasks/api_maker_tasks.rake +5 -0
  103. metadata +581 -0
@@ -0,0 +1,43 @@
1
+ class ApiMaker::UpdateCommand < ApiMaker::BaseCommand
2
+ attr_reader :command, :model, :params, :serializer
3
+
4
+ def execute!
5
+ each_command do |command|
6
+ @command = command
7
+ @model = command.model
8
+ @params = command.args || {}
9
+ @serializer = serialized_resource(model)
10
+
11
+ if command.model.update(sanitize_parameters)
12
+ success_response
13
+ else
14
+ failure_response
15
+ end
16
+ end
17
+
18
+ ServicePattern::Response.new(success: true)
19
+ end
20
+
21
+ def failure_response
22
+ command.fail(
23
+ model: serializer.result,
24
+ success: false,
25
+ errors: model.errors.full_messages
26
+ )
27
+ end
28
+
29
+ def sanitize_parameters
30
+ serializer.resource_instance.permitted_params(ApiMaker::PermittedParamsArgument.new(command: command, model: model))
31
+ end
32
+
33
+ def serialized_resource(model)
34
+ ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
35
+ end
36
+
37
+ def success_response
38
+ command.result(
39
+ model: serializer.result,
40
+ success: true
41
+ )
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ class ApiMaker::UpdateCommandService < ApiMaker::CommandService
2
+ def execute
3
+ ApiMaker::UpdateCommand.execute_in_thread!(
4
+ ability: ability,
5
+ args: args,
6
+ collection: collection,
7
+ commands: commands,
8
+ command_response: command_response,
9
+ controller: controller
10
+ )
11
+ ServicePattern::Response.new(success: true)
12
+ end
13
+
14
+ def collection
15
+ @collection ||= model_class.accessible_by(@ability, :update).where(model_class.primary_key => ids)
16
+ end
17
+
18
+ def ids
19
+ @commands.values.map { |command| command.fetch("primary_key") }
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ class ApiMaker::ValidCommand < ApiMaker::BaseCommand
2
+ attr_reader :command, :model, :params, :serializer
3
+
4
+ def execute!
5
+ each_command do |command|
6
+ @command = command
7
+ @params = command.args || {}
8
+
9
+ if command.model_id.present?
10
+ model = resource_instance_class.find(command.model_id)
11
+ else
12
+ model = resource_instance_class.new
13
+ end
14
+
15
+ serializer = serialized_resource(model)
16
+ model.assign_attributes(sanitize_parameters(serializer))
17
+
18
+ command.result(valid: model.valid?, errors: model.errors.full_messages)
19
+ end
20
+
21
+ ServicePattern::Response.new(success: true)
22
+ end
23
+
24
+ def resource_instance_class
25
+ collection.klass
26
+ end
27
+
28
+ def sanitize_parameters(serializer)
29
+ serializer.resource_instance.permitted_params(ApiMaker::PermittedParamsArgument.new(command: command, model: serializer.model))
30
+ end
31
+
32
+ def serialized_resource(model)
33
+ ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ class ApiMaker::ValidCommandService < ApiMaker::CommandService
2
+ def execute
3
+ ApiMaker::ValidCommand.execute_in_thread!(
4
+ ability: ability,
5
+ args: args,
6
+ collection: collection,
7
+ commands: commands,
8
+ command_response: command_response,
9
+ controller: controller
10
+ )
11
+ ServicePattern::Response.new(success: true)
12
+ end
13
+
14
+ def collection
15
+ @collection ||= model_class.accessible_by(@ability, :valid).where(model_class.primary_key => ids)
16
+ end
17
+
18
+ def ids
19
+ @commands.values.map { |command| command.fetch("primary_key") }
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ <div class="api-maker-data"<%
2
+
3
+ Devise.mappings.each do |scope|
4
+ model = __send__("current_#{scope[0]}")
5
+ next unless model
6
+
7
+ resource_class = ApiMaker::Serializer.resource_for(model.class)
8
+ next unless resource_class
9
+
10
+ serializer = ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model) if model
11
+
12
+ %> data-current-<%= scope[0].to_s.dasherize %>="<%= model ? serializer.to_json : null %>"<%
13
+ end
14
+
15
+ %>></div>
@@ -0,0 +1,55 @@
1
+ AddModelVirtualAttributeCheck: { }
2
+ AlwaysAddDbIndexCheck: { }
3
+ #CheckSaveReturnValueCheck: { }
4
+ #CheckDestroyReturnValueCheck: { }
5
+ DefaultScopeIsEvilCheck: { }
6
+ DryBundlerInCapistranoCheck: { }
7
+ #HashSyntaxCheck: { }
8
+ IsolateSeedDataCheck: { }
9
+ KeepFindersOnTheirOwnModelCheck: { }
10
+ LawOfDemeterCheck: { }
11
+ #LongLineCheck: { max_line_length: 80 }
12
+ MoveCodeIntoControllerCheck: { }
13
+ MoveCodeIntoHelperCheck: { array_count: 3 }
14
+ MoveCodeIntoModelCheck: { use_count: 2 }
15
+ MoveFinderToNamedScopeCheck: { }
16
+ #MoveModelLogicIntoModelCheck: { use_count: 4 }
17
+ NeedlessDeepNestingCheck: { nested_count: 2 }
18
+ NotRescueExceptionCheck: { }
19
+ NotUseDefaultRouteCheck: { }
20
+ NotUseTimeAgoInWordsCheck: { }
21
+ OveruseRouteCustomizationsCheck: { customize_count: 3 }
22
+ ProtectMassAssignmentCheck: { }
23
+ RemoveEmptyHelpersCheck: { }
24
+ #RemoveTabCheck: { }
25
+ RemoveTrailingWhitespaceCheck: { }
26
+ RemoveUnusedMethodsInControllersCheck: {
27
+ except_methods: [
28
+ "ApiMaker::BaseController#current_ability",
29
+ "ApiMaker::CommandsController#create",
30
+ "ApiMaker::DeviseController#do_sign_in",
31
+ "ApiMaker::DeviseController#do_sign_out",
32
+ "ApiMaker::MemberActionsController#create",
33
+ "ApiMaker::ModelController#index",
34
+ "ApiMaker::ModelController#show",
35
+ "ApiMaker::ModelController#create",
36
+ "ApiMaker::ModelController#edit",
37
+ "ApiMaker::ModelController#validate"
38
+ ]
39
+ }
40
+ RemoveUnusedMethodsInHelpersCheck: { except_methods: [] }
41
+ RemoveUnusedMethodsInModelsCheck: { except_methods: [] }
42
+ ReplaceComplexCreationWithFactoryMethodCheck: { attribute_assignment_count: 2 }
43
+ ReplaceInstanceVariableWithLocalVariableCheck: { }
44
+ RestrictAutoGeneratedRoutesCheck: { }
45
+ SimplifyRenderInControllersCheck: { }
46
+ SimplifyRenderInViewsCheck: { }
47
+ #UseBeforeFilterCheck: { customize_count: 2 }
48
+ UseModelAssociationCheck: { }
49
+ UseMultipartAlternativeAsContentTypeOfEmailCheck: { }
50
+ #UseParenthesesInMethodDefCheck: { }
51
+ UseObserverCheck: { }
52
+ UseQueryAttributeCheck: { }
53
+ UseSayWithTimeInMigrationsCheck: { }
54
+ UseScopeAccessCheck: { }
55
+ UseTurboSprocketsRails3Check: { }
@@ -0,0 +1,7 @@
1
+ ApiMaker::Engine.routes.draw do
2
+ post "commands" => "commands#create"
3
+ post "devise/do_sign_in" => "devise#do_sign_in"
4
+ post "devise/do_sign_out" => "devise#do_sign_out"
5
+
6
+ resources :session_statuses, only: :create
7
+ end
@@ -0,0 +1,36 @@
1
+ require "api_maker/engine"
2
+
3
+ require "active_record_query_fixer"
4
+ require "ransack"
5
+ require "service_pattern"
6
+
7
+ require_relative "api_maker/action_controller_base_extensions"
8
+
9
+ module ApiMaker
10
+ dir = "#{__dir__}/api_maker"
11
+
12
+ autoload :Ability, "#{dir}/ability"
13
+ autoload :AbilityLoader, "#{dir}/ability_loader"
14
+ autoload :BaseCommand, "#{dir}/base_command"
15
+ autoload :BaseResource, "#{dir}/base_resource"
16
+ autoload :CollectionSerializer, "#{dir}/collection_serializer"
17
+ autoload :CommandSpecHelper, "#{dir}/command_spec_helper"
18
+ autoload :Configuration, "#{dir}/configuration"
19
+ autoload :IndividualCommand, "#{dir}/individual_command"
20
+ autoload :Loader, "#{dir}/loader"
21
+ autoload :MemoryStorage, "#{dir}/memory_storage"
22
+ autoload :ModelExtensions, "#{dir}/model_extensions"
23
+ autoload :PermittedParamsArgument, "#{dir}/permitted_params_argument"
24
+ autoload :Preloader, "#{dir}/preloader"
25
+ autoload :PreloaderBelongsTo, "#{dir}/preloader_belongs_to"
26
+ autoload :PreloaderHasMany, "#{dir}/preloader_has_many"
27
+ autoload :PreloaderHasOne, "#{dir}/preloader_has_one"
28
+ autoload :PreloaderThrough, "#{dir}/preloader_through"
29
+ autoload :RelationshipIncluder, "#{dir}/relationship_includer"
30
+ autoload :ResourceRouting, "#{dir}/resource_routing"
31
+ autoload :ResultParser, "#{dir}/result_parser"
32
+ autoload :Serializer, "#{dir}/serializer"
33
+ autoload :SpecHelper, "#{dir}/spec_helper"
34
+ end
35
+
36
+ require_relative "api_maker/railtie"
@@ -0,0 +1,39 @@
1
+ class ApiMaker::Ability
2
+ include CanCan::Ability
3
+
4
+ attr_reader :loader
5
+
6
+ def initialize(args: nil)
7
+ @args = args
8
+ @loader = ApiMaker::AbilityLoader.new(ability: self, args: args)
9
+ end
10
+
11
+ # Override methods from CanCan::Ability to first load abilities from the given resource
12
+ def can?(*args)
13
+ subject = args.second
14
+ load_abilities(subject)
15
+ super
16
+ end
17
+
18
+ def model_adapter(*args)
19
+ subject = args.first
20
+ load_abilities(subject)
21
+ super
22
+ end
23
+
24
+ def load_abilities(subject)
25
+ return unless active_record?(subject)
26
+
27
+ if subject.class == Class
28
+ loader.load_model_class(subject)
29
+ elsif subject.class != Class
30
+ loader.load_model_class(subject.class)
31
+ end
32
+ end
33
+
34
+ def active_record?(subject)
35
+ return subject < ActiveRecord::Base if subject.class == Class
36
+
37
+ subject.is_a?(ActiveRecord::Base)
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ class ApiMaker::AbilityLoader
2
+ def initialize(ability:, args:)
3
+ @ability = ability
4
+ @args = args
5
+ @loaded_model_names = {}
6
+ end
7
+
8
+ def load_model_class(model_class)
9
+ return if @loaded_model_names.key?(model_class.name)
10
+
11
+ resource = ApiMaker::MemoryStorage.current.resource_for_model(model_class)
12
+ load_resource(resource)
13
+ end
14
+
15
+ def load_resource(resource)
16
+ return if @loaded_model_names.key?(resource.model_class_name)
17
+
18
+ resource.new(ability: @ability, args: @args, model: nil).abilities
19
+ @loaded_model_names[resource.model_class_name] = true
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ ActionController::Base.__send__(:define_method, :api_maker_args) do
2
+ @api_maker_args ||= {}
3
+ end
4
+
5
+ ActionController::Base.helper_method(:api_maker_args)
@@ -0,0 +1,81 @@
1
+ class ApiMaker::BaseCommand
2
+ attr_reader :api_maker_args, :commands, :command_response, :collection, :controller, :current_ability
3
+
4
+ # Returns true if the gem "goldiloader" is present in the app
5
+ def self.goldiloader?
6
+ @goldiloader = Gem::Specification.find_all_by_name("goldiloader").any? if @goldiloader.nil?
7
+ @goldiloader
8
+ end
9
+
10
+ def initialize(ability:, args:, collection:, commands:, command_response:, controller:)
11
+ @api_maker_args = args
12
+ @current_ability = ability
13
+ @collection = collection
14
+ @commands = commands
15
+ @command_response = command_response
16
+ @controller = controller
17
+
18
+ # Make it possible to do custom preloads (useful in threadded mode that doesnt support Goldiloader)
19
+ @collection = custom_collection(@collection) if respond_to?(:custom_collection)
20
+ end
21
+
22
+ def self.execute_in_thread!(**args)
23
+ args.fetch(:command_response).with_thread do
24
+ new(**args).execute!
25
+ end
26
+ end
27
+
28
+ def each_command(args = {}, &blk)
29
+ if args[:threadded]
30
+ # Goldiloader doesn't work with threads (loads all relationships for each thread)
31
+ @collection = @collection.auto_include(false) if ApiMaker::BaseCommand.goldiloader?
32
+
33
+ # Load relationship before commands so each command doesn't query on its own
34
+ @collection.load
35
+ end
36
+
37
+ @commands.each do |command_id, command_data|
38
+ if args[:threadded]
39
+ command_response.with_thread do
40
+ run_command(command_id, command_data, &blk)
41
+ end
42
+ else
43
+ run_command(command_id, command_data, &blk)
44
+ end
45
+ end
46
+ end
47
+
48
+ delegate :result_for_command, to: :command_response
49
+
50
+ private
51
+
52
+ def run_command(command_id, command_data)
53
+ command = ApiMaker::IndividualCommand.new(
54
+ args: command_data[:args],
55
+ collection: @collection,
56
+ command: self,
57
+ id: command_id,
58
+ primary_key: command_data[:primary_key],
59
+ response: command_response
60
+ )
61
+
62
+ begin
63
+ yield command
64
+ rescue => e # rubocop:disable Style/RescueStandardError
65
+ command.error(success: false, errors: [command_error_message(e)])
66
+
67
+ Rails.logger.error e.message
68
+ Rails.logger.error e.backtrace.join("\n")
69
+
70
+ ApiMaker::Configuration.current.report_error(controller: controller, error: e)
71
+ end
72
+ end
73
+
74
+ def command_error_message(error)
75
+ if Rails.application.config.consider_all_requests_local
76
+ "#{error.class.name}: #{error.message}"
77
+ else
78
+ "Internal server error"
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,78 @@
1
+ class ApiMaker::BaseResource
2
+ attr_reader :ability, :args, :model
3
+
4
+ delegate :can, to: :ability
5
+
6
+ CRUD = [:create, :read, :update, :destroy].freeze
7
+
8
+ def self.attributes(*attributes, **args)
9
+ attributes.each do |attribute|
10
+ ApiMaker::MemoryStorage.current.add(self, :attributes, attribute, args)
11
+ end
12
+ end
13
+
14
+ def self._attributes
15
+ ApiMaker::MemoryStorage.current.storage_for(self, :attributes)
16
+ end
17
+
18
+ def self.collection_commands(*list)
19
+ list.each do |collection_command|
20
+ ApiMaker::MemoryStorage.current.add(self, :collection_commands, collection_command)
21
+ end
22
+ end
23
+
24
+ def self.member_commands(*list)
25
+ list.each do |member_command|
26
+ ApiMaker::MemoryStorage.current.add(self, :member_commands, member_command)
27
+ end
28
+ end
29
+
30
+ def self.model_class=(klass)
31
+ # Set the name to avoid reloading issues with Rails
32
+ @model_class_name ||= klass.name
33
+ ApiMaker::MemoryStorage.current.model_class_for(resource: self, klass: klass)
34
+ end
35
+
36
+ def self.model_class
37
+ # Use the name to constantize to avoid reloading issues with Rails
38
+ model_class_name.constantize
39
+ end
40
+
41
+ def self.model_class_name
42
+ @model_class_name ||= name.gsub(/Resource$/, "").gsub(/^Resources::/, "")
43
+ end
44
+
45
+ def self.relationships(*relationships)
46
+ relationships.each do |relationship|
47
+ ApiMaker::MemoryStorage.current.add(self, :relationships, relationship)
48
+ end
49
+ end
50
+
51
+ def self._relationships
52
+ ApiMaker::MemoryStorage.current.storage_for(self, :relationships)
53
+ end
54
+
55
+ def self.collection_name
56
+ @collection_name ||= plural_name.underscore.dasherize
57
+ end
58
+
59
+ def self.default_select
60
+ _attributes.select do |_attribute_name, args|
61
+ !args.fetch(:args).key?(:selected_by_default) || args.fetch(:args).fetch(:selected_by_default)
62
+ end
63
+ end
64
+
65
+ def self.plural_name
66
+ @plural_name ||= short_name.pluralize
67
+ end
68
+
69
+ def self.short_name
70
+ @short_name ||= name.match(/\AResources::(.+)Resource\Z/)[1]
71
+ end
72
+
73
+ def initialize(ability: nil, args: {}, model:)
74
+ @ability = ability
75
+ @args = args
76
+ @model = model
77
+ end
78
+ end