appfuel 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/appfuel.gemspec +1 -1
  4. data/lib/appfuel/application/app_container.rb +0 -1
  5. data/lib/appfuel/application/dispatcher.rb +32 -0
  6. data/lib/appfuel/application/root.rb +4 -4
  7. data/lib/appfuel/application.rb +2 -1
  8. data/lib/appfuel/domain/domain_name_parser.rb +9 -5
  9. data/lib/appfuel/domain.rb +0 -7
  10. data/lib/appfuel/handler/base.rb +8 -14
  11. data/lib/appfuel/storage/db/mapper.rb +31 -35
  12. data/lib/appfuel/storage/db/repository.rb +53 -121
  13. data/lib/appfuel/storage/repository/base.rb +246 -0
  14. data/lib/appfuel/storage/repository/criteria.rb +317 -0
  15. data/lib/appfuel/{domain → storage/repository}/expr.rb +1 -1
  16. data/lib/appfuel/storage/repository/expr_conjunction.rb +41 -0
  17. data/lib/appfuel/{domain → storage/repository}/expr_parser.rb +10 -22
  18. data/lib/appfuel/{domain → storage/repository}/expr_transform.rb +7 -7
  19. data/lib/appfuel/{repository → storage/repository}/mapper.rb +8 -3
  20. data/lib/appfuel/storage/repository/order_expr.rb +51 -0
  21. data/lib/appfuel/storage/repository/runner.rb +62 -0
  22. data/lib/appfuel/storage/repository/search_parser.rb +50 -0
  23. data/lib/appfuel/storage/repository/search_transform.rb +58 -0
  24. data/lib/appfuel/{domain/criteria_settings.rb → storage/repository/settings.rb} +20 -5
  25. data/lib/appfuel/{repository.rb → storage/repository.rb} +13 -0
  26. data/lib/appfuel/storage.rb +1 -0
  27. data/lib/appfuel/version.rb +1 -1
  28. data/lib/appfuel.rb +0 -2
  29. metadata +21 -19
  30. data/lib/appfuel/domain/base_criteria.rb +0 -171
  31. data/lib/appfuel/domain/exists_criteria.rb +0 -57
  32. data/lib/appfuel/domain/expr_conjunction.rb +0 -27
  33. data/lib/appfuel/domain/search_criteria.rb +0 -137
  34. data/lib/appfuel/repository/base.rb +0 -86
  35. data/lib/appfuel/repository_runner.rb +0 -60
  36. /data/lib/appfuel/{repository → storage/repository}/initializer.rb +0 -0
  37. /data/lib/appfuel/{repository → storage/repository}/mapping_dsl.rb +0 -0
  38. /data/lib/appfuel/{repository → storage/repository}/mapping_entry.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab35093c6d5bbbed105926ad6cad55cde7616d5e
4
- data.tar.gz: e9bf722af35d2bad3dea89fba52eea61a02f3a00
3
+ metadata.gz: 730ab86942bed445ca95032499fb709e294614e5
4
+ data.tar.gz: 9b6fb2eeb4ad17c1737d9ff66a459edb11e86625
5
5
  SHA512:
