Class: Smooth::Command

Inherits:
Mutations::Command
  • Object
show all
Includes:
Instrumented
Defined in:
lib/smooth/command.rb,
lib/smooth/command/run_proxy.rb,
lib/smooth/command/instrumented.rb

Direct Known Subclasses

Query

Defined Under Namespace

Modules: Instrumented Classes: AsyncWorker, RunProxy

Constant Summary collapse

Patterns =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Instrumented

included, #run_with_instrumentation, #run_with_outcome

Instance Attribute Details

#current_userObject

Commands are aware of who is running them



23
24
25
# File 'lib/smooth/command.rb', line 23

def current_user
  @current_user
end

Class Method Details

.as(current_user) ⇒ Object



4
5
6
7
# File 'lib/smooth/command.rb', line 4

def self.as(current_user)
  require 'smooth/command/run_proxy' unless defined?(RunProxy)
  RunProxy.new(current_user, self)
end

.base_scopeObject



32
33
34
# File 'lib/smooth/command.rb', line 32

def self.base_scope
  @base_scope || :all
end

.belongs_to_resource(resource) ⇒ Object



44
45
46
# File 'lib/smooth/command.rb', line 44

def self.belongs_to_resource(resource)
  self.parent_resource = resource
end

.configure(dsl_config_object, resource = nil) ⇒ Object

DSL Hooks



124
125
126
127
128
129
130
131
132
133
# File 'lib/smooth/command.rb', line 124

def self.configure(dsl_config_object, resource = nil)
  resource ||= Smooth.current_resource
  klass = define_or_open(dsl_config_object, resource)

  Array(dsl_config_object.blocks).each do |blk|
    klass.class_eval(&blk)
  end

  klass
end

.define_or_open(options, resource) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/smooth/command.rb', line 135

def self.define_or_open(options, resource)
  resource_name = resource.name.to_s.singularize
  base          = Smooth.command

  name = options.name.to_s.camelize
  klass = "#{ name }#{ resource.model_class }".gsub(/\s+/, '')

  apply_options = lambda do |k|
    k.model_class     ||= resource.model_class if resource.model_class

    k.belongs_to_resource(resource)

    k.resource_name   = resource.name.to_s
    k.command_action  = options.name.to_s
  end

  if command_klass = Object.const_get(klass) rescue nil
    return command_klass.tap(&apply_options)
  end

  parent_klass = Class.new(base)

  begin
    Object.const_set(klass, parent_klass).tap(&apply_options)
  rescue => ex
    puts ex.message
    puts "Error setting #{ klass } #{ base }. klass is a #{ klass.class }"
  end

  parent_klass
end

.event_namespaceObject



79
80
81
# File 'lib/smooth/command.rb', line 79

def self.event_namespace
  @event_namespace || "#{ command_action }.#{ resource_alias }".downcase
end

.execute(execution_pattern = nil, &block) ⇒ Object

Allows for defining common execution pattern methods mostly for standard CRUD against scoped models



267
268
269
# File 'lib/smooth/command.rb', line 267

def self.execute(execution_pattern = nil, &block)
  send :define_method, :execute, (block || get_execution_pattern(execution_pattern))
end

.filter_for_param(param) ⇒ Object



217
218
219
# File 'lib/smooth/command.rb', line 217

def self.filter_for_param(param)
  optional_inputs[param] || required_inputs[param]
end

.filter_options_for_param(param) ⇒ Object



221
222
223
# File 'lib/smooth/command.rb', line 221

def self.filter_options_for_param(param)
  filter_for_param(param).try(:options)
end

.find_serializer_for(_request_object) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/smooth/command.rb', line 248

def self.find_serializer_for(_request_object)
  resource = Smooth.resource(resource_name)
  resource ||= Smooth.resource("#{ resource_name }".pluralize)

  # TODO
  # We can make the preferred format something you can
  # specify via a header or parameter.  We can also restrict
  # certain serializers from certain policies.
  preferred_format = :default

  resource.fetch(:serializer, preferred_format)
end

.get_execution_pattern(pattern_name) ⇒ Object



295
296
297
298
299
300
301
302
303
# File 'lib/smooth/command.rb', line 295

def self.get_execution_pattern(pattern_name)
  if respond_to?("#{ pattern_name }_execution_pattern")
    return method("#{ pattern_name }_execution_pattern").to_proc
  elsif Patterns.key?(pattern_name.to_sym)
    Patterns.fetch(pattern_name.to_sym)
  else
    return method(:execute).to_proc
  end
end

.input_argument_namesObject



18
19
20
# File 'lib/smooth/command.rb', line 18

def self.input_argument_names
  required_inputs.keys + optional_inputs.keys
end

.interface(*args, &block) ⇒ Object



14
15
16
# File 'lib/smooth/command.rb', line 14

def self.interface(*args, &block)
  send(:required, *args, &block)
end

.interface_descriptionObject



173
174
175
# File 'lib/smooth/command.rb', line 173

def self.interface_description
  interface_documentation
end

.interface_documentationObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/smooth/command.rb', line 177

