rom-fmp 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ ### NOT CURRENTLY USED ###
2
+
3
+ module ROM
4
+ module FMP
5
+ # @private
6
+ class Header
7
+ include Equalizer.new(:columns, :table)
8
+
9
+ attr_reader :columns, :table
10
+
11
+ def initialize(columns, table)
12
+ @columns = columns
13
+ @table = table
14
+ end
15
+
16
+ def to_ary
17
+ columns
18
+ end
19
+ alias_method :to_a, :to_ary
20
+
21
+ def to_h
22
+ columns.each_with_object({}) do |col, h|
23
+ left, right = col.to_s.split('___')
24
+ h[left.to_sym] = (right || left).to_sym
25
+ end
26
+ end
27
+
28
+ def names
29
+ columns.map { |col| :"#{col.to_s.split('___').last}" }
30
+ end
31
+
32
+ def project(*names)
33
+ self.class.new(columns.find_all { |col| names.include?(col) }, table)
34
+ end
35
+
36
+ def qualified
37
+ self.class.new(columns.map { |col| :"#{table}__#{col}" }, table)
38
+ end
39
+
40
+ def rename(options)
41
+ self.class.new(columns.map { |col|
42
+ new_name = options[col]
43
+
44
+ if new_name
45
+ :"#{col}___#{new_name}"
46
+ else
47
+ col
48
+ end
49
+ }, table)
50
+ end
51
+
52
+ def prefix(col_prefix)
53
+ rename(Hash[columns.map { |col| [col, :"#{col_prefix}_#{col}"] }])
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,55 @@
1
+ # This is an alternative version of rom-fmp, with all moduels & classes in a single file.
2
+
3
+ require 'logger'
4
+ require 'rom'
5
+ require 'rfm'
6
+ require 'yaml'
7
+
8
+ class Rfm::Layout
9
+ def to_a
10
+ all(:max_records=>10)
11
+ end
12
+
13
+ def each
14
+ # passes block - if any - to upstream each.
15
+ to_a.each(&Proc.new)
16
+ end
17
+ end
18
+
19
+ module ROM
20
+ module FMP
21
+
22
+
23
+ class Gateway < ROM::Gateway
24
+ attr_reader :datasets
25
+
26
+ def initialize(*options)
27
+ @datasets = Rfm.database(options[0].to_h.merge(FMRESULTSET_TEMPLATE).to_h)
28
+ end
29
+
30
+ def dataset(name)
31
+ datasets[name.to_s]
32
+ end
33
+
34
+ # This is required per lint specs
35
+ alias_method :[], :dataset
36
+
37
+ def dataset?(name)
38
+ datasets.layouts.key?(name.to_s)
39
+ end
40
+ end
41
+
42
+
43
+ class Relation < ROM::Relation
44
+ # we must configure adapter identifier here
45
+ adapter :fmp
46
+
47
+ forward :find, :any, :all
48
+ end
49
+
50
+
51
+
52
+
53
+
54
+ end # FMP
55
+ end # ROM
@@ -0,0 +1,161 @@
1
+ # This is an alternative version of rom-fmp, with all moduels & classes in a single file.
2
+
3
+ require 'logger'
4
+ require 'rom/gateway'
5
+ require 'rfm'
6
+ require 'charlatan'
7
+ require 'yaml'
8
+
9
+ class Rfm::Layout
10
+ # Strip out unnecessary output in Rfm::Layout#inspect
11
+ def inspect
12
+ "#<#{self.class.name}:#{self.object_id} @name=#{self.name} @database=#{database.name} @server=#{server.host_name} @loaded=#{@loaded}>"
13
+ end
14
+ end
15
+
16
+ module ROM
17
+ module FMP
18
+
19
+
20
+ class Gateway < ROM::Gateway
21
+ attr_reader :datasets, :database
22
+
23
+ def initialize(*options)
24
+ @database = Rfm.database(options[0].to_h.merge(FMRESULTSET_TEMPLATE).to_h)
25
+ @datasets = Hash.new
26
+ end
27
+
28
+ def dataset(name)
29
+ datasets[name.to_s] ||= Dataset.new(@database[name.to_s])
30
+ end
31
+
32
+ # This is required per lint specs
33
+ alias_method :[], :dataset
34
+
35
+ def dataset?(name)
36
+ datasets.key?(name.to_s)
37
+ end
38
+ end
39
+
40
+
41
+ class Dataset
42
+ include ::Rfm::Scope
43
+
44
+ DEFAULT_REQUEST_OPTIONS = {}
45
+
46
+ # Dataset instance expects to hold Array of data in @data,
47
+ # but it will also hold a FM Layout instance.
48
+ # If any call to Dataset instance returns Array instance,
49
+ # it will be wrapped in a new Dataset instance.
50
+ include Charlatan.new(:data, kind: Array)
51
+ attr_reader :layout, :data, :queries
52
+
53
+ # Store layout, data, query in new dataset.
54
+ def initialize(_layout, _data=[], _queries=[])
55
+ @layout = _layout
56
+ @queries = _queries
57
+ #puts "DATASET NEW queries:#{@queries}"
58
+ super(_data)
59
+ end
60
+
61
+
62
+
63
+ # Creates new dataset with current args and resultset. Not lazy.
64
+ # This may not be how rom or sql uses 'where'. Find out more.
65
+ def where(*args)
66
+ #self.class.new(layout, layout.find(*args), args)
67
+ get_results(:find, args)
68
+ end
69
+
70
+ # Creates new dataset with existing data & queries, plus new query
71
+ def find(*args)
72
+ #self.class.new(layout, data, (queries.dup << args))
73
+ wrap_data(data, (queries.dup << args))
74
+ end
75
+
76
+ def any(options={})
77
+ wrap_data(layout.any(options))
78
+ end
79
+
80
+ def all(options={})
81
+ wrap_data(layout.all(DEFAULT_REQUEST_OPTIONS.merge(options)))
82
+ end
83
+
84
+ def count(*args)
85
+ compiled_query = compile_query
86
+ compiled_query ? layout.count(*compiled_query) : layout.total_count
87
+ end
88
+
89
+ def create(args={})
90
+ get_results(:create, [args]) unless args.empty?
91
+ end
92
+
93
+ def update(record_id, args={})
94
+ get_results(:edit, [record_id, args]) unless args.empty?
95
+ end
96
+
97
+ def delete(record_id)
98
+ get_results(:delete, record_id)
99
+ end
100
+
101
+
102
+
103
+ # Triggers actual fm action.
104
+ def to_a
105
+ (data.nil? || data.empty?) ? call.data.to_a : data.to_a
106
+ end
107
+
108
+ # Triggers actual fm action.
109
+ def each
110
+ # passes block - if any - to upstream each.
111
+ to_a.each(&Proc.new)
112
+ end
113
+
114
+ # Combines all queries, sends to FM, returns result in new dataset.
115
+ def call
116
+ compiled_query = compile_query
117
+ wrap_data(compiled_query ? layout.find(*compiled_query) : layout.all(DEFAULT_REQUEST_OPTIONS))
118
+ end
119
+
120
+ # Mixes chained queries together into single query.
121
+ # Now works with multiple-request queries (using new rfm scope feature).
122
+ # Other ways: consider mixing multi-request queries with intersection: (result1 & result2),
123
+ # or with the new scope feature: query1(scope:query2(scope:query3))
124
+ def compile_query
125
+ #puts "DATASET COMPILE self #{self}"
126
+ #puts "DATASET COMPILE queries #{queries}"
127
+
128
+ # Old way: works but doesn't handle fmp compound queries.
129
+ #query.each_with_object([{},{}]){|x,o| o[0].merge!(x[0] || {}); o[1].merge!(x[1] || {})}
130
+
131
+ # New way: handles compound queries. Reqires ginjo-rfm 3.0.11.
132
+ return unless queries # This should help introspecting dataset that results from record deletion. TODO: test this.
133
+ queries.inject {|new_query,scope| apply_scope(new_query, scope)} ##puts "SCOPE INJECTION scope:#{scope} new_query:#{new_query}";
134
+ end
135
+
136
+ # Returns new dataset containing, data, layout, query.
137
+ def wrap_data(_data=data, _queries=queries, _layout=layout)
138
+ self.class.new(_layout, _data, _queries)
139
+ end
140
+
141
+ # Send method & query to layout, and wrap results in new dataset.
142
+ def get_results(_method, query=queries, _layout=layout)
143
+ wrap_data(_layout.send(_method, *query), query, _layout)
144
+ end
145
+
146
+ end # Dataset
147
+
148
+
149
+ class Relation < ROM::Relation
150
+ # we must configure adapter identifier here
151
+ adapter :fmp
152
+
153
+ forward :find, :all, :count, :create, :update, :delete
154
+ end
155
+
156
+
157
+
158
+
159
+
160
+ end # FMP
161
+ end # ROM
@@ -0,0 +1,91 @@
1
+ # This is an alternative version of rom-fmp, with all moduels & classes in a single file.
2
+
3
+ require 'logger'
4
+ require 'rom/gateway'
5
+ require 'rom/fmp/commands'
6
+ require 'rfm'
7
+ require 'yaml'
8
+
9
+
10
+ module ROM
11
+ module FMP
12
+
13
+ class Relation < ROM::Relation
14
+ adapter :fmp
15
+ forward :find, :where, :all, :any, :count
16
+ end
17
+
18
+
19
+ class Gateway < ROM::Gateway
20
+ attr_reader :sets
21
+
22
+ def initialize(uri, options = {})
23
+ puts "INITIALIZING GATEWAY WITH uri: #{uri} options: #{options}"
24
+ @connection = connect(uri, options)
25
+ @sets = {}
26
+ self
27
+ end
28
+
29
+ def dataset(name)
30
+ sets[name] = Dataset.new(name, connection)
31
+ end
32
+
33
+ def [](name)
34
+ sets.fetch(name)
35
+ end
36
+
37
+ def dataset?(name)
38
+ sets.key?(name)
39
+ end
40
+
41
+ def connect(*args)
42
+ options = args.last.kind_of?(Hash) ? args.pop : Hash.new
43
+ options.merge! args.pop if args.last.kind_of?(Hash)
44
+ case args[0]
45
+ when Rfm::Database
46
+ args[0]
47
+ else
48
+ #::Rfm::Database.new(uri[:database], *Array([uri.to_s, *args]).flatten)
49
+ #Rfm.layout(storage_name, gateway.adapter.options.merge(FMRESULTSET_TEMPLATE).symbolize_keys)
50
+ Rfm.database(*args, options.to_h.merge(FMRESULTSET_TEMPLATE).to_h)
51
+ end
52
+ end
53
+
54
+ end # Gateway
55
+
56
+
57
+ class Dataset
58
+ #include Equalizer.new(:name, :connection)
59
+
60
+ attr_reader :name, :connection, :table
61
+
62
+ def initialize(name, connection)
63
+ puts "INITIALIZING DATASET WITH name: #{name} connection: #{connection}"
64
+ @name = name
65
+ @connection = connection
66
+ @table = connection[name.to_s]
67
+ self
68
+ end
69
+
70
+ def find(*args)
71
+ table.find(*args)
72
+ end
73
+
74
+ def each(&block)
75
+ table.all(:max_records=>10).each(&block)
76
+ end
77
+
78
+ def to_a
79
+ table.all(:max_records=>10)
80
+ end
81
+
82
+ private
83
+
84
+ # def table
85
+ # @table ||= connection[name]
86
+ # end
87
+
88
+ end # Dataset
89
+
90
+ end # FMP
91
+ end # ROM
@@ -0,0 +1,18 @@
1
+ require 'rom'
2
+ #require 'rom/fmp/header'
3
+ #require 'rom/fmp/relation/class_methods'
4
+ #require 'rom/fmp/relation/inspection'
5
+ #require 'rom/fmp/relation/associations'
6
+
7
+ module ROM
8
+ module FMP
9
+
10
+ class Relation < ROM::Relation
11
+ # we must configure adapter identifier here
12
+ adapter :fmp
13
+
14
+ forward :find, :all, :count, :create, :update, :delete
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,104 @@
1
+ module ROM
2
+ module SQL
3
+ class Relation < ROM::Relation
4
+ module Associations
5
+ # Join configured association.
6
+ #
7
+ # Uses INNER JOIN type.
8
+ #
9
+ # @example
10
+ #
11
+ # setup.relation(:tasks)
12
+ #
13
+ # setup.relations(:users) do
14
+ # one_to_many :tasks, key: :user_id
15
+ #
16
+ # def with_tasks
17
+ # association_join(:tasks, select: [:title])
18
+ # end
19
+ # end
20
+ #
21
+ # @api public
22
+ def association_join(name, options = {})
23
+ graph_join(name, :inner, options)
24
+ end
25
+
26
+ # Join configured association
27
+ #
28
+ # Uses LEFT JOIN type.
29
+ #
30
+ # @example
31
+ #
32
+ # setup.relation(:tasks)
33
+ #
34
+ # setup.relations(:users) do
35
+ # one_to_many :tasks, key: :user_id
36
+ #
37
+ # def with_tasks
38
+ # association_left_join(:tasks, select: [:title])
39
+ # end
40
+ # end
41
+ #
42
+ # @api public
43
+ def association_left_join(name, options = {})
44
+ graph_join(name, :left_outer, options)
45
+ end
46
+
47
+ # @api private
48
+ def graph_join(name, join_type, options = {})
49
+ assoc = model.association_reflection(name)
50
+
51
+ key = assoc[:key]
52
+ type = assoc[:type]
53
+
54
+ if type == :many_to_many
55
+ select = options[:select] || {}
56
+ graph_join_many_to_many(name, assoc, select)
57
+ else
58
+ graph_join_other(name, key, type, join_type, options)
59
+ end
60
+ end
61
+
62
+ # @api private
63
+ def graph(*args)
64
+ __new__(dataset.__send__(__method__, *args))
65
+ end
66
+
67
+ private
68
+
69
+ def graph_join_many_to_many(name, assoc, select)
70
+ l_select, r_select =
71
+ if select.is_a?(Hash)
72
+ [select[assoc[:join_table]] || [], select[name]]
73
+ else
74
+ [[], select]
75
+ end
76
+
77
+ l_graph = graph(
78
+ assoc[:join_table],
79
+ { assoc[:left_key] => primary_key },
80
+ select: l_select, implicit_qualifier: self.name
81
+ )
82
+
83
+ l_graph.graph(
84
+ name, { primary_key => assoc[:right_key] }, select: r_select
85
+ )
86
+ end
87
+
88
+ def graph_join_other(name, key, type, join_type, options)
89
+ join_keys =
90
+ if type == :many_to_one
91
+ { primary_key => key }
92
+ else
93
+ { key => primary_key }
94
+ end
95
+
96
+ graph(
97
+ name, join_keys,
98
+ options.merge(join_type: join_type, implicit_qualifier: self.name)
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end