6
- metadata.gz: b1bb7c55c07770ca5b77be1d2e2db73ed5d4542ac0209c0dd069efc9a5646f1d22c813964e07999e7d028c1b02096d53a9638dda6b6381a216618482128b0e95
7
- data.tar.gz: ff0140867e8db857de3377e39032f9e534fcdc2a7dc336e507cbe62e41da6f93cdf5c8029bc48ecc147d8b5b40f6f6eb779748b278a656af84f2b6f146a3e2f4
6
+ metadata.gz: 94cd230b06bd3fafb420f909c329f531b9e227d0ef9213432399fa531d05ac01914bcee6e026030733fb7c6ec5362047cf585b1cf5d139179092c98ad54f3cfe
7
+ data.tar.gz: 85e629ee6586d4495b700b4713fbc1a7af980af062e4394b67ccbd0b44cba48f4a1c0dd0f6f5da4ebe6c9a8aa810efb1c77c2f6251a3dba3b749001a1d634741
data/CHANGELOG.md CHANGED
@@ -21,3 +21,11 @@ All notable changes to this project will be documented in this file. (Pending ap
21
21
 
22
22
  ### Added
23
23
  - added exists interface to db mapper, will finalize the repo on next release
24
+
25
+ ## [[0.2.6]](https://github.com/rsb/appfuel/releases/tag/0.2.6) 2017-06-6
26
+
27
+ ### Added
28
+ - dispatcher mixin
29
+
30
+ ### Changed
31
+ - moved domain parsing to repository namespace
data/appfuel.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  # we have to lock dry-types due to failures I am encountering
24
24
  # when dynamically creating form validators. Not sure if it is the library
25
25
  # or the way I am using it.
26
- spec.add_dependency "activerecord", "~> 5.0.2"
26
+ spec.add_dependency "activerecord", "~> 5.1.0"
27
27
  spec.add_dependency "dry-types", "0.9.2"
28
28
  spec.add_dependency "dry-container", "~> 0.6"
29
29
  spec.add_dependency "dry-validation", "~> 0.10.5"
@@ -120,7 +120,6 @@ module Appfuel
120
120
  # @return nil
121
121
  def container_class_key(value = nil)
122
122
  return @container_class_key if value.nil?
123
- p "[container-class-key] #{self} => #{value}"
124
123
  @container_class_key = value
125
124
  end
126
125
 
@@ -0,0 +1,32 @@
1
+ module Appfuel
2
+ module Application
3
+ module Dispatcher
4
+
5
+ def dispatch(request, container)
6
+ begin
7
+ container[:feature_initializer].call(request.feature, container)
8
+ action = container[:action_loader].call(request.namespace, container)
9
+ response = action.run(inputs)
10
+ rescue => e
11
+ handler_error(e, container)
12
+ end
13
+
14
+ if response.failure?
15
+ handle_error(contaier, :failed, error)
16
+ end
17
+ end
18
+
19
+ private
20
+ def handle_error(e, container)
21
+ unless container.key?(:error_handler)
22
+ return default_error_handling(e, contianer) unless container.key?(:error_handler)
23
+ end
24
+
25
+ end
26
+
27
+ def default_error_handling(e, container)
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,6 +1,8 @@
1
1
  module Appfuel
2
2
  module Application
3
3
  module Root
4
+ extend Dispatcher
5
+
4
6
  # Initialize Appfuel by creating an application container for the
5
7
  # app represented by the root module passed in. The app container is
6
8
  # a dependency injection container that is used thought the app.
@@ -111,6 +113,7 @@ module Appfuel
111
113
  container.register(:root_path, root_path)
112
114
  container.register(:auto_register_classes, [])
113
115
  container.register(:repository_mappings, {})
116
+ container.register(:repository_cache, {})
114
117
  container.register(:repository_initializer, repo_initializer)
115
118
  container.register(:features_path, "#{root_name}/features")
116
119
  container.register(:feature_initializer, feature_initializer)
@@ -131,10 +134,7 @@ module Appfuel
131
134
  def call(route, inputs = {})
132
135
  container = Appfuel.app_container
133
136
  request = Request.new(route, inputs)
134
-
135
- container[:feature_initializer].call(request.feature, container)
136
- action = container[:action_loader].call(request.namespace, container)
137
- action.run(inputs)
137
+ dispatch(request, container)
138
138
  end
139
139
  end
140
140
  end
@@ -1,3 +1,4 @@
1
- require_relative 'application/root'
1
+ require_relative 'application/dispatcher'
2
2
  require_relative 'application/app_container'
3
+ require_relative 'application/root'
3
4
  require_relative 'application/container_class_registration'
@@ -5,9 +5,11 @@ module Appfuel
5
5
 
6
6
  # This parse the domain name string or object with domain_name method
7
7
  # and returns an array with feature, domain and name.
8
+ #
8
9
  # @example
9
- # parse_domain_name('Foo.Bar.Baz')
10
- # ['Foo', 'Bar', 'Foo.Bar.Baz']
10
+ # parse_domain_name('foo.bar')
11
+ # ['foo', 'bar', 'foo.bar']
12
+ #
11
13
  # @param name [String, Object] domain name
12
14
  # @return [Array] parsed Array from domain name
13
15
  def parse_domain_name(name)
@@ -18,7 +20,7 @@ module Appfuel
18
20
  name = name.domain_name if name.respond_to?(:domain_name)
19
21
  feature, domain = name.split('.')
20
22
  if domain.nil?
21
- domain, feature = feature, nil
23
+ fail "domain names must be in the form of (<feature|global>.domain)"
22
24
  end
23
25
 
24
26
  [feature, domain, name]
@@ -26,9 +28,11 @@ module Appfuel
26
28
 
27
29
  # This parse the domain attributes string and returns an array with
28
30
  # two elements.
31
+ #
29
32
  # @example
30
- # parse_domain_attr('Foo.Bar.Baz')
31
- # ['Foo.Bar', 'Baz']
33
+ # parse_domain_attr('foo.bar.baz')
34
+ # ['foo.bar', 'baz']
35
+ #
32
36
  # @param name [String] domain name attributes
33
37
  # @return [Array] parsed Array from domain attributes
34
38
  def parse_domain_attr(name)
@@ -1,12 +1,5 @@
1
1
  require_relative "domain/domain_name_parser"
2
2
  require_relative "domain/dsl"
3
- require_relative "domain/expr"
4
- require_relative "domain/expr_conjunction"
5
3
  require_relative "domain/entity"
6
4
  require_relative "domain/value_object"
7
5
  require_relative "domain/entity_collection"
8
- require_relative "domain/criteria_settings"
9
- require_relative "domain/base_criteria"
10
- require_relative "domain/search_criteria"
11
- require_relative "domain/exists_criteria"
12
- require_relative "domain/expr_parser"
@@ -29,20 +29,14 @@ module Appfuel
29
29
  # @param inputs [Hash] inputs to be validated
30
30
  # @return [Response]
31
31
  def run(inputs = {}, container = Dry::Container.new)
32
- begin
33
- response = resolve_inputs(inputs)
34
- return response if response.failure?
35
- valid_inputs = response.ok
36
-
37
- resolve_dependencies(container)
38
- handler = self.new(container)
39
- result = handler.call(valid_inputs)
40
- result = create_response(result) unless response?(result)
41
- rescue RunError => e
42
- result = e.response
43
- rescue StandardError => e
44
- result = error(e)
45
- end
32
+ response = resolve_inputs(inputs)
33
+ return response if response.failure?
34
+ valid_inputs = response.ok
35
+
36
+ resolve_dependencies(container)
37
+ handler = self.new(container)
38
+ result = handler.call(valid_inputs)
39
+ result = create_response(result) unless response?(result)
46
40
 
47
41
  result
48
42
  end
@@ -2,10 +2,6 @@ module Appfuel
2
2
  module Db
3
3
  class Mapper < Appfuel::Repository::Mapper
4
4
 
5
- def search(domain_name, criteria, opts = {})
6
-
7
- end
8
-
9
5
  # Return qualified db column name from entity expression.
10
6
  #
11
7
  # @param expr [SpCore::Domain::Expr]
@@ -33,7 +29,11 @@ module Appfuel
33
29
  # @param entry [Repository::MappingEntry] optional
34
30
  # @return [Array] The first index is the expr string using ? for values
35
31
  # The second index is the actual value(s)
36
- def convert_expr(expr, entry = nil)
32
+ def convert_expr(expr, values = [], entry = nil)
33
+ if expr_conjunction?(expr)
34
+ return convert_conjunction(expr, values, entry)
35
+ end
36
+
37
37
  column = qualified_db_column(expr, entry)
38
38
  op = expr.op
39
39
  arg = case expr.op
@@ -42,7 +42,15 @@ module Appfuel
42
42
  else
43
43
  '?'
44
44
  end
45
- ["#{column} #{op} #{arg}", expr.value]
45
+
46
+ values << expr.value
47
+ ["#{column} #{op} #{arg}", values]
48
+ end
49
+
50
+ def convert_conjunction(expr, values = [], entry = nil)
51
+ left, values = convert_expr(expr.left, values, entry)
52
+ right, values = convert_expr(expr.right, values, entry)
53
+ ["#{left} #{expr.op} #{right}", values]
46
54
  end
47
55
 
48
56
  # Validates if a record exists in the table that matches the array with
@@ -55,10 +63,10 @@ module Appfuel
55
63
  domain_attr = domain_expr.domain_attr
56
64
 
57
65
  entry = find(domain_name, domain_attr)
58
- db_expr, values = convert_expr(domain_expr, entry)
66
+ db_expr, values = convert_expr(domain_expr, [], entry)
59
67
  db = storage_class_from_entry(entry, :db)
60
68
 
61
- db.exists?([db_expr, values])
69
+ db.exists?([db_expr, *values])
62
70
  end
63
71
 
64
72
  # Build a where expression from the mapped db class using the criteria.Ï
@@ -67,18 +75,8 @@ module Appfuel
67
75
  # @param relation [DbModel, ActiveRecord::Relation]
68
76
  # @return [DbModel, ActiveRecord::Relation]
69
77
  def where(criteria, relation)
70
- unless criteria.where?
71
- fail "you must explicitly call :all when criteria has no exprs."
72
- end
73
-
74
- criteria.each do |domain_expr, op|
75
- relation = if op == :or
76
- relation.or(db_where(domain_expr, relation))
77
- else
78
- db_where(domain_expr, relation)
79
- end
80
- end
81
- relation
78
+ conditions, values = convert_expr(criteria.filters)
79
+ relation.where(conditions, *values)
82
80
  end
83
81
 
84
82
  # Build an order by expression for the given db relation based on the
@@ -89,12 +87,19 @@ module Appfuel
89
87
  # @return [ActiveRecord::Relation]
90
88
  def order(criteria, relation)
91
89
  return relation unless criteria.order?
92
- criteria.order.each do |expr|
90
+ order_str = convert_order_exprs(criteria.order_by)
91
+ relation.order(order_str)
92
+ end
93
+
94
+ def convert_order_exprs(list)
95
+ str = ''
96
+ list.each do |expr|
93
97
  db_column = qualified_db_column(expr)
94
- direction = expr.value
95
- relation = relation.order("#{db_column} #{direction}")
98
+ direction = expr.op
99
+ str << "#{db_column} #{direction}, "
96
100
  end
97
- relation
101
+
102
+ str.strip.chomp(',')
98
103
  end
99
104
 
100
105
  # Eventhough there is no mapping here we add the interface for
@@ -109,17 +114,8 @@ module Appfuel
109
114
  relation.limit(criteria.limit)
110
115
  end
111
116
 
112
- # Map the entity expr to a hash of db_column => value and call
113
- # on the relation using that.
114
- #
115
- # @note this is db library specific and needs to be moved to an adapter
116
- #
117
- # @param expr [Appfuel::Domain::Expr]
118
- # @param relation [ActiveRecord::Relation]
119
- # @return [ActiveRecord::Relation]
120
- def db_where(domain_expr, relation)
121
- db_expr = create_db_expr(domain_expr)
122
- relation.where([db_expr.string, db_expr.values])
117
+ def storage_hash(db_model)
118
+ db_model.attributes.select {|_, value| !value.nil?}
123
119
  end
124
120
  end
125
121
  end
@@ -24,35 +24,14 @@ module Appfuel
24
24
  build(name: entity.domain_name, storage: db_results, type: :db)
25
25
  end
26
26
 
27
- def db_class(key)
28
- app_container[key]
29
- end
30
-
31
- # Used when the automated query methods don't suite your use case. It
32
- # is assumed that the method executed will honor the same interfaces as
33
- # query does
34
- #
35
- # @param criteria [SpCore::Criteria]
36
- # @return [Dataset]
37
- def execute_criteria(criteria)
38
- query_method = "#{criteria.exec}_manual_query"
39
- validate_query_method(query_method)
40
-
41
- public_send(query_method, criteria)
42
- end
43
-
44
- # Use the criteria entity's basename as a convention to find a method
45
- # on the repository that returns the necessary relation (db scope) for
46
- # which to add conditions that will be used to complete the query.
27
+ # when key has no . assume the feature of the repository
47
28
  #
48
29
  #
49
- # @param criteria [SpCore::Criteria]
50
- # @return [ActiveRecord::Relation]
51
- def query_relation(criteria)
52
- query_method = "#{criteria.domain}_query"
53
- validate_query_method(query_method)
54
-
55
- public_send(query_method)
30
+ def db_class(key)
31
+ unless key.include?('.')
32
+ key = "features.#{self.class.container_feature_name}.db.#{key}"
33
+ end
34
+ app_container[key]
56
35
  end
57
36
 
58
37
  # Handles the treatment of the relation when the recordset is empty
@@ -86,126 +65,79 @@ module Appfuel
86
65
  # @param criteria [SpCore::Criteria]
87
66
  # @param relation [mixed]
88
67
  # @return relation
89
- def apply_query_conditions(criteria, relation)
90
- relation = where(criteria, relation)
91
- relation = order(criteria, relation)
92
- relation = limit(criteria, relation)
68
+ def apply_query_conditions(criteria, relation, _settings)
69
+ ap 'i am applying some query condititions'
70
+ relation = mapper.where(criteria, relation)
71
+ ap relation
72
+ ap 'some where conditions'
73
+ relation = mapper.order(criteria, relation)
74
+ ap 'some order condition'
75
+ relation = mapper.limit(criteria, relation)
76
+ ap 'some limit condition'
93
77
  relation
94
78
  end
95
79
 
96
- # We have an interface for getting all recordsets separately because
97
- # this is usually done with no filters or limits.
98
- #
99
- # @param criteria [SpCore::Criteria]
100
- # @param relation
101
- # @return relation
102
- def apply_query_all(criteria, relation)
103
- unless criteria.all?
104
- fail "This interface can only be used when the criteria :all is used"
105
- end
106
-
107
- order(criteria, relation.all)
108
- end
109
-
110
80
  # Determines which query conditions to apply to the relation
111
81
  #
112
82
  # @param criteria [SpCore::Criteria]
113
83
  # @param relation
114
84
  # @return relation
115
- def handle_query_conditions(criteria, relation)
116
- if criteria.all?
117
- return apply_query_all(criteria, relation)
85
+ def handle_query_conditions(criteria, relation, settings)
86
+ if settings.all?
87
+ return order(criteria, relation.all)
118
88
  end
119
89
 
120
- apply_query_conditions(criteria, relation)
90
+ apply_query_conditions(criteria, relation, settings)
121
91
  end
122
92
 
123
- # Factory method to create a domain entity
124
- #
125
- # @param domain_name [String]
126
- # @return [SpCore::Domain::EntityCollection]
127
- def create_entity_collection(domain_name)
128
- Appfuel::Domain::EntityCollection.new(domain_name)
129
- end
93
+ def handle_empty_relation(criteria, relation, settings)
94
+ unless relation.respond_to?(:blank?)
95
+ fail "The database relation invalid, does not implement :blank?"
96
+ end
130
97
 
131
- # Creates a lambda to used with the entity collection
132
- #
133
- # @param criteria [SpCore::Criteria]
134
- # @param relation [Object]
135
- # @param builder [Object]
136
- # @return lambda
137
- def entity_loader(criteria, relation, builder)
138
- -> { load_collection(criteria, relation, builder) }
139
- end
98
+ return nil unless relation.blank?
140
99
 
141
- # A collection is usually loaded within an entity collection via
142
- # a lambda. It setups up pagination results and builds an entity
143
- # foreach record in the list
144
- #
145
- # @param criteria [SpCore::Criteria]
146
- # @param relation [Object]
147
- # @param builder [Object]
148
- # @return [Hash]
149
- def load_collection(criteria, relation, builder)
150
- data = { items: [] }
151
- unless criteria.disable_pagination?
152
- relation = relation.page(criteria.page).per(criteria.per_page)
153
- data[:pager] = create_pager_result(
154
- total_pages: relation.total_pages,
155
- current_page: relation.current_page,
156
- total_count: relation.total_count,
157
- page_limit: relation.limit_value,
158
- page_size: relation.size
159
- )
100
+ if criteria.error_on_empty_dataset?
101
+ return domain_not_found_error(criteria)
160
102
  end
161
103
 
162
- relation.each do |db_item|
163
- data[:items] << builder.call(db_item)
104
+ if criteria.single?
105
+ return domain_not_found(criteria)
164
106
  end
165
- data
166
107
  end
167
108
 
168
- # Create an entity collection and assign the entity loader with
169
- # the entity builder class.
109
+ # 1) lookup query id in cache
110
+ # if found build collection from cached query ids
170
111
  #
171
- # @param criteria [SpCore::Criteria]
172
- # @param relation relation
173
- # @return SpCore::Domain::EntityCollection
174
- def build_criteria_entities(criteria, relation)
175
- builder = create_entity_builder(criteria.domain_name)
176
- result = handle_empty_relation(criteria, relation)
177
- # when this has a result it means an empty relation has been
178
- # handled and ready for return otherwise it was a no op
112
+ # 2) query id not found in cache
113
+ # a) assign query id from criteria
114
+ # b) find the domain builder for that criteria
115
+ # c) loop through each item in the database relation
116
+ # d) build an domain from each record in the relation
117
+ # e) create cache id from the domain
118
+ # f) record cache id into a list that represents query
119
+ # g) assign domain into the cache with its cache id
120
+ # id is in the cache the its updated *represents a miss
121
+ # h) assign the query list into the cache with its query id
122
+ #
123
+ def build_domains(criteria, relation, settings)
124
+ result = handle_empty_relation(criteria, relation, settings)
179
125
  return result if result
180
126
 
181
- if criteria.single?
182
- relation_method = criteria.first? ? :first : :last
183
- return builder.call(relation.send(relation_method))
127
+
128
+ if settings.single?
129
+ method = settings.first? ? :first : :last
130
+ db_model = relation.send(method)
131
+ builder = domain_builder(criteria.domain_name)
132
+ domain = builder.call(db_model, criteria)
133
+ ap domain
184
134
  end
185
135
 
186
- collection = create_entity_collection(criteria.domain_name)
187
- collection.entity_loader = entity_loader(criteria, relation, builder)
188
- collection
189
136
  end
190
137
 
191
- # Query will use the database model to build a query based on the
192
- # given criteria. It supports where, order and limit conditions.
193
- #
194
- # @param criteria [SpCore::Criteria]
195
- # @return [SpCore::Domain::Entity, SpCore::Domain::EntityCollection]
196
- def search(criteria)
197
- return execute_criteria(criteria) if criteria.exec?
198
-
199
- begin
200
- relation = query_relation(criteria)
201
- relation = handle_query_conditions(criteria, relation)
202
- build_criteria_entities(criteria, relation)
203
- rescue => e
204
- msg = "query failed for #{criteria.domain}: #{e.class} #{e.message}"
205
- err = RuntimeError.new(msg)
206
- err.set_backtrace(e.backtrace)
207
- raise err
208
- end
138
+ def domain_builder(domain_name)
139
+ key = qualify_container_key(domain_name, 'domain_builders.db')
140
+ app_container[key]
209
141
  end
210
142
 
211
143
  private