def self.interface_documentation
  optional_inputs = input_filters.optional_inputs
  required_inputs = input_filters.required_inputs

  data = {
    required: required_inputs.keys,
    optional: optional_inputs.keys,
    filters: {}
  }

  blk = lambda do |memo, parts, required|
    key, filter = parts

    type        = filter.class.name[/^Mutations::([a-zA-Z]*)Filter$/, 1].underscore
    options     = filter.options.merge(required: required)

    value = memo[key] = {
      type: type,
      options: options.reject { |_k, v| v.nil? },
      description: input_descriptions[key]
    }

    if options[:faker]
      value[:example] = Smooth.faker(options[:faker])
    end

    memo
  end

  required_inputs.reduce(data[:filters]) do |memo, parts|
    blk.call(memo, parts, true)
  end

  optional_inputs.reduce(data[:filters]) do |memo, parts|
    blk.call(memo, parts, false)
  end

  data.to_mash
end

.object_pathObject



97
98
99
# File 'lib/smooth/command.rb', line 97

def self.object_path
  resource_name.downcase + '.' + command_action
end

.params(*args, &block) ⇒ Object

DSL Improvements English



10
11
12
# File 'lib/smooth/command.rb', line 10

def self.params(*args, &block)
  send(:required, *args, &block)
end

.parent_apiObject



48
49
50
# File 'lib/smooth/command.rb', line 48

def self.parent_api
  parent_resource.api
end

.resource_aliasObject



83
84
85
# File 'lib/smooth/command.rb', line 83

def self.resource_alias
  resource_name.singularize.underscore
end

.resource_nameObject



87
88
89
90
91
92
93
94
95
# File 'lib/smooth/command.rb', line 87

def self.resource_name
  value = @resource_name.to_s

  if value.empty? && model_class
    value = model_class.to_s
  end

  value
end

.respond_to_request(request_object, options = {}) ⇒ Object

Creates a new instance of the Smooth::Command::Response class in response to a request from the Router. It is assumed that a request object responds to: user, and params



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/smooth/command.rb', line 232

def self.respond_to_request(request_object, options = {})
  klass = self

  outcome = options.fetch(:outcome) do
    klass.as(request_object.user).run(request_object.params)
  end

  response_class.new(outcome, serializer_options).tap do |response|
    response.request_headers = request_object.headers
    response.serializer = find_serializer_for(request_object)
    response.event_namespace = event_namespace
    response.command_action = command_action
    response.current_user = request_object.user
  end
end

.response_classObject



225
226
227
# File 'lib/smooth/command.rb', line 225

def self.response_class
  Smooth::Response
end

.scope(setting = nil) ⇒ Object



74
75
76
77
# File 'lib/smooth/command.rb', line 74

def self.scope(setting = nil)
  self.base_scope = setting if setting
  base_scope || :all
end

.serializer_optionsObject



261
262
263
# File 'lib/smooth/command.rb', line 261

def self.serializer_options
  {}
end

Instance Method Details

#event_namespaceObject



101
102
103
# File 'lib/smooth/command.rb', line 101

def event_namespace
  self.class.event_namespace
end

#interface_for(filter) ⇒ Object

Interface Documentation



169
170
171
# File 'lib/smooth/command.rb', line 169

def interface_for(filter)
  self.class.interface_description.filters.send(filter)
end

#model_classObject



117
118
119
# File 'lib/smooth/command.rb', line 117

def model_class
  self.class.model_class
end

#object_pathObject



105
106
107
# File 'lib/smooth/command.rb', line 105

def object_path
  self.class.object_path
end

#parent_apiObject



36
37
38
# File 'lib/smooth/command.rb', line 36

def parent_api
  self.class.parent_api
end

#parent_resourceObject



40
41
42
# File 'lib/smooth/command.rb', line 40

def parent_resource
  self.class.parent_resource
end

#resource_aliasObject



113
114
115
# File 'lib/smooth/command.rb', line 113

def resource_alias
  self.class.resource_alias
end

#resource_nameObject



109
110
111
# File 'lib/smooth/command.rb', line 109

def resource_name
  self.class.resource_name
end

#scope(*args) ⇒ Object

Returns the model scope for this command. If a scope method is set on this command, it will make sure to scope the model by that method. It will pass whatever arguments you pass to scope to the scope method. if you pass no args, and the scope requires one, we will assume the user wants us to pass the current user of the command



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/smooth/command.rb', line 57

def scope(*args)
  @scope ||= begin
               meth = model_class.send(:method, self.class.base_scope)

               if meth.arity.abs >= 1
                 args.push(current_user) if args.empty?
                 model_class.send(self.class.base_scope, *args)
               else
                 model_class.send(self.class.base_scope)
               end
             end
end

#scope=(new_scope) ⇒ Object



70
71
72
# File 'lib/smooth/command.rb', line 70

def scope=(new_scope)
  @scope = new_scope || scope
end

#scoped_find_objectObject



287
288
289
290
291
292
293
# File 'lib/smooth/command.rb', line 287

def scoped_find_object
  @scoped_find ||= if scope.respond_to?(:find) && found = (scope.find(id) rescue nil)
                     found
  else
    add_error(:id, :not_found, "could not find a #{ resource_name } model with that id")
  end
end