dm-frontbase-adapter 1.1.0

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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ nbproject
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dm-frontbase-adapter.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dm-frontbase-adapter"
7
+ s.version = '1.1.0'
8
+ s.authors = ["zapo"]
9
+ s.email = ["antoine.niek@supinfo.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{gem summary}
12
+ s.description = %q{gem description}
13
+
14
+ s.rubyforge_project = "dm-frontbase-adapter"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency 'ruby-frontbase'
24
+ s.add_dependency "dm-core"
25
+ s.add_dependency "dm-validations"
26
+ s.add_dependency "dm-types"
27
+ end
@@ -0,0 +1,14 @@
1
+ require 'dm-core'
2
+
3
+ class FrontbaseAdapter < ::DataMapper::Adapters::AbstractAdapter
4
+ Inflector = ::DataMapper.const_defined?(:Inflector) ? ::DataMapper::Inflector : ::Extlib::Inflection
5
+ end
6
+
7
+
8
+ # add our adapter to datamapper adapter list
9
+
10
+ ::DataMapper::Adapters::FrontbaseAdapter = FrontbaseAdapter
11
+ ::DataMapper::Adapters.const_added(:FrontbaseAdapter)
12
+
13
+ require 'frontbase'
14
+ require "dm-frontbase-adapter/adapter"
@@ -0,0 +1,115 @@
1
+ require 'dm-frontbase-adapter/connection'
2
+ require 'dm-frontbase-adapter/sql_query'
3
+
4
+
5
+ class FrontbaseAdapter < ::DataMapper::Adapters::AbstractAdapter
6
+
7
+ # cache data per operation accessor
8
+ attr_accessor :data
9
+
10
+ # frontbase operations accessor
11
+ attr_accessor :models_operations
12
+
13
+ def field_naming_convention
14
+ proc {|property| property.name.to_s }
15
+ end
16
+
17
+ def resource_naming_convention
18
+ proc {|resource| resource.to_s }
19
+ end
20
+
21
+ def initialize(name, options)
22
+
23
+ # store our options in a hash :sym => value
24
+ @options = options.inject({}) {|memo, (k,v)| memo[k.to_sym] = v; memo}
25
+ @options[:encoding] ||= 'iso-8859-1'
26
+
27
+ # initialize abstract adapter
28
+ super(name, @options)
29
+ end
30
+
31
+ def log msg
32
+ DataMapper.logger.info "FrontbaseAdapter: #{msg}"
33
+ end
34
+
35
+ def connection
36
+ @connection ||= FrontbaseAdapter::Connection.new(@options)
37
+ end
38
+
39
+ def with_connection &block
40
+ block.call(connection) if block
41
+ rescue
42
+ raise $!
43
+ ensure
44
+ connection.close
45
+ end
46
+
47
+ # Simple read method that take a DataMapper::Query object that represent the query
48
+ # Returns a filtered data hash built from the query model operation returned xml
49
+ def read(query)
50
+
51
+ properties = query.fields
52
+
53
+ statement = SQLQuery.new(query, :select).to_s
54
+
55
+ log statement
56
+
57
+ records = with_connection { |connection|
58
+ response_to_a(connection.query(statement), properties)
59
+ }
60
+
61
+ query.filter_records(records)
62
+ end
63
+
64
+ def response_to_a response, props = nil
65
+ columns = response.columns
66
+ result = response.result
67
+
68
+ result.inject([]) do |arr, record|
69
+
70
+ record = Hash[*columns.zip(record).flatten].inject({}) do |hash, (column, value)|
71
+ if props && (prop = props.find {|prop| prop.field.to_sym == column.to_sym })
72
+
73
+ case
74
+ when prop.is_a?(::DataMapper::Property::Boolean)
75
+ value = case
76
+ when [1.0, 1, true, "true"].include?(value)
77
+ true
78
+ else
79
+ false
80
+ end
81
+ when prop.kind_of?(::DataMapper::Property::String)
82
+ value = value.to_s.force_encoding('ISO-8859-1').encode('UTF-8', :undef => :replace, :invalid => :replace)
83
+ end
84
+
85
+ value = prop.typecast(value)
86
+
87
+ elsif value.kind_of? String
88
+ value = value.to_s.force_encoding('ISO-8859-1').encode('UTF-8', :undef => :replace, :invalid => :replace)
89
+ end
90
+
91
+ hash[column] = value
92
+ hash
93
+ end
94
+ arr << record
95
+ arr
96
+ end
97
+ end
98
+
99
+ def describe storage_name
100
+ with_connection do |connection|
101
+ connection.describe storage_name
102
+ end
103
+ end
104
+
105
+ def show_tables
106
+ with_connection do |connection|
107
+
108
+ stmt = 'SELECT * FROM INFORMATION_SCHEMA.SCHEMATA T0, INFORMATION_SCHEMA.TABLES T1 WHERE T0."SCHEMA_PK" = T1."SCHEMA_PK";'
109
+ log stmt
110
+
111
+ records = response_to_a(connection.query(stmt))
112
+ records.find_all {|record| record[:SCHEMA_NAME] != 'INFORMATION SCHEMA' && record[:TABLE_TYPE] == 'BASE_TABLE'}.map {|record| record[:TABLE_NAME]}
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,74 @@
1
+ class FrontbaseAdapter < ::DataMapper::Adapters::AbstractAdapter
2
+
3
+ # Connection wrapper
4
+
5
+ class Connection
6
+
7
+ def initialize(options)
8
+ default = {
9
+ :host => 'localhost',
10
+ :port => -1,
11
+ :user => '',
12
+ :password => '',
13
+ :dbpassword => '',
14
+ :database => '',
15
+ }
16
+
17
+ @options = default.merge(options)
18
+ connection
19
+ end
20
+
21
+ def connection
22
+ unless @connection
23
+ @connection = FBSQL_Connect.new(
24
+ @options[:host],
25
+ @options[:port],
26
+ @options[:database],
27
+ @options[:user],
28
+ @options[:password],
29
+ @options[:dbpassword]
30
+ )
31
+ @connection.input_charset = encoding_map['UTF-8']
32
+ @connection.output_charset = encoding_map['ISO-8859-1']
33
+ end
34
+
35
+ @connection
36
+ end
37
+
38
+ def query str
39
+ connection.query str
40
+ end
41
+
42
+ def encoding_map
43
+ {
44
+ 'ISO-8859-1' => FBSQL_Connect::FBC_ISO8859_1,
45
+ 'UTF-8' => FBSQL_Connect::FBC_UTF_8
46
+ }
47
+ end
48
+
49
+ def describe(table)
50
+ sql = %[SELECT T3."COLUMN_NAME" AS NAME, T4."DATA_TYPE" FROM
51
+ INFORMATION_SCHEMA.CATALOGS T0,
52
+ INFORMATION_SCHEMA.SCHEMATA T1,
53
+ INFORMATION_SCHEMA.TABLES T2,
54
+ INFORMATION_SCHEMA.COLUMNS T3,
55
+ INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T4
56
+
57
+ WHERE
58
+ T0."CATALOG_PK" = T1."CATALOG_PK" AND
59
+ T1."SCHEMA_PK" = T2."SCHEMA_PK" AND
60
+ T2."TABLE_PK" = T3."TABLE_PK" AND
61
+ T3."COLUMN_PK" = T4."COLUMN_NAME_PK" AND
62
+ T1."SCHEMA_NAME" LIKE CURRENT_SCHEMA AND
63
+ T2."TABLE_NAME" LIKE '#{table}';]
64
+ connection.query(sql)
65
+ end
66
+
67
+ def close
68
+ connection.close
69
+ @connection = nil
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,142 @@
1
+ class FrontbaseAdapter
2
+ class SQLQuery
3
+
4
+ include DataMapper::Query::Conditions
5
+
6
+ attr_reader :conditions, :order, :type, :from, :columns
7
+
8
+ def initialize(query, type)
9
+ @type = type
10
+ @query = query
11
+
12
+ setup_statement
13
+ end
14
+
15
+ def setup_statement
16
+
17
+ @conditions = (@query.conditions) ?
18
+ conditions_statement(@query.conditions) : ''
19
+
20
+ @order = (@query.order && !@query.order.empty?) ?
21
+ order_statement(@query.order) : ''
22
+
23
+ @columns = columns_statement @query.fields
24
+
25
+ @from = quote_name(@query.model.storage_name(@query.repository.name))
26
+ end
27
+
28
+ def order_statement orders
29
+ orders.map {|o| "#{o.target.field} #{o.operator.to_s.upcase}" }.join(', ')
30
+ end
31
+
32
+ def columns_statement properties
33
+ properties.map {|property| property_to_column_name(property) }.join(', ')
34
+ end
35
+
36
+ def to_s
37
+
38
+ statement = ''
39
+
40
+ if @type == :select
41
+ statement << "SELECT #{columns}"
42
+ statement << " FROM #{from}"
43
+ statement << " WHERE #{conditions}" unless conditions.to_s.empty?
44
+ statement << " ORDER BY #{order}" unless order.empty?
45
+ statement << ";"
46
+
47
+ if @query.limit || (@query.limit && @query.offset > 0)
48
+
49
+ replacement = "SELECT TOP("
50
+ replacement << "#{@query.offset.to_i}," if @query.limit && @query.offset > 0
51
+ replacement << "#{@query.limit.to_i}" if @query.limit
52
+ replacement << ")"
53
+
54
+ statement.gsub!('SELECT', replacement)
55
+ end
56
+ end
57
+
58
+ statement
59
+ end
60
+
61
+ def conditions_statement(conditions)
62
+ case conditions
63
+ when AbstractOperation then operation_statement(conditions)
64
+ when AbstractComparison then comparison_statement(conditions)
65
+ end
66
+ end
67
+
68
+ def operation_statement(operation)
69
+ case operation
70
+ when NotOperation then "NOT(#{conditions_statement(operation.first)})"
71
+ when AndOperation then "(#{operation.map {|op| conditions_statement(op) }.join(' AND ')})"
72
+ when OrOperation then "(#{operation.map {|op| conditions_statement(op) }.join(' OR ')})"
73
+ end
74
+ end
75
+
76
+ def comparison_statement(comparison)
77
+
78
+ return conditions_statement(comparison.foreign_key_mapping) if comparison.relationship?
79
+
80
+ value = comparison.value
81
+ subject = property_to_column_name comparison.subject
82
+
83
+ operator = case comparison
84
+ when EqualToComparison then '='
85
+ when GreaterThanComparison then '>'
86
+ when LessThanComparison then '<'
87
+ when GreaterThanOrEqualToComparison then '>='
88
+ when LessThanOrEqualToComparison then '<='
89
+ when LikeComparison then 'LIKE'
90
+ when InclusionComparison then include_operator(value)
91
+ end
92
+
93
+ "#{subject} #{operator} #{quote_value(value, comparison.subject)}"
94
+ end
95
+
96
+ def include_operator(value)
97
+ case value
98
+ when Array then 'IN'
99
+ when Range then 'BETWEEN'
100
+ end
101
+ end
102
+
103
+ def property_to_column_name(prop)
104
+ res = case prop
105
+ when DataMapper::Property
106
+ quote_name(prop.field)
107
+ when DataMapper::Query::Path
108
+ rels = prop.relationships
109
+ names = rels.map {|r| storage_name(r, @query.repository) }.join(".")
110
+ "#{names}.#{quote_name(prop.field)}"
111
+ end
112
+
113
+ res
114
+ end
115
+
116
+ def quote_name(name)
117
+ "\"#{name.gsub('"', '""')}\""
118
+ end
119
+
120
+ def storage_name(rel, repository)
121
+ rel.parent_model.storage_name(repository.name)
122
+ end
123
+
124
+
125
+ def quote_value(value, property)
126
+ if property.kind_of? DataMapper::Property::Boolean
127
+ return value == DataMapper::Property::Boolean::TRUE ? 'TRUE' : 'FALSE'
128
+ end
129
+
130
+ case
131
+ when value.kind_of?(Array)
132
+ "(#{value.map {|v| quote_value(v, property)}.join(", ")})"
133
+ when value.kind_of?(NilClass)
134
+ "NULL"
135
+ when value.kind_of?(String)
136
+ "'#{value.to_s.gsub(/'/, "\\'").gsub(/\\/, %{\\\\})}'"
137
+ else
138
+ value.to_s
139
+ end
140
+ end
141
+ end
142
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-frontbase-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - zapo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-frontbase
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: dm-core
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: dm-validations
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: dm-types
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: gem description
79
+ email:
80
+ - antoine.niek@supinfo.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - Rakefile
88
+ - dm-frontbase-adapter.gemspec
89
+ - lib/dm-frontbase-adapter.rb
90
+ - lib/dm-frontbase-adapter/adapter.rb
91
+ - lib/dm-frontbase-adapter/connection.rb
92
+ - lib/dm-frontbase-adapter/sql_query.rb
93
+ homepage: ''
94
+ licenses: []
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project: dm-frontbase-adapter
113
+ rubygems_version: 1.8.23
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: gem summary
117
+ test_files: []