appfuel 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +25 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +19 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/README.md +38 -0
- data/Rakefile +6 -0
- data/appfuel.gemspec +42 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/lib/appfuel.rb +210 -0
- data/lib/appfuel/application.rb +4 -0
- data/lib/appfuel/application/app_container.rb +223 -0
- data/lib/appfuel/application/container_class_registration.rb +22 -0
- data/lib/appfuel/application/container_key.rb +201 -0
- data/lib/appfuel/application/qualify_container_key.rb +76 -0
- data/lib/appfuel/application/root.rb +140 -0
- data/lib/appfuel/cli_msg_request.rb +19 -0
- data/lib/appfuel/configuration.rb +14 -0
- data/lib/appfuel/configuration/definition_dsl.rb +175 -0
- data/lib/appfuel/configuration/file_loader.rb +61 -0
- data/lib/appfuel/configuration/populate.rb +95 -0
- data/lib/appfuel/configuration/search.rb +45 -0
- data/lib/appfuel/db_model.rb +16 -0
- data/lib/appfuel/domain.rb +7 -0
- data/lib/appfuel/domain/criteria.rb +436 -0
- data/lib/appfuel/domain/domain_name_parser.rb +44 -0
- data/lib/appfuel/domain/dsl.rb +247 -0
- data/lib/appfuel/domain/entity.rb +242 -0
- data/lib/appfuel/domain/entity_collection.rb +87 -0
- data/lib/appfuel/domain/expr.rb +127 -0
- data/lib/appfuel/domain/value_object.rb +7 -0
- data/lib/appfuel/errors.rb +104 -0
- data/lib/appfuel/feature.rb +2 -0
- data/lib/appfuel/feature/action_loader.rb +25 -0
- data/lib/appfuel/feature/initializer.rb +43 -0
- data/lib/appfuel/handler.rb +6 -0
- data/lib/appfuel/handler/action.rb +17 -0
- data/lib/appfuel/handler/base.rb +103 -0
- data/lib/appfuel/handler/command.rb +18 -0
- data/lib/appfuel/handler/inject_dsl.rb +88 -0
- data/lib/appfuel/handler/validator_dsl.rb +256 -0
- data/lib/appfuel/initialize.rb +70 -0
- data/lib/appfuel/initialize/initializer.rb +68 -0
- data/lib/appfuel/msg_request.rb +207 -0
- data/lib/appfuel/predicates.rb +10 -0
- data/lib/appfuel/presenter.rb +18 -0
- data/lib/appfuel/presenter/base.rb +7 -0
- data/lib/appfuel/repository.rb +73 -0
- data/lib/appfuel/repository/base.rb +72 -0
- data/lib/appfuel/repository/initializer.rb +19 -0
- data/lib/appfuel/repository/mapper.rb +203 -0
- data/lib/appfuel/repository/mapping_dsl.rb +210 -0
- data/lib/appfuel/repository/mapping_entry.rb +76 -0
- data/lib/appfuel/repository/mapping_registry.rb +121 -0
- data/lib/appfuel/repository_runner.rb +60 -0
- data/lib/appfuel/request.rb +53 -0
- data/lib/appfuel/response.rb +96 -0
- data/lib/appfuel/response_handler.rb +79 -0
- data/lib/appfuel/root_module.rb +31 -0
- data/lib/appfuel/run_error.rb +9 -0
- data/lib/appfuel/storage.rb +3 -0
- data/lib/appfuel/storage/db.rb +4 -0
- data/lib/appfuel/storage/db/active_record_model.rb +42 -0
- data/lib/appfuel/storage/db/mapper.rb +213 -0
- data/lib/appfuel/storage/db/migration_initializer.rb +42 -0
- data/lib/appfuel/storage/db/migration_runner.rb +15 -0
- data/lib/appfuel/storage/db/migration_tasks.rb +18 -0
- data/lib/appfuel/storage/db/repository.rb +231 -0
- data/lib/appfuel/storage/db/repository_query.rb +13 -0
- data/lib/appfuel/storage/file.rb +1 -0
- data/lib/appfuel/storage/file/base.rb +32 -0
- data/lib/appfuel/storage/memory.rb +2 -0
- data/lib/appfuel/storage/memory/mapper.rb +30 -0
- data/lib/appfuel/storage/memory/repository.rb +37 -0
- data/lib/appfuel/types.rb +53 -0
- data/lib/appfuel/validation.rb +80 -0
- data/lib/appfuel/validation/validator.rb +59 -0
- data/lib/appfuel/validation/validator_pipe.rb +47 -0
- data/lib/appfuel/version.rb +3 -0
- metadata +335 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Appfuel
|
2
|
+
class Request
|
3
|
+
attr_reader :action_route, :feature, :action, :inputs, :namespace
|
4
|
+
|
5
|
+
def initialize(action_route, inputs = {})
|
6
|
+
unless inputs.respond_to?(:to_h)
|
7
|
+
fail "inputs must respond to :to_h"
|
8
|
+
end
|
9
|
+
@inputs = inputs.to_h
|
10
|
+
@action_route, @feature, @action = parse_route(action_route)
|
11
|
+
@namespace = "features.#{feature}.actions.#{action}"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# The service route is a forward slash separated string consisting of two
|
17
|
+
# parts. The first part is the feature that holds the action and the
|
18
|
+
# second is the action itself.
|
19
|
+
#
|
20
|
+
# @example 'offers/create'
|
21
|
+
# feature is Offers
|
22
|
+
# action is Create
|
23
|
+
#
|
24
|
+
# @param route [String]
|
25
|
+
# @return [Array]
|
26
|
+
def parse_route(route)
|
27
|
+
feature_name, action_name = route.to_s.split('/')
|
28
|
+
|
29
|
+
feature_name = handle_parsed_string(feature_name)
|
30
|
+
action_name = handle_parsed_string(action_name)
|
31
|
+
|
32
|
+
handle_empty_feature(feature_name)
|
33
|
+
handle_empty_action(action_name)
|
34
|
+
|
35
|
+
|
36
|
+
[route, feature_name, action_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_parsed_string(value)
|
40
|
+
value.to_s.strip
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_empty_feature(feature_name)
|
44
|
+
return unless feature_name.empty?
|
45
|
+
fail "feature is missing, action route must be like <feature/action>"
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_empty_action(action_name)
|
49
|
+
return unless action_name.empty?
|
50
|
+
fail "action is missing, action route must be like <feature/action>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Appfuel
|
2
|
+
# Every action or command must return a response. A response is either
|
3
|
+
# ok or it has errors. You can retrieve the results with the "ok" method
|
4
|
+
# or the errors with the "error" method
|
5
|
+
class Response
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Convience method for creating a successfull response
|
9
|
+
#
|
10
|
+
# @param result Hash the successfull resultset
|
11
|
+
# @reuturn Response
|
12
|
+
def ok(result = nil)
|
13
|
+
self.new(ok: result)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Convience method for creating an error response. It understands
|
17
|
+
# how to handle a SpCore::Error object. Any thing that
|
18
|
+
# is not a hash or can't be converted to a hash is assumed to be
|
19
|
+
# a string and converted into a general_error
|
20
|
+
#
|
21
|
+
# @param data Hash the errors hash
|
22
|
+
# @reuturn Response
|
23
|
+
def error(data)
|
24
|
+
result = format_result_hash(data, default_key: :general_error)
|
25
|
+
result = result[:errors] if result.key?(:errors)
|
26
|
+
self.new(errors: result)
|
27
|
+
end
|
28
|
+
|
29
|
+
def format_result_hash(data, default_key:)
|
30
|
+
if data.is_a?(Hash)
|
31
|
+
result = data
|
32
|
+
elsif data.respond_to?(:to_h)
|
33
|
+
result = data.to_h
|
34
|
+
else
|
35
|
+
result = {default_key => data.to_s}
|
36
|
+
end
|
37
|
+
|
38
|
+
result.symbolize_keys
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :ok, :errors
|
43
|
+
|
44
|
+
# @param data [Hash]
|
45
|
+
# @return [Response]
|
46
|
+
def initialize(data = {})
|
47
|
+
result = format_result_hash(data)
|
48
|
+
|
49
|
+
# when no ok key and no errors key the assume
|
50
|
+
# it is a successfull response
|
51
|
+
if !result.key?(:ok) && !result.key?(:errors)
|
52
|
+
result = {ok: result}
|
53
|
+
end
|
54
|
+
|
55
|
+
@ok = result[:ok]
|
56
|
+
@errors = nil
|
57
|
+
if result.key?(:errors)
|
58
|
+
@ok = nil
|
59
|
+
@errors = Errors.new(result[:errors])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def errors?
|
64
|
+
!ok?
|
65
|
+
end
|
66
|
+
alias_method :failure?, :errors?
|
67
|
+
|
68
|
+
def error_messages
|
69
|
+
return {} if ok?
|
70
|
+
|
71
|
+
errors.messages
|
72
|
+
end
|
73
|
+
|
74
|
+
def ok?
|
75
|
+
errors.nil?
|
76
|
+
end
|
77
|
+
alias_method :success?, :ok?
|
78
|
+
|
79
|
+
def to_h
|
80
|
+
if ok?
|
81
|
+
{ok: ok}
|
82
|
+
else
|
83
|
+
errors.to_h
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_json
|
88
|
+
to_h.to_json
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def format_result_hash(data)
|
93
|
+
self.class.format_result_hash(data, default_key: :ok)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Appfuel
|
2
|
+
class ResponseHandler
|
3
|
+
attr_reader :response_class
|
4
|
+
|
5
|
+
def initialize(response_class = Response)
|
6
|
+
@response_class = response_class
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_response(data)
|
10
|
+
return data if response?(data)
|
11
|
+
return error(data) if error_data?(data)
|
12
|
+
ok(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def response?(data)
|
16
|
+
data.is_a?(response_class)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Determine if the data given is an error by looking at its class or
|
20
|
+
# checking if it is a hash with the key :errors
|
21
|
+
#
|
22
|
+
# @param data
|
23
|
+
# @return Bool
|
24
|
+
def error_data?(data)
|
25
|
+
case
|
26
|
+
when data.kind_of?(::StandardError) || data.is_a?(Errors)
|
27
|
+
true
|
28
|
+
when data.is_a?(Hash)
|
29
|
+
data.key?(:errors)
|
30
|
+
else
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# This is used when returning results back to the action handler. We
|
36
|
+
# use this to indicate it was a successful response
|
37
|
+
#
|
38
|
+
# @param ok Hash
|
39
|
+
# @return Response
|
40
|
+
def ok(value = nil)
|
41
|
+
response_class.ok(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Convert a number of different error formats into hash and use that to
|
45
|
+
# build the response
|
46
|
+
#
|
47
|
+
# @param args StandardError|ActiveModel::Errors|Hash|Errors|Symbol|Response
|
48
|
+
# @return Response
|
49
|
+
def error(*args)
|
50
|
+
error = args.shift
|
51
|
+
case
|
52
|
+
when error.kind_of?(ActiveModel::Errors)
|
53
|
+
messages = error.messages
|
54
|
+
when error.kind_of?(StandardError)
|
55
|
+
key = error.class.to_s.underscore.to_sym
|
56
|
+
backtrace_key = "#{key}_backtrace".to_sym
|
57
|
+
messages = {
|
58
|
+
errors: {
|
59
|
+
key => [error.message],
|
60
|
+
backtrace_key => error.backtrace || []
|
61
|
+
}
|
62
|
+
}
|
63
|
+
when error.is_a?(Hash)
|
64
|
+
messages = error.key?(:errors) ? error : {errors: error}
|
65
|
+
when error.is_a?(Errors)
|
66
|
+
messages = error.to_h
|
67
|
+
|
68
|
+
when args.length >= 1
|
69
|
+
messages = {errors: {error => args}}
|
70
|
+
when error.is_a?(response_class)
|
71
|
+
return error
|
72
|
+
else
|
73
|
+
messages = {errors: {general_error: [error.to_s]}}
|
74
|
+
end
|
75
|
+
|
76
|
+
response_class.error(messages)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Appfuel
|
2
|
+
# The root module is an import concept. It represents the services top most
|
3
|
+
# namespace. It is assumed that the root module will have a feature module,
|
4
|
+
# its child, and that feature module will have many action classes inside it.
|
5
|
+
module RootModule
|
6
|
+
|
7
|
+
def root_module=(value)
|
8
|
+
fail "Root module must be a module" unless value.is_a?(Module)
|
9
|
+
@root_module = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def root_module
|
13
|
+
@root_module ||= root_module_const
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def root_module_const
|
19
|
+
name = root_module_name
|
20
|
+
unless Kernel.const_defined?(name)
|
21
|
+
fail "Root module is not defined (#{name})"
|
22
|
+
end
|
23
|
+
|
24
|
+
Kernel.const_get(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def root_module_name
|
28
|
+
self.to_s.split("::").first
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Appfuel
|
2
|
+
module Db
|
3
|
+
class ActiveRecordModel < ActiveRecord::Base
|
4
|
+
# ChangeOrder::Global::Db::FooBar
|
5
|
+
#
|
6
|
+
# ChangeOrder::Membership::Peristence::Db::Account
|
7
|
+
# ChangeOrder::Membership::Persistence::Yaml::Account
|
8
|
+
#
|
9
|
+
# ChangeOrder::Membership::Domains::Account
|
10
|
+
#
|
11
|
+
# Appfuel.mapping membership.account,
|
12
|
+
# db: account, yaml: account do
|
13
|
+
# map id, account.id
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# module Membership
|
17
|
+
# module Db
|
18
|
+
#
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# global.db.foobar
|
24
|
+
#
|
25
|
+
# features.membership.db.account
|
26
|
+
# features.membership.yaml.account
|
27
|
+
#
|
28
|
+
#
|
29
|
+
self.abstract_class = true
|
30
|
+
include Appfuel::Application::AppContainer
|
31
|
+
def self.inherited(klass)
|
32
|
+
super
|
33
|
+
register_container_class(klass)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def entity_attributes
|
38
|
+
attributes.symbolize_keys.select {|_,value| !value.nil?}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Appfuel
|
2
|
+
module Db
|
3
|
+
class Mapper < Appfuel::Repository::Mapper
|
4
|
+
|
5
|
+
# Determines if an domain entity exists for this key
|
6
|
+
#
|
7
|
+
# @param key [String, Symbol]
|
8
|
+
# @return [Boolean]
|
9
|
+
def entity_mapped?(name)
|
10
|
+
registry.entity?(name)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the active record model from a map for a given entity
|
14
|
+
#
|
15
|
+
# @raise [RuntimeError] if entity key does not exist
|
16
|
+
# @raise [RuntimeError] if map key does not exist
|
17
|
+
#
|
18
|
+
# @param entity [String] encoded "feature.entity"
|
19
|
+
# @param domain_attr [String] attribute of entity
|
20
|
+
# @return [DbModel]
|
21
|
+
def db_class_key(entity_name, entity_attr)
|
22
|
+
entry = find(entity_name, entity_attr)
|
23
|
+
db_class_key = entry.storage(:db)
|
24
|
+
|
25
|
+
mapp.storage(entity, domain_attr)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Converts an entity expression into a valid active record expresion with
|
29
|
+
# string expresion (array canditions) and value(s)
|
30
|
+
#
|
31
|
+
# @param expr [Domain::Expr]
|
32
|
+
# @param results [Hash]
|
33
|
+
# @return [DbExpr] Returns a valid active record expresion
|
34
|
+
def create_db_expr(expr)
|
35
|
+
DbExpr.new(qualified_db_column(expr), expr.op, expr.value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Validates if a record exists in the table that matches the array with
|
39
|
+
# the conditions given.
|
40
|
+
#
|
41
|
+
# @param criteria [Criteria]
|
42
|
+
# @return [Boolean]
|
43
|
+
def exists?(criteria)
|
44
|
+
domain_expr = criteria.exists_expr
|
45
|
+
domain_name = domain_expr.domain_name
|
46
|
+
domain_attr = domain_expr.domain_attr
|
47
|
+
|
48
|
+
db_expr = create_db_expr(domain_expr)
|
49
|
+
db_model = registry.db_class(domain_name, domain_attr)
|
50
|
+
db_model.exists?([db_expr.string, db_expr.values])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Build a where expression from the mapped db class using the criteria.Ï
|
54
|
+
#
|
55
|
+
# @param criteria [Criteria]
|
56
|
+
# @param relation [DbModel, ActiveRecord::Relation]
|
57
|
+
# @return [DbModel, ActiveRecord::Relation]
|
58
|
+
def where(criteria, relation)
|
59
|
+
unless criteria.where?
|
60
|
+
fail "you must explicitly call :all when criteria has no exprs."
|
61
|
+
end
|
62
|
+
|
63
|
+
criteria.each do |domain_expr, op|
|
64
|
+
relation = if op == :or
|
65
|
+
relation.or(db_where(domain_expr, relation))
|
66
|
+
else
|
67
|
+
db_where(domain_expr, relation)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
relation
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return qualified db column name from entity expression.
|
74
|
+
#
|
75
|
+
# @param expr [SpCore::Domain::Expr]
|
76
|
+
# @return db column name [String]
|
77
|
+
def qualified_db_column(expr)
|
78
|
+
table_name, column = db_table_column(expr)
|
79
|
+
"#{table_name}.#{column}"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Determine Domain Mapentry and DbModel from entity expression.
|
83
|
+
#
|
84
|
+
# @param expr [SpCore::Domain::Expr]
|
85
|
+
# @return [table_name, column] [Array]
|
86
|
+
def db_table_column(expr)
|
87
|
+
entry = registry.find(expr.domain_name, expr.domain_attr)
|
88
|
+
db = registry.db_class_constant(entry.db_class)
|
89
|
+
[db.table_name, entry.db_column]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Build an order by expression for the given db relation based on the
|
93
|
+
# criteria
|
94
|
+
#
|
95
|
+
# @param criteria [Criteria]
|
96
|
+
# @param relation [DbModel, ActiveRecord::Relation]
|
97
|
+
# @return [ActiveRecord::Relation]
|
98
|
+
def order(criteria, relation)
|
99
|
+
return relation unless criteria.order?
|
100
|
+
criteria.order.each do |expr|
|
101
|
+
db_column = qualified_db_column(expr)
|
102
|
+
direction = expr.value
|
103
|
+
relation = relation.order("#{db_column} #{direction}")
|
104
|
+
end
|
105
|
+
relation
|
106
|
+
end
|
107
|
+
|
108
|
+
# Eventhough there is no mapping here we add the interface for
|
109
|
+
# consistency.
|
110
|
+
#
|
111
|
+
# @param criteria [Criteria]
|
112
|
+
# @param relation [DbModel, ActiveRecord::Relation]
|
113
|
+
# @return [ActiveRecord::Relation]
|
114
|
+
def limit(criteria, relation)
|
115
|
+
return relation unless criteria.limit?
|
116
|
+
|
117
|
+
relation.limit(criteria.limit)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Map the entity expr to a hash of db_column => value and call
|
121
|
+
# on the relation using that.
|
122
|
+
#
|
123
|
+
# @note this is db library specific and needs to be moved to an adapter
|
124
|
+
#
|
125
|
+
# @param expr [Appfuel::Domain::Expr]
|
126
|
+
# @param relation [ActiveRecord::Relation]
|
127
|
+
# @return [ActiveRecord::Relation]
|
128
|
+
def db_where(domain_expr, relation)
|
129
|
+
db_expr = create_db_expr(domain_expr)
|
130
|
+
relation.where([db_expr.string, db_expr.values])
|
131
|
+
end
|
132
|
+
|
133
|
+
# Convert the entity into a hash of db tables that represent
|
134
|
+
# that entity. Each table has its own hash of mapped columns.
|
135
|
+
#
|
136
|
+
# @param domain [Appfuel::Domain::Entity]
|
137
|
+
# @param opts [Hash]
|
138
|
+
# @option exclued [Array] list of columns to exclude from mapping
|
139
|
+
#
|
140
|
+
# @return [Hash] each key is a table with a hash of column name/value
|
141
|
+
def to_storage(domain, opts = {})
|
142
|
+
excluded = opts[:exclude] || []
|
143
|
+
data = {}
|
144
|
+
each_entity_attr(domain.domain_name) do |entry|
|
145
|
+
column = entry.storage_attr
|
146
|
+
db_class = entry.storage(:db)
|
147
|
+
next if excluded.include?(column) || entry.skip?
|
148
|
+
|
149
|
+
data[db_class] = {} unless data.key?(db_class)
|
150
|
+
data[db_class][column] = entity_value(domain, entry)
|
151
|
+
end
|
152
|
+
data
|
153
|
+
end
|
154
|
+
|
155
|
+
# Handles entity value by checking if its a computed property,
|
156
|
+
# fetching the value and converting undefined values to nil.
|
157
|
+
#
|
158
|
+
# @param domain [Appfuel::Domain::Entity]
|
159
|
+
# @param map_entry [MappingEntity]
|
160
|
+
# @return the value
|
161
|
+
def entity_value(domain, entry)
|
162
|
+
value = retrieve_entity_value(domain, entry.domain_attr)
|
163
|
+
if entry.computed_attr?
|
164
|
+
value = entry.compute_attr(value)
|
165
|
+
end
|
166
|
+
|
167
|
+
value = nil if undefined?(value)
|
168
|
+
|
169
|
+
value
|
170
|
+
end
|
171
|
+
|
172
|
+
# @params value [mixed]
|
173
|
+
# @return [Boolean]
|
174
|
+
def undefined?(value)
|
175
|
+
value == Types::Undefined
|
176
|
+
end
|
177
|
+
|
178
|
+
# Fetch the value for the entity attribute. When the attribute name
|
179
|
+
# contains a '.' then traverse the dots and call the last attribute
|
180
|
+
# for the value
|
181
|
+
#
|
182
|
+
# @param domain [Appfuel::Domain::Entity]
|
183
|
+
# @param entity_attribute [String]
|
184
|
+
# @return the value
|
185
|
+
def retrieve_entity_value(domain, entity_attr)
|
186
|
+
chain = entity_attr.split('.')
|
187
|
+
target = domain
|
188
|
+
chain.each do |attr_method|
|
189
|
+
unless target.respond_to?(attr_method)
|
190
|
+
return nil
|
191
|
+
end
|
192
|
+
|
193
|
+
target = target.public_send(attr_method)
|
194
|
+
end
|
195
|
+
target
|
196
|
+
end
|
197
|
+
|
198
|
+
# Create nested hashes from string
|
199
|
+
#
|
200
|
+
# @param domain_attr [String]
|
201
|
+
# @param entity_value [String]
|
202
|
+
# @return [nested hash]
|
203
|
+
def create_entity_hash(domain_attr, entity_value)
|
204
|
+
domain_attr.split('.').reverse.inject(entity_value) { |a,n| {n => a}}
|
205
|
+
end
|
206
|
+
|
207
|
+
def model_attributes(relation)
|
208
|
+
ap relation
|
209
|
+
relation.attributes.select {|_, value| !value.nil?}
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|