#
# Copyright (c) 2006-2021 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of ronin.
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin. If not, see .
#
require 'ronin/ui/cli/command'
require 'ronin/database'
module Ronin
module UI
module CLI
#
# A base-command for querying {Model}s.
#
class ModelCommand < Command
option :database, :type => URI,
:flag => '-D',
:description => 'The Database URI'
#
# The query options for the command.
#
# @return [Array]
# The query options and their query method names.
#
# @since 1.0.0
#
# @api semipublic
#
def self.query_options
@query_options ||= []
end
#
# Iterates over the query options for the command.
#
# @yield [name]
# The given block will be passed the names of the query options.
#
# @yieldparam [Symbol] name
# The name of a query option.
#
# @return [Enumerator]
# If no block is given, an Enumerator object will be returned.
#
# @since 1.1.0
#
def self.each_query_option(&block)
return enum_for(__method__) unless block
self.class.ancestors.each do |ancestor|
if ancestor < ModelCommand
ancestor.query_options.each(&block)
end
end
end
protected
#
# Sets or gets the model to query.
#
# @param [DataMapper::Resource, nil] model
# The model class.
#
# @return [DataMapper::Resource]
# The model that will be queried.
#
# @since 1.1.0
#
# @api semipublic
#
def self.model(model=nil)
if model
@model = model
else
@model ||= if superclass < ModelCommand
superclass.model
end
end
end
#
# Defines a query option for the command.
#
# @param [Symbol] name
# The option name.
#
# @param [Hash] options
# Additional options.
#
# @since 1.0.0
#
# @api semipublic
#
def self.query_option(name,options={})
query_options << name
return option(name,options)
end
#
# Sets up the {Database}.
#
# @since 1.1.0
#
# @api semipublic
#
def setup
super
if @database
Database.repositories[:default] = @database
end
Database.setup
end
#
# Builds a new query using the options of the command.
#
# @return [DataMapper::Collection]
# The new query.
#
# @raise [RuntimeError]
# The query option does not map to a query-method or property
# defined in the Model.
#
# @since 1.0.0
#
# @api semipublic
#
def query
unless self.class.model
raise("query model not defined for #{self.class}")
end
query = self.class.model.all
self.class.each_query_option do |name|
value = get_param(name).value
# skip unset options
next if value.nil?
if model.properties.named?(name)
query = query.all(name => value)
elsif model.respond_to?(name)
query_method = model.method(name)
query = case query_method.arity
when 0 then query.send(name)
when 1 then query.send(name,value)
else query.send(name,*value)
end
else
raise("unknown query method or property #{name} for #{model}")
end
end
return query
end
end
end
end
end