riveter 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +147 -0
- data/LICENSE.txt +23 -0
- data/README.md +77 -0
- data/Rakefile +7 -0
- data/app/helpers/riveter/command_form_helper.rb +15 -0
- data/app/helpers/riveter/enquiry_form_helper.rb +16 -0
- data/app/helpers/riveter/query_filter_form_helper.rb +16 -0
- data/config/locales/forms.en.yml +25 -0
- data/config/locales/validators.en.yml +26 -0
- data/init.rb +25 -0
- data/lib/generators/erb/TODO +1 -0
- data/lib/generators/haml/command_controller/USAGE +0 -0
- data/lib/generators/haml/command_controller/command_controller_generator.rb +56 -0
- data/lib/generators/haml/command_controller/templates/new.html.haml +26 -0
- data/lib/generators/haml/enquiry_controller/USAGE +0 -0
- data/lib/generators/haml/enquiry_controller/enquiry_controller_generator.rb +56 -0
- data/lib/generators/haml/enquiry_controller/templates/index.html.haml +57 -0
- data/lib/generators/riveter/command/USAGE +0 -0
- data/lib/generators/riveter/command/command_generator.rb +55 -0
- data/lib/generators/riveter/command/templates/command.rb +40 -0
- data/lib/generators/riveter/command/templates/commands.en.yml +57 -0
- data/lib/generators/riveter/command/templates/module.rb +4 -0
- data/lib/generators/riveter/command_controller/USAGE +0 -0
- data/lib/generators/riveter/command_controller/command_controller_generator.rb +40 -0
- data/lib/generators/riveter/command_controller/templates/command_controller.rb +18 -0
- data/lib/generators/riveter/command_controller/templates/module.rb +4 -0
- data/lib/generators/riveter/enquiry/USAGE +0 -0
- data/lib/generators/riveter/enquiry/enquiry_generator.rb +29 -0
- data/lib/generators/riveter/enquiry/templates/enquiry.rb +11 -0
- data/lib/generators/riveter/enquiry/templates/module.rb +4 -0
- data/lib/generators/riveter/enquiry_controller/USAGE +0 -0
- data/lib/generators/riveter/enquiry_controller/enquiry_controller_generator.rb +34 -0
- data/lib/generators/riveter/enquiry_controller/templates/enquiry_controller.rb +9 -0
- data/lib/generators/riveter/enquiry_controller/templates/module.rb +4 -0
- data/lib/generators/riveter/enum/USAGE +41 -0
- data/lib/generators/riveter/enum/enum_generator.rb +51 -0
- data/lib/generators/riveter/enum/templates/enum.rb +17 -0
- data/lib/generators/riveter/enum/templates/enums.en.yml +35 -0
- data/lib/generators/riveter/enum/templates/module.rb +4 -0
- data/lib/generators/riveter/install/USAGE +0 -0
- data/lib/generators/riveter/install/install_generator.rb +14 -0
- data/lib/generators/riveter/presenter/USAGE +0 -0
- data/lib/generators/riveter/presenter/presenter_generator.rb +20 -0
- data/lib/generators/riveter/presenter/templates/module.rb +4 -0
- data/lib/generators/riveter/presenter/templates/presenter.rb +7 -0
- data/lib/generators/riveter/query/USAGE +22 -0
- data/lib/generators/riveter/query/query_generator.rb +20 -0
- data/lib/generators/riveter/query/templates/module.rb +4 -0
- data/lib/generators/riveter/query/templates/query.rb +31 -0
- data/lib/generators/riveter/query_filter/USAGE +0 -0
- data/lib/generators/riveter/query_filter/query_filter_generator.rb +52 -0
- data/lib/generators/riveter/query_filter/templates/module.rb +4 -0
- data/lib/generators/riveter/query_filter/templates/query_filter.rb +40 -0
- data/lib/generators/riveter/query_filter/templates/query_filters.en.yml +49 -0
- data/lib/generators/riveter/service/USAGE +0 -0
- data/lib/generators/riveter/service/service_generator.rb +20 -0
- data/lib/generators/riveter/service/templates/module.rb +4 -0
- data/lib/generators/riveter/service/templates/service.rb +12 -0
- data/lib/generators/riveter/worker/USAGE +0 -0
- data/lib/generators/riveter/worker/templates/module.rb +4 -0
- data/lib/generators/riveter/worker/templates/worker.rb +7 -0
- data/lib/generators/riveter/worker/worker_generator.rb +20 -0
- data/lib/generators/rspec/command/USAGE +0 -0
- data/lib/generators/rspec/command/command_generator.rb +11 -0
- data/lib/generators/rspec/command/templates/command_spec.rb +8 -0
- data/lib/generators/rspec/command_controller/USAGE +0 -0
- data/lib/generators/rspec/command_controller/command_controller_generator.rb +21 -0
- data/lib/generators/rspec/command_controller/templates/command_controller_spec.rb +70 -0
- data/lib/generators/rspec/enquiry/USAGE +0 -0
- data/lib/generators/rspec/enquiry/enquiry_generator.rb +11 -0
- data/lib/generators/rspec/enquiry/templates/enquiry_spec.rb +69 -0
- data/lib/generators/rspec/enquiry_controller/USAGE +0 -0
- data/lib/generators/rspec/enquiry_controller/enquiry_controller_generator.rb +16 -0
- data/lib/generators/rspec/enquiry_controller/templates/enquiry_controller_spec.rb +32 -0
- data/lib/generators/rspec/enum/USAGE +0 -0
- data/lib/generators/rspec/enum/enum_generator.rb +11 -0
- data/lib/generators/rspec/enum/templates/enum_spec.rb +8 -0
- data/lib/generators/rspec/presenter/USAGE +0 -0
- data/lib/generators/rspec/presenter/presenter_generator.rb +11 -0
- data/lib/generators/rspec/presenter/templates/presenter_spec.rb +8 -0
- data/lib/generators/rspec/query/USAGE +0 -0
- data/lib/generators/rspec/query/query_generator.rb +11 -0
- data/lib/generators/rspec/query/templates/query_spec.rb +41 -0
- data/lib/generators/rspec/query_filter/USAGE +0 -0
- data/lib/generators/rspec/query_filter/query_filter_generator.rb +11 -0
- data/lib/generators/rspec/query_filter/templates/query_filter_spec.rb +8 -0
- data/lib/generators/rspec/service/USAGE +0 -0
- data/lib/generators/rspec/service/service_generator.rb +11 -0
- data/lib/generators/rspec/service/templates/service_spec.rb +8 -0
- data/lib/generators/rspec/worker/USAGE +0 -0
- data/lib/generators/rspec/worker/templates/worker_spec.rb +8 -0
- data/lib/generators/rspec/worker/worker_generator.rb +11 -0
- data/lib/generators/test_unit/TODO +1 -0
- data/lib/riveter/associated_type_registry.rb +63 -0
- data/lib/riveter/attribute_default_values.rb +67 -0
- data/lib/riveter/attributes.rb +443 -0
- data/lib/riveter/booleaness_validator.rb +20 -0
- data/lib/riveter/command.rb +59 -0
- data/lib/riveter/command_controller.rb +93 -0
- data/lib/riveter/command_routes.rb +73 -0
- data/lib/riveter/core_extensions.rb +246 -0
- data/lib/riveter/email_validator.rb +20 -0
- data/lib/riveter/enquiry.rb +137 -0
- data/lib/riveter/enquiry_controller.rb +80 -0
- data/lib/riveter/enquiry_routes.rb +69 -0
- data/lib/riveter/enumerated.rb +107 -0
- data/lib/riveter/errors.rb +11 -0
- data/lib/riveter/form_builder_extensions.rb +21 -0
- data/lib/riveter/hash_with_dependency.rb +12 -0
- data/lib/riveter/presenter.rb +73 -0
- data/lib/riveter/query.rb +45 -0
- data/lib/riveter/query_filter.rb +22 -0
- data/lib/riveter/rails/engine.rb +6 -0
- data/lib/riveter/rails/railtie.rb +50 -0
- data/lib/riveter/service.rb +45 -0
- data/lib/riveter/spec_helper.rb +55 -0
- data/lib/riveter/tasks/.keep +0 -0
- data/lib/riveter/version.rb +3 -0
- data/lib/riveter/worker.rb +20 -0
- data/lib/riveter.rb +47 -0
- data/riveter.gemspec +40 -0
- data/spec/examples/attribute_examples.rb +57 -0
- data/spec/generators/haml/command_controller/command_controller_generator_spec.rb +34 -0
- data/spec/generators/haml/enquiry_controller/enquiry_controller_generator_spec.rb +34 -0
- data/spec/generators/riveter/command/command_generator_spec.rb +58 -0
- data/spec/generators/riveter/command_controller/command_controller_generator_spec.rb +0 -0
- data/spec/generators/riveter/enquiry/enquiry_generator_spec.rb +0 -0
- data/spec/generators/riveter/query_filter/query_filter_generator_spec.rb +58 -0
- data/spec/riveter/associated_type_registry_spec.rb +158 -0
- data/spec/riveter/attribute_default_value_spec.rb +69 -0
- data/spec/riveter/attributes_spec.rb +228 -0
- data/spec/riveter/command_controller_spec.rb +116 -0
- data/spec/riveter/command_routes_spec.rb +116 -0
- data/spec/riveter/command_spec.rb +66 -0
- data/spec/riveter/core_extensions_spec.rb +209 -0
- data/spec/riveter/enquiry_controller_spec.rb +128 -0
- data/spec/riveter/enquiry_routes_spec.rb +101 -0
- data/spec/riveter/enquiry_spec.rb +299 -0
- data/spec/riveter/enumerated_spec.rb +47 -0
- data/spec/riveter/form_builder_extensions_spec.rb +28 -0
- data/spec/riveter/presenter_spec.rb +131 -0
- data/spec/riveter/query_filter_spec.rb +19 -0
- data/spec/riveter/query_spec.rb +72 -0
- data/spec/riveter/service_spec.rb +49 -0
- data/spec/riveter/spec_helper_spec.rb +21 -0
- data/spec/riveter/worker_spec.rb +11 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/support/test_associated_class.rb +2 -0
- data/spec/support/test_class_with_attributes.rb +17 -0
- data/spec/support/test_command.rb +4 -0
- data/spec/support/test_command_controller.rb +12 -0
- data/spec/support/test_command_routes.rb +3 -0
- data/spec/support/test_enquiry.rb +7 -0
- data/spec/support/test_enquiry_controller.rb +12 -0
- data/spec/support/test_enquiry_routes.rb +3 -0
- data/spec/support/test_enum.rb +8 -0
- data/spec/support/test_form_builder.rb +3 -0
- data/spec/support/test_model.rb +2 -0
- data/spec/support/test_model_with_attribute_default_values.rb +29 -0
- data/spec/support/test_presenter.rb +2 -0
- data/spec/support/test_query.rb +5 -0
- data/spec/support/test_query_filter.rb +4 -0
- data/spec/support/test_service.rb +7 -0
- data/spec/support/validate_booleaness_of_matcher.rb +17 -0
- data/spec/support/validate_timeliness_of_matcher.rb +17 -0
- data/spec/support/validator_detector.rb +48 -0
- metadata +487 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Rspec
|
|
2
|
+
module Generators
|
|
3
|
+
class WorkerGenerator < ::Rails::Generators::NamedBase
|
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
5
|
+
|
|
6
|
+
def create_worker_spec
|
|
7
|
+
template 'worker_spec.rb', File.join('spec/workers', class_path, "#{file_name}_worker_spec.rb")
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Create generators and templates for test unit implementation
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Riveter
|
|
2
|
+
module AssociatedTypeRegistry
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
container = self
|
|
7
|
+
type_registry = TypeRegistry.new()
|
|
8
|
+
|
|
9
|
+
define_singleton_method :container do
|
|
10
|
+
container
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
define_singleton_method :registry do
|
|
14
|
+
type_registry
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
def key_for(type)
|
|
20
|
+
type.is_a?(Symbol) ? type : type.name.underscore.to_sym
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def register_type(type, associated_type, options={})
|
|
24
|
+
options = {
|
|
25
|
+
:key => key_for(associated_type)
|
|
26
|
+
}.merge(options)
|
|
27
|
+
|
|
28
|
+
# prevent duplicate registrations
|
|
29
|
+
type_list = container.registry[options[:key]]
|
|
30
|
+
type_list << type unless type_list.include?(type)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def resolve!(type)
|
|
34
|
+
registered = self.resolve(type)
|
|
35
|
+
raise UnregisteredType.new(type) unless registered
|
|
36
|
+
registered
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def resolve(type)
|
|
40
|
+
self.resolve_all(type).first
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def resolve_all(type)
|
|
44
|
+
key = key_for(type)
|
|
45
|
+
container.registry.key?(key) ? container.registry[key] : []
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class TypeRegistry < Hash
|
|
51
|
+
def initialize
|
|
52
|
+
super {|h, k| h[k] = []}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class UnregisteredType < Exception
|
|
57
|
+
attr_reader :type
|
|
58
|
+
|
|
59
|
+
def initialize(type)
|
|
60
|
+
@type = type
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Riveter
|
|
2
|
+
module AttributeDefaultValues
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
#
|
|
7
|
+
# NB:
|
|
8
|
+
# defines the container within the scope of this class (or module)
|
|
9
|
+
# so that the list of attributes with defaults aren't shared across models
|
|
10
|
+
#
|
|
11
|
+
class << self
|
|
12
|
+
def attribute_defaults
|
|
13
|
+
@attribute_defaults ||= {}
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# setup after_initialize callback to set the defaults for listed attributes
|
|
18
|
+
after_initialize :set_defaults
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ClassMethods
|
|
22
|
+
#
|
|
23
|
+
# defines a default value for the given attribute
|
|
24
|
+
#
|
|
25
|
+
# E.g. default_value_for :active, true
|
|
26
|
+
#
|
|
27
|
+
# default_value_for :active do
|
|
28
|
+
# # ... some logic ...
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
def default_value_for(attribute, value=nil, &block)
|
|
32
|
+
self.attribute_defaults[attribute] = (block_given? ? block : value)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# defines default values for one or more attributes
|
|
37
|
+
# specify defaults using a hash, where the key is the attribute name
|
|
38
|
+
# and the value is the value or a proc
|
|
39
|
+
#
|
|
40
|
+
# E.g. default_values :confirmed => true,
|
|
41
|
+
# :processed => false,
|
|
42
|
+
# :some_other => lambda { ... some logic ... }
|
|
43
|
+
#
|
|
44
|
+
def default_values(*args)
|
|
45
|
+
defaults = args.first.is_a?(Hash) ?
|
|
46
|
+
args.first :
|
|
47
|
+
{ args.first => args.last }
|
|
48
|
+
|
|
49
|
+
defaults.each do |attribute, value_or_proc|
|
|
50
|
+
value_or_proc.respond_to?(:call) ?
|
|
51
|
+
default_value_for(attribute, &value_or_proc) :
|
|
52
|
+
default_value_for(attribute, value_or_proc)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_defaults
|
|
58
|
+
return if self.respond_to?(:persisted?) && self.persisted?
|
|
59
|
+
self.class.attribute_defaults.each do |attribute, value_or_proc|
|
|
60
|
+
value = value_or_proc.respond_to?(:call) ?
|
|
61
|
+
(value_or_proc.call(self) rescue value_or_proc.call) :
|
|
62
|
+
value_or_proc
|
|
63
|
+
send("#{attribute}=", value) if send(attribute).blank?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
module Riveter
|
|
2
|
+
module Attributes
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
|
|
7
|
+
include ActiveModel::Conversion
|
|
8
|
+
include ActiveModel::Validations
|
|
9
|
+
include ActiveModel::Validations::Callbacks
|
|
10
|
+
include ActiveModel::ForbiddenAttributesProtection
|
|
11
|
+
|
|
12
|
+
# define attributes within the class scope
|
|
13
|
+
# so that they aren't shared between including classes
|
|
14
|
+
|
|
15
|
+
mattr_accessor :_attributes_container, :instance_writer => false
|
|
16
|
+
self._attributes_container = Hash.new {|hash, key| hash[key] = {}.with_indifferent_access }
|
|
17
|
+
|
|
18
|
+
def self._attributes
|
|
19
|
+
self._attributes_container[self]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.attributes
|
|
23
|
+
self._attributes.keys
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ClassMethods
|
|
28
|
+
def attr_string(name, options={})
|
|
29
|
+
converter = Converters.converter_for(:string)
|
|
30
|
+
|
|
31
|
+
define_method name do
|
|
32
|
+
converter.call instance_variable_get("@#{name}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_writer name
|
|
36
|
+
|
|
37
|
+
add_attr(name, :string, converter, options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias_method :attr_text, :attr_string
|
|
41
|
+
|
|
42
|
+
def attr_integer(name, options={})
|
|
43
|
+
options = {
|
|
44
|
+
:validate => true
|
|
45
|
+
}.merge(options)
|
|
46
|
+
|
|
47
|
+
required = options[:required] == true
|
|
48
|
+
converter = Converters.converter_for(:integer)
|
|
49
|
+
|
|
50
|
+
define_method name do
|
|
51
|
+
converter.call instance_variable_get("@#{name}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
validates name,
|
|
55
|
+
:allow_blank => !required,
|
|
56
|
+
:allow_nil => !required,
|
|
57
|
+
:numericality => { :only => :integer } if options[:validate]
|
|
58
|
+
|
|
59
|
+
attr_writer name
|
|
60
|
+
|
|
61
|
+
add_attr(name, :integer, converter, options)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def attr_decimal(name, options={})
|
|
65
|
+
options = {
|
|
66
|
+
:validate => true
|
|
67
|
+
}.merge(options)
|
|
68
|
+
|
|
69
|
+
required = options[:required] == true
|
|
70
|
+
converter = Converters.converter_for(:decimal)
|
|
71
|
+
|
|
72
|
+
define_method name do
|
|
73
|
+
converter.call instance_variable_get("@#{name}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
validates name,
|
|
77
|
+
:allow_blank => !required,
|
|
78
|
+
:allow_nil => !required,
|
|
79
|
+
:numericality => true if options[:validate]
|
|
80
|
+
|
|
81
|
+
attr_writer name
|
|
82
|
+
|
|
83
|
+
add_attr(name, :decimal, converter, options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def attr_date(name, options={})
|
|
87
|
+
options = {
|
|
88
|
+
:validate => true
|
|
89
|
+
}.merge(options)
|
|
90
|
+
|
|
91
|
+
required = options[:required] == true
|
|
92
|
+
converter = Converters.converter_for(:date)
|
|
93
|
+
|
|
94
|
+
define_method name do
|
|
95
|
+
converter.call instance_variable_get("@#{name}")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
validates name,
|
|
99
|
+
:allow_blank => !required,
|
|
100
|
+
:allow_nil => !required,
|
|
101
|
+
:timeliness => { :type => :date } if options[:validate]
|
|
102
|
+
|
|
103
|
+
attr_writer name
|
|
104
|
+
|
|
105
|
+
add_attr(name, :date, converter, options)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def attr_time(name, options={})
|
|
109
|
+
options = {
|
|
110
|
+
:validate => true
|
|
111
|
+
}.merge(options)
|
|
112
|
+
|
|
113
|
+
required = options[:required] == true
|
|
114
|
+
converter = Converters.converter_for(:time)
|
|
115
|
+
|
|
116
|
+
define_method name do
|
|
117
|
+
converter.call instance_variable_get("@#{name}")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
validates name,
|
|
121
|
+
:allow_blank => !required,
|
|
122
|
+
:allow_nil => !required,
|
|
123
|
+
:timeliness => { :type => :time } if options[:validate]
|
|
124
|
+
|
|
125
|
+
attr_writer name
|
|
126
|
+
|
|
127
|
+
add_attr(name, :time, converter, options)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
alias :attr_datetime :attr_time
|
|
131
|
+
|
|
132
|
+
def attr_boolean(name, options={})
|
|
133
|
+
options = {
|
|
134
|
+
:validate => true
|
|
135
|
+
}.merge(options)
|
|
136
|
+
|
|
137
|
+
required = options[:required] == true
|
|
138
|
+
converter = Converters.converter_for(:boolean)
|
|
139
|
+
|
|
140
|
+
define_method name do
|
|
141
|
+
converter.call instance_variable_get("@#{name}")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
alias_method "#{name}?", name
|
|
145
|
+
|
|
146
|
+
validates name,
|
|
147
|
+
:allow_blank => !required,
|
|
148
|
+
:allow_nil => !required,
|
|
149
|
+
:booleaness => true if options[:validate]
|
|
150
|
+
|
|
151
|
+
attr_writer name
|
|
152
|
+
|
|
153
|
+
add_attr(name, :boolean, converter, options)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def attr_enum(name, enum, options={})
|
|
157
|
+
options = {
|
|
158
|
+
:enum => enum,
|
|
159
|
+
:validate => true
|
|
160
|
+
}.merge(options)
|
|
161
|
+
|
|
162
|
+
required = options[:required] == true
|
|
163
|
+
converter = Converters.converter_for(:enum, options)
|
|
164
|
+
|
|
165
|
+
define_method name do
|
|
166
|
+
converter.call instance_variable_get("@#{name}")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# helpers
|
|
170
|
+
# TODO: would be nicer to emulate an association
|
|
171
|
+
|
|
172
|
+
define_singleton_method "#{name}_enum" do
|
|
173
|
+
enum
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
define_singleton_method name.to_s.pluralize do
|
|
177
|
+
enum.collection
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
validates name,
|
|
181
|
+
:allow_blank => !required,
|
|
182
|
+
:allow_nil => !required,
|
|
183
|
+
:inclusion => { :in => enum.all } if options[:validate]
|
|
184
|
+
|
|
185
|
+
attr_writer name
|
|
186
|
+
|
|
187
|
+
add_attr(name, :enum, converter, options)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def attr_array(name, options={}, &block)
|
|
191
|
+
options = {
|
|
192
|
+
:data_type => :integer,
|
|
193
|
+
:validate => true
|
|
194
|
+
}.merge(options)
|
|
195
|
+
data_type = options.delete(:data_type)
|
|
196
|
+
|
|
197
|
+
converter = block_given? ? block : Converters.converter_for(data_type)
|
|
198
|
+
|
|
199
|
+
define_method name do
|
|
200
|
+
(instance_variable_get("@#{name}") || []).map {|item| converter.call(item) }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
attr_writer name
|
|
204
|
+
|
|
205
|
+
add_attr(name, :array, converter, options)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def attr_hash(name, options={})
|
|
209
|
+
converter = Converters.converter_for(:hash, options)
|
|
210
|
+
|
|
211
|
+
# FIXME:
|
|
212
|
+
# * need converter for values?
|
|
213
|
+
# * assumes the hash values are contiguous?
|
|
214
|
+
#
|
|
215
|
+
|
|
216
|
+
attr_accessor name
|
|
217
|
+
|
|
218
|
+
add_attr(name, :hash, converter, options)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def attr_model(name, model, options={})
|
|
222
|
+
options = {
|
|
223
|
+
:model => model
|
|
224
|
+
}.merge(options)
|
|
225
|
+
converter = Converters.converter_for(:model, options)
|
|
226
|
+
|
|
227
|
+
# FIXME:
|
|
228
|
+
# * need converter for values?
|
|
229
|
+
# * define methods to get the actual model instance?
|
|
230
|
+
# * assign model instance directly?
|
|
231
|
+
#
|
|
232
|
+
|
|
233
|
+
# helpers
|
|
234
|
+
define_singleton_method "#{name}_model" do
|
|
235
|
+
model
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
define_method name do
|
|
239
|
+
converter.call instance_variable_get("@#{name}")
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
attr_writer name
|
|
243
|
+
|
|
244
|
+
add_attr(name, :model, converter, options)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def attr_object(name, options={})
|
|
248
|
+
converter = Converters.converter_for(:object, options)
|
|
249
|
+
|
|
250
|
+
define_method name do
|
|
251
|
+
converter.call instance_variable_get("@#{name}")
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
attr_writer name
|
|
255
|
+
|
|
256
|
+
add_attr(name, :object, converter, options)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
private
|
|
260
|
+
|
|
261
|
+
def add_attr(name, type, converter, options={})
|
|
262
|
+
self._attributes[name] = attribute_info = AttributeInfo.new(name, type, converter, options)
|
|
263
|
+
validates name, :presence => true if attribute_info.required?
|
|
264
|
+
attribute_info
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
class AttributeInfo < Struct.new(:name, :type, :converter, :options)
|
|
269
|
+
|
|
270
|
+
def required?
|
|
271
|
+
@required ||= (options[:required] == true)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def default
|
|
275
|
+
@default ||= options[:default]
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def default?
|
|
279
|
+
!self.default.nil?
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
attr_reader :options
|
|
285
|
+
|
|
286
|
+
def initialize(params=nil, options={})
|
|
287
|
+
# assign default values
|
|
288
|
+
self.class._attributes.each do |name, attribute_info|
|
|
289
|
+
next unless attribute_info.default?
|
|
290
|
+
value = attribute_info.default
|
|
291
|
+
send("#{name}=", value.respond_to?(:call) ? value.call : value)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
@options = options.freeze
|
|
295
|
+
|
|
296
|
+
# filter and clean params before applying
|
|
297
|
+
apply_params(
|
|
298
|
+
clean_params(
|
|
299
|
+
filter_params(
|
|
300
|
+
sanitize_for_mass_assignment(params)
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
) unless params.nil?
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def attributes(options={})
|
|
307
|
+
self.class._attributes.inject({}) do |list, (key, attribute_info)|
|
|
308
|
+
list[key] = self.send(attribute_info.name)
|
|
309
|
+
list
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
alias_method :to_params, :attributes
|
|
314
|
+
|
|
315
|
+
# forms use this to determine the HTTP verb
|
|
316
|
+
def persisted?
|
|
317
|
+
false
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# forms use this for getting column meta data
|
|
321
|
+
def column_for_attribute(column)
|
|
322
|
+
attribute_info = self.class._attributes[column]
|
|
323
|
+
OpenStruct.new(
|
|
324
|
+
:name => attribute_info.name,
|
|
325
|
+
:type => attribute_info.type,
|
|
326
|
+
:null => !attribute_info.required?,
|
|
327
|
+
:default => attribute_info.default
|
|
328
|
+
)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
protected
|
|
332
|
+
|
|
333
|
+
# only include defined attributes
|
|
334
|
+
def filter_params(params)
|
|
335
|
+
attributes = self.class._attributes
|
|
336
|
+
params.keep_if {|name, value|
|
|
337
|
+
attributes.key?(name)
|
|
338
|
+
}
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# remove blank/nil attributes
|
|
342
|
+
def clean_params(params)
|
|
343
|
+
params.reject { |key, value|
|
|
344
|
+
reject_value?(value)
|
|
345
|
+
}
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# given sanitized params, assign values to instance
|
|
349
|
+
def apply_params(params)
|
|
350
|
+
params.each do |attribute, value|
|
|
351
|
+
assign_attribute(attribute, value)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
private
|
|
356
|
+
|
|
357
|
+
def reject_value?(value)
|
|
358
|
+
case value
|
|
359
|
+
when Array
|
|
360
|
+
value.reject! {|v| reject_value?(v) }
|
|
361
|
+
false
|
|
362
|
+
when Hash
|
|
363
|
+
value.reject! {|k, v| reject_value?(v) }
|
|
364
|
+
false
|
|
365
|
+
else
|
|
366
|
+
value.blank? unless value.is_a?(FalseClass)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def assign_attribute(attribute, value)
|
|
371
|
+
public_send("#{attribute}=", value)
|
|
372
|
+
|
|
373
|
+
rescue
|
|
374
|
+
if respond_to?("#{attribute}=")
|
|
375
|
+
raise
|
|
376
|
+
else
|
|
377
|
+
raise UnknownAttributeError.new(self, attribute)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
module Converters
|
|
382
|
+
|
|
383
|
+
# supply a built-in converter for the given type
|
|
384
|
+
def self.converter_for(data_type, options={})
|
|
385
|
+
case data_type
|
|
386
|
+
when :string
|
|
387
|
+
lambda {|v| v.to_s }
|
|
388
|
+
|
|
389
|
+
when :boolean
|
|
390
|
+
lambda {|v| v.to_b }
|
|
391
|
+
|
|
392
|
+
when :integer
|
|
393
|
+
lambda {|v| Integer(v) rescue v }
|
|
394
|
+
|
|
395
|
+
when :decimal
|
|
396
|
+
lambda {|v| Float(v) rescue v }
|
|
397
|
+
|
|
398
|
+
when :hash
|
|
399
|
+
lambda {|v| v.to_hash rescue v }
|
|
400
|
+
|
|
401
|
+
when :date
|
|
402
|
+
lambda {|v| Date.parse(v) rescue v }
|
|
403
|
+
|
|
404
|
+
when :time
|
|
405
|
+
lambda {|v| Time.parse(v) rescue v }
|
|
406
|
+
|
|
407
|
+
when :enum
|
|
408
|
+
lambda {|enum, v|
|
|
409
|
+
if v.blank? || v.nil?
|
|
410
|
+
nil
|
|
411
|
+
else
|
|
412
|
+
# FIXME: assuming enum values are integers!
|
|
413
|
+
v = Integer(v) rescue v
|
|
414
|
+
(v.is_a?(String) || v.is_a?(Symbol)) ? enum.value_for(v) : v.to_i
|
|
415
|
+
end
|
|
416
|
+
}.curry[options[:enum]]
|
|
417
|
+
|
|
418
|
+
# FIXME:
|
|
419
|
+
# when :hash
|
|
420
|
+
# lambda {|v| v }
|
|
421
|
+
|
|
422
|
+
when :model
|
|
423
|
+
# FIXME:
|
|
424
|
+
lambda {|model, v|
|
|
425
|
+
# assuming v is an ID
|
|
426
|
+
# model.find(v)
|
|
427
|
+
# FIXME
|
|
428
|
+
v
|
|
429
|
+
}.curry[options[:model]]
|
|
430
|
+
|
|
431
|
+
else
|
|
432
|
+
lambda {|v| v }
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# helper class
|
|
439
|
+
class Base
|
|
440
|
+
include Attributes
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Riveter
|
|
2
|
+
class BooleanessValidator < ActiveModel::EachValidator
|
|
3
|
+
def initialize(options)
|
|
4
|
+
@allow_nil, @allow_blank = options.delete(:allow_nil), options.delete(:allow_blank)
|
|
5
|
+
super
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def validate_each(record, attribute, value)
|
|
9
|
+
return if (@allow_nil && value.nil?) || (@allow_blank && value.blank?)
|
|
10
|
+
|
|
11
|
+
unless [true, false].include?(value)
|
|
12
|
+
record.errors.add(attribute, :booleaness, :value => value)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# add compatibility with ActiveModel validates method which
|
|
19
|
+
# matches option keys to their validator class
|
|
20
|
+
ActiveModel::Validations::BooleanessValidator = Riveter::BooleanessValidator
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Riveter
|
|
2
|
+
module Command
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
include Riveter::Attributes
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
alias_method :command_name, :model_name
|
|
10
|
+
|
|
11
|
+
def i18n_scope
|
|
12
|
+
:commands
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success_notice
|
|
16
|
+
I18n.translate(
|
|
17
|
+
:success,
|
|
18
|
+
:scope => [i18n_scope, :notices, command_name.i18n_key],
|
|
19
|
+
:default => "Successfully executed #{command_name.human}."
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
alias_method :can_perform?, :valid?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def submit(*args)
|
|
28
|
+
params = args.extract_options!
|
|
29
|
+
|
|
30
|
+
# filter and clean params before applying
|
|
31
|
+
apply_params(
|
|
32
|
+
clean_params(
|
|
33
|
+
filter_params(params)
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# perform validations, and proceed if valid
|
|
38
|
+
return false unless self.can_perform?
|
|
39
|
+
|
|
40
|
+
# all good, perform the action
|
|
41
|
+
self.perform *args
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
protected
|
|
45
|
+
|
|
46
|
+
def perform(*args)
|
|
47
|
+
# resolve for the registered service for this command
|
|
48
|
+
service_class = Service::Base.resolve!(self.class)
|
|
49
|
+
# create an instance and invoke perform
|
|
50
|
+
service = service_class.new()
|
|
51
|
+
service.perform(self, *args)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# helper class
|
|
55
|
+
class Base
|
|
56
|
+
include Command
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|