daylite-models 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.1.0 / 11.02.2009
2
+
3
+ * Release
4
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Dmitriy Dzema, Infonium Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,25 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.markdown
5
+ Rakefile
6
+ app/models/daylite.rb
7
+ app/models/daylite/contact.rb
8
+ app/models/daylite/organization.rb
9
+ app/models/daylite/organization_task_join.rb
10
+ app/models/daylite/project.rb
11
+ app/models/daylite/project_task_join.rb
12
+ app/models/daylite/role.rb
13
+ app/models/daylite/role_type.rb
14
+ app/models/daylite/task.rb
15
+ app/models/daylite/user.rb
16
+ config/database.yml.exmp
17
+ daylite_models.gemspec
18
+ daylite_models.rb
19
+ init.rb
20
+ install.rb
21
+ lib/active_record/connection_adapters/openbase_adapter.rb
22
+ tasks/users_plugin_tasks.rake
23
+ test/daylite_models_plugin_test.rb
24
+ test/test_helper.rb
25
+ uninstall.rb
data/README.markdown ADDED
@@ -0,0 +1,47 @@
1
+ DayliteModels
2
+ ===========
3
+
4
+ This is a set of ActiveRecord classes, designed to work with Daylite 3 by [Marketcircle](www.marketcircle.com).
5
+
6
+ All the classes are in namespace Daylite. For example Daylite projects live in the class Daylite::Project.
7
+
8
+ Models attributes are mapped to usual ruby underscore_notation from Daylite cameCase notation.
9
+
10
+ Usage
11
+ =======
12
+
13
+ You can use this module as rails plugin and as a standalone library.
14
+
15
+ Usage as Rails plugin
16
+ ---------------------
17
+
18
+ Install the plugin
19
+
20
+ ./script/plugin install <path_to_repo>
21
+
22
+
23
+ Add _daylite_ section to you database.yml
24
+
25
+ daylite:
26
+ adapter: openbase
27
+ database: MyOrganization
28
+ host: localhost
29
+ username: my_username
30
+ password: my_password
31
+
32
+
33
+ Add the following line to you environment.rb
34
+
35
+ Daylite::Base.establish_connection(:daylite)
36
+
37
+ You are ready to rock! Now you can use Daylite classes in you application as usual Rails models.
38
+
39
+ Usage as a standalone library
40
+ -----------------------------
41
+ The easiest way is to create file config/database.yml relative to you running script and place _daylite_ config in it (see Usage as Rails plugin for example). After this you need to place
42
+
43
+ require 'daylite_models'
44
+
45
+ somewhere in you initialization code and you are done.
46
+
47
+ Another way is to provide connection info directly throw Daylite::Base.establish_connection call. You will need to provide adapter, database, host, username and password params. See ActiveRecord::Base.establish_connection and Opnebase documentation for more info.
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ desc 'Generate documentation for the daylite_models plugin.'
7
+ Rake::RDocTask.new(:rdoc) do |rdoc|
8
+ rdoc.rdoc_dir = 'rdoc'
9
+ rdoc.title = 'DayliteModels'
10
+ rdoc.options << '--line-numbers' << '--inline-source'
11
+ rdoc.rdoc_files.include('README.markdown')
12
+ rdoc.rdoc_files.include('lib/**/*.rb')
13
+ end
14
+
15
+ begin
16
+ require 'daylite_models'
17
+ require 'hoe'
18
+
19
+ #oh, shit
20
+ class Hoe
21
+ def extra_deps
22
+ @extra_deps.reject do |x|
23
+ Array(x).first == 'hoe'
24
+ end
25
+ end
26
+ end
27
+
28
+ Hoe.new('daylite-models', DayliteModels::VERSION) do |s|
29
+ s.name = "daylite-models"
30
+ s.version = "0.1.0"
31
+ s.author = "Dmitriy Dzema"
32
+ s.email = "dimad.ag@gmail.com"
33
+ s.summary = "Set of ActiveRecord classes to work with the Daylite 3 database"
34
+
35
+ s.rubyforge_name = 'daylite-models'
36
+ s.remote_rdoc_dir = '' # Release to root
37
+ s.need_zip = true
38
+ s.extra_deps = [ ["activesupport", ">= 2.0.0"], ["activerecord", ">= 2.0.0"], ["activerecord", ">= 0.8.3"]]
39
+ end
40
+ rescue LoadError => e
41
+
42
+ end
@@ -0,0 +1,3 @@
1
+ class Daylite::Contact < Daylite::Base
2
+ has_one :user, :foreign_key => "contactID"
3
+ end
@@ -0,0 +1,17 @@
1
+ class Daylite::Organization < Daylite::Base
2
+ belongs_to :owner, :class_name => "User", :foreign_key => "assignedToID"
3
+
4
+ has_many :organization_task_joins, :foreign_key => "organizationID"
5
+ has_many :tasks, :through => :organization_task_joins
6
+
7
+ has_many :roles, :foreign_key => "organizationID"
8
+ has_many :projects, :through => :roles
9
+
10
+ def projects_tasks
11
+ projects.collect { |p| p.tasks }.flatten
12
+ end
13
+
14
+ def all_tasks
15
+ projects_tasks + tasks
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ class Daylite::OrganizationTaskJoin < Daylite::Base
2
+ belongs_to :organization, :class_name => "Organization", :foreign_key => "organizationID"
3
+ belongs_to :task, :class_name => "Task", :foreign_key => "taskID"
4
+ end
@@ -0,0 +1,24 @@
1
+ class Daylite::Project < Daylite::Base
2
+ belongs_to :owner, :class_name => "User", :foreign_key => "assignedToID"
3
+
4
+ has_many :roles, :foreign_key => "projectID"
5
+ has_many :projects, :through => :roles
6
+
7
+ has_many :project_task_joins, :foreign_key => "projectID"
8
+ has_many :tasks, :through => :project_task_joins
9
+
10
+ def status_codes
11
+ @@status_codes ||= {
12
+ 0 => "In Progress",
13
+ 1 => "Deffered",
14
+ 2 => "Canceled",
15
+ 3 => "Abandoned",
16
+ 4 => "New",
17
+ 7 => "Done",
18
+ }
19
+ end
20
+
21
+ def status_string
22
+ status_codes[attributes["statusCode"]] || "Unknown status"
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ class Daylite::ProjectTaskJoin < Daylite::Base
2
+ belongs_to :project, :foreign_key => "projectID"
3
+ belongs_to :task, :foreign_key => "taskID"
4
+ end
@@ -0,0 +1,10 @@
1
+ class Daylite::Role < Daylite::Base
2
+ belongs_to :organization, :foreign_key => "organizationID"
3
+ belongs_to :project, :foreign_key => "projectID"
4
+
5
+ belongs_to :role_type, :foreign_key => "roleTypeID"
6
+
7
+ def type
8
+ role_type.name
9
+ end
10
+ end
@@ -0,0 +1,2 @@
1
+ class Daylite::RoleType < Daylite::Base
2
+ end
@@ -0,0 +1,17 @@
1
+ class Daylite::Task < Daylite::Base
2
+ belongs_to :owner, :class_name => "User", :foreign_key => "assignedToID"
3
+
4
+ has_many :organization_task_joins, :foreign_key => "taskID"
5
+ has_many :organizations, :through => :organization_task_joins
6
+
7
+ has_many :project_task_joins, :foreign_key => "taskID"
8
+ has_many :projects, :through => :project_task_joins
9
+
10
+ def organization
11
+ organizations[0]
12
+ end
13
+
14
+ def to_s
15
+ "#<Daylite::Task:#{title}>"
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ class Daylite::User < Daylite::Base
2
+ belongs_to :contact, :foreign_key => "contactID"
3
+
4
+ has_many :organizations, :foreign_key => "assignedToID"
5
+ has_many :tasks, :class_name => "Task", :foreign_key => "assignedToID"
6
+
7
+ def name
8
+ contact.cachedName
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ module Daylite
2
+ end
3
+
4
+ class Daylite::Base < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ self.pluralize_table_names = false
7
+
8
+ set_primary_key "_rowid"
9
+ set_table_name do
10
+ self.name.sub("Daylite::", "")
11
+ end
12
+
13
+ # don't allow to save Daylite::Base objects to DB
14
+ def readonly?
15
+ true
16
+ end
17
+
18
+ def method_missing(symbol, *args)
19
+ guess_attribute = symbol.to_s.camelize(:lower).sub("Id", "ID")
20
+ if result = attributes[guess_attribute]
21
+ self.class.class_eval do
22
+ define_method symbol do
23
+ return attributes[guess_attribute]
24
+ end
25
+ end
26
+ result
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ #database connection info.
2
+ #Don't touch this file if you use it as rails plugin
3
+ #Specify daylite: settings in you rails database.yml instead
4
+ daylite:
5
+ adapter: openbase
6
+ database: MyOrganization
7
+ host: localhost
8
+ username: user
9
+ password: password
@@ -0,0 +1,38 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{DayliteModels}
3
+ s.version = "0.1.0"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Dmitriy Dzema"]
7
+ s.date = %q{2009-02-11}
8
+ s.email = %q{dimad.ag@gmail.com}
9
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
10
+ s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.markdown", "Rakefile", "app/models/daylite.rb", "app/models/daylite/contact.rb", "app/models/daylite/organization.rb", "app/models/daylite/organization_task_join.rb", "app/models/daylite/project.rb", "app/models/daylite/project_task_join.rb", "app/models/daylite/role.rb", "app/models/daylite/role_type.rb", "app/models/daylite/task.rb", "app/models/daylite/user.rb", "config/database.yml.exmp", "daylite_models.gemspec", "daylite_models.rb", "init.rb", "install.rb", "lib/active_record/connection_adapters/openbase_adapter.rb", "tasks/users_plugin_tasks.rake", "test/daylite_models_plugin_test.rb", "test/test_helper.rb", "uninstall.rb"]
11
+ s.has_rdoc = true
12
+ s.rdoc_options = ["--main", "README.markdown"]
13
+ s.require_paths = ["lib"]
14
+ s.rubyforge_project = %q{daylite-models}
15
+ s.rubygems_version = %q{1.3.1}
16
+ s.summary = %q{Set of ActiveRecord classes to work with the Daylite 3 database}
17
+ s.test_files = ["test/test_helper.rb"]
18
+
19
+ if s.respond_to? :specification_version then
20
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
21
+ s.specification_version = 2
22
+
23
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
24
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.0.0"])
25
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.0.0"])
26
+ s.add_runtime_dependency(%q<activerecord>, [">= 0.8.3"])
27
+ s.add_development_dependency(%q<hoe>, [">= 1.8.3"])
28
+ else
29
+ s.add_dependency(%q<activesupport>, [">= 2.0.0"])
30
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
31
+ s.add_dependency(%q<activerecord>, [">= 0.8.3"])
32
+ end
33
+ else
34
+ s.add_dependency(%q<activesupport>, [">= 2.0.0"])
35
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
36
+ s.add_dependency(%q<activerecord>, [">= 0.8.3"])
37
+ end
38
+ end
data/daylite_models.rb ADDED
@@ -0,0 +1,17 @@
1
+ $: << File.join(File.dirname(__FILE__), 'lib')
2
+ require 'init'
3
+
4
+ module DayliteModels
5
+ VERSION = '0.1.0'
6
+ end
7
+
8
+ DAYLITE_DATABASE_CONFIG = 'config/database.yml'
9
+ DAYLITE_CONFIG_SECTION = 'daylite'
10
+
11
+ if File.exist?(DAYLITE_DATABASE_CONFIG)
12
+ yaml = YAML::load(File.read(DAYLITE_DATABASE_CONFIG))
13
+
14
+ if yaml[DAYLITE_CONFIG_SECTION]
15
+ Daylite::Base.establish_connection( yaml[DAYLITE_CONFIG_SECTION] )
16
+ end
17
+ end
data/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'active_record'
2
+ require 'active_support'
3
+
4
+ models_path = File.join(File.dirname(__FILE__), 'app', 'models')
5
+ daylite_path = File.join(models_path, 'daylite')
6
+
7
+ $LOAD_PATH << models_path << daylite_path << './lib'
8
+ ActiveSupport::Dependencies.load_paths << models_path << daylite_path << './lib'
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,515 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ # Establishes a connection to the database that's used by all Active Record objects
6
+ def self.openbase_connection(config) # :nodoc:
7
+ require_library_or_gem 'openbase' unless self.class.const_defined?(:OpenBase)
8
+
9
+ config = config.symbolize_keys
10
+ host = config[:host]
11
+ username = config[:username].to_s
12
+ password = config[:password].to_s
13
+
14
+
15
+ if config.has_key?(:database)
16
+ database = config[:database]
17
+ else
18
+ raise ArgumentError, "No database specified. Missing argument: database."
19
+ end
20
+
21
+ oba = ConnectionAdapters::OpenBaseAdapter.new(
22
+ OpenBase.new(database, host, username, password), logger
23
+ )
24
+
25
+ if oba.raw_connection.connected?
26
+ unless oba.tables.include?(ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE)
27
+ oba.execute(<<-SQL,"Creating OpenBase Column Support Table")
28
+ CREATE TABLE #{ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE} (name char, type char, precision int, scale int)
29
+ SQL
30
+ end
31
+ oba.select_all("SELECT * FROM #{ConnectionAdapters::OpenBaseAdapter::COLUMN_SUPPORT_TABLE}").each do |col|
32
+ ConnectionAdapters::OpenBaseAdapter::DECIMAL_COLUMNS.store(col["name"],[col["precision"],col["scale"]])
33
+ end
34
+ end
35
+
36
+ oba
37
+ end
38
+
39
+ end
40
+
41
+ module ConnectionAdapters
42
+ class OpenBaseColumn < Column #:nodoc:
43
+ private
44
+ def simplified_type(field_type)
45
+ return :integer if field_type.downcase =~ /long/
46
+ return :decimal if field_type.downcase == "money"
47
+ return :binary if field_type.downcase == "object"
48
+ super
49
+ end
50
+ end
51
+ # The OpenBase adapter works with the Ruby/Openbase driver by Tetsuya Suzuki.
52
+ # http://www.spice-of-life.net/ruby-openbase/ (needs version 0.7.3+)
53
+ #
54
+ # Options:
55
+ #
56
+ # * <tt>:host</tt> -- Defaults to localhost
57
+ # * <tt>:username</tt> -- Defaults to nothing
58
+ # * <tt>:password</tt> -- Defaults to nothing
59
+ # * <tt>:database</tt> -- The name of the database. No default, must be provided.
60
+ #
61
+ # The OpenBase adapter will make use of OpenBase's ability to generate unique ids
62
+ # for any column with an unique index applied. Thus, if the value of a primary
63
+ # key is not specified at the time an INSERT is performed, the adapter will prefetch
64
+ # a unique id for the primary key. This prefetching is also necessary in order
65
+ # to return the id after an insert.
66
+ #
67
+ # Caveat: Operations involving LIMIT and OFFSET do not yet work!
68
+ #
69
+ # Maintainer: derrick.spell@gmail.com
70
+ class OpenBaseAdapter < AbstractAdapter
71
+ DECIMAL_COLUMNS = {}
72
+ COLUMN_SUPPORT_TABLE = "rails_openbase_column_support"
73
+ def adapter_name
74
+ 'OpenBase'
75
+ end
76
+
77
+ def native_database_types
78
+ {
79
+ :primary_key => "integer NOT NULL UNIQUE INDEX DEFAULT _rowid",
80
+ :string => { :name => "char", :limit => 4096 },
81
+ :text => { :name => "text" },
82
+ :integer => { :name => "integer" },
83
+ :float => { :name => "float" },
84
+ :decimal => { :name => "decimal" },
85
+ :datetime => { :name => "datetime" },
86
+ :timestamp => { :name => "timestamp" },
87
+ :time => { :name => "time" },
88
+ :date => { :name => "date" },
89
+ :binary => { :name => "object" },
90
+ :boolean => { :name => "boolean" }
91
+ }
92
+ end
93
+
94
+ def supports_migrations?
95
+ true
96
+ end
97
+
98
+ def prefetch_primary_key?(table_name = nil)
99
+ true
100
+ end
101
+
102
+ def default_sequence_name(table_name, primary_key) # :nodoc:
103
+ "#{table_name} #{primary_key}"
104
+ end
105
+
106
+ def next_sequence_value(sequence_name)
107
+ ary = sequence_name.split(' ')
108
+ if (!ary[1]) then
109
+ ary[0] =~ /(\w+)_nonstd_seq/
110
+ ary[0] = $1
111
+ end
112
+ @connection.unique_row_id(ary[0], ary[1])
113
+ end
114
+
115
+
116
+ # QUOTING ==================================================
117
+
118
+ def quote(value, column = nil)
119
+ if value.kind_of?(String) && column && column.type == :binary
120
+ "'#{@connection.insert_binary(value)}'"
121
+ elsif value.kind_of?(BigDecimal)
122
+ return "'#{value.to_s}'"
123
+ elsif column && column.type == :integer && column.sql_type =~ /decimal/
124
+ return "'#{value.to_s}'"
125
+ elsif [Float,Fixnum,Bignum].include?(value.class) && column && column.type == :string
126
+ return "'#{value.to_s}'"
127
+ else
128
+ super
129
+ end
130
+ end
131
+
132
+ def quoted_true
133
+ "1"
134
+ end
135
+
136
+ def quoted_false
137
+ "0"
138
+ end
139
+
140
+
141
+
142
+ # DATABASE STATEMENTS ======================================
143
+
144
+ def add_limit_offset!(sql, options) #:nodoc:
145
+ return if options[:limit].nil?
146
+ limit = options[:limit]
147
+ offset = options[:offset]
148
+ if limit == 0
149
+ # Mess with the where clause to ensure we get no results
150
+ if sql =~ /WHERE/i
151
+ sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
152
+ elsif sql =~ /ORDER\s+BY/i
153
+ sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
154
+ else
155
+ sql << 'WHERE 1 = 2'
156
+ end
157
+ elsif offset.nil?
158
+ sql << " RETURN RESULTS #{limit}"
159
+ else
160
+ sql << " RETURN RESULTS #{offset} TO #{limit + offset}"
161
+ end
162
+ end
163
+
164
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
165
+ execute(sql, name)
166
+ update_nulls_after_insert(sql, name, pk, id_value, sequence_name)
167
+ id_value
168
+ end
169
+
170
+ def execute(sql, name = nil) #:nodoc:
171
+ log(sql, name) { @connection.execute(sql) }
172
+ end
173
+
174
+ def direct_execute(sql, name = nil) #:nodoc:
175
+ log(sql, name) { @connection.execute(sql) }
176
+ end
177
+
178
+ def update(sql, name = nil) #:nodoc:
179
+ execute(sql, name).rows_affected
180
+ end
181
+
182
+ alias_method :delete, :update #:nodoc:
183
+
184
+ def begin_db_transaction #:nodoc:
185
+ execute "START TRANSACTION"
186
+ rescue Exception
187
+ # Transactions aren't supported
188
+ end
189
+
190
+ def commit_db_transaction #:nodoc:
191
+ execute "COMMIT"
192
+ rescue Exception
193
+ # Transactions aren't supported
194
+ end
195
+
196
+ def rollback_db_transaction #:nodoc:
197
+ execute "ROLLBACK"
198
+ rescue Exception
199
+ # Transactions aren't supported
200
+ end
201
+
202
+
203
+ # SCHEMA STATEMENTS ========================================
204
+ # Return the list of all tables in the schema search path.
205
+ def tables(name = nil) #:nodoc:
206
+ tables = @connection.tables
207
+ tables.reject! { |t| /\A_SYS_/ === t }
208
+ end
209
+
210
+ def columns(table_name, name = nil) #:nodoc:
211
+ sql = "SELECT * FROM _sys_tables "
212
+ sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
213
+ sql << "ORDER BY columnNumber"
214
+ columns = []
215
+ direct_execute(sql, name).each_hash do |row|
216
+ columns << OpenBaseColumn.new(row["fieldname"],
217
+ default_value(row["defaultvalue"],row["typename"]),
218
+ sql_type_name(table_name,row["fieldname"],row["typename"],row["length"]),
219
+ (row["notnull"] == 1 ? false : true)
220
+ )
221
+ end
222
+ columns
223
+ end
224
+
225
+ def column_names(table_name) #:nodoc:
226
+ sql = "SELECT fieldname FROM _sys_tables "
227
+ sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
228
+ sql << "ORDER BY columnNumber"
229
+ names = direct_execute(sql).fetch_all
230
+ names.flatten! || names
231
+ end
232
+
233
+ def indexes(table_name, name = nil)#:nodoc:
234
+ sql = "SELECT fieldname, notnull, searchindex, uniqueindex, clusteredindex FROM _sys_tables "
235
+ sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
236
+ sql << "AND primarykey=0 "
237
+ sql << "AND (searchindex=1 OR uniqueindex=1 OR clusteredindex=1) "
238
+ sql << "ORDER BY columnNumber"
239
+ indexes = []
240
+ execute(sql, name).each do |row|
241
+ indexes << IndexDefinition.new(table_name,ob_index_name(row),row[3]==1,[row[0]])
242
+ end
243
+ indexes
244
+ end
245
+
246
+ def create_table(name, options = {}) #:nodoc:
247
+ return_value = super
248
+
249
+ # Get my own copy of TableDefinition so that i can detect decimal columns
250
+ table_definition = TableDefinition.new(self)
251
+ yield table_definition
252
+
253
+ table_definition.columns.each do |col|
254
+ if col.type == :decimal
255
+ record_decimal(name, col.name, col.precision, col.scale)
256
+ end
257
+ end
258
+
259
+ unless options[:id] == false
260
+ primary_key = (options[:primary_key] || "id")
261
+ direct_execute("CREATE PRIMARY KEY #{name} (#{primary_key})")
262
+ end
263
+ return_value
264
+ end
265
+
266
+ def rename_table(name, new_name)
267
+ execute "RENAME #{name} #{new_name}"
268
+ end
269
+
270
+ def add_column(table_name, column_name, type, options = {})
271
+ return_value = super(table_name, "COLUMN " + column_name.to_s, type, options)
272
+ if type == :decimal
273
+ record_decimal(table_name, column_name, options[:precision], options[:scale])
274
+ end
275
+ end
276
+
277
+ def remove_column(table_name, column_name)
278
+ execute "ALTER TABLE #{table_name} REMOVE COLUMN #{quote_column_name(column_name)}"
279
+ end
280
+
281
+ def rename_column(table_name, column_name, new_column_name)
282
+ execute "ALTER TABLE #{table_name} RENAME #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
283
+ end
284
+
285
+ def add_column_options!(sql, options) #:nodoc:
286
+ sql << " NOT NULL" if options[:null] == false
287
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
288
+ end
289
+
290
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
291
+ unless options_include_default?(options)
292
+ options[:default] = select_one("SELECT * FROM _sys_tables WHERE tablename='#{table_name}' AND fieldname='#{column_name}'")["defaultvalue"]
293
+ end
294
+
295
+ change_column_sql = "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
296
+ add_column_options!(change_column_sql, options)
297
+ execute(change_column_sql)
298
+ end
299
+
300
+ def change_column_default(table_name, column_name, default)
301
+ execute "ALTER TABLE #{table_name} COLUMN #{column_name} SET DEFAULT #{quote(default)}"
302
+ end
303
+
304
+ def add_index(table_name, column_name, options = {})
305
+ if Hash === options # legacy support, since this param was a string
306
+ index_type = options[:unique] ? "UNIQUE" : ""
307
+ else
308
+ index_type = options
309
+ end
310
+ execute "CREATE #{index_type} INDEX #{table_name} #{column_name}"
311
+ end
312
+
313
+ def remove_index(table_name, options = {})
314
+ execute "DROP INDEX #{table_name} #{options === Hash ? options[:column] : options}"
315
+ end
316
+
317
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
318
+ return super unless type.to_s == 'decimal'
319
+
320
+ if (scale.to_i == 2)
321
+ return 'money'
322
+ elsif (scale.to_i == 0)
323
+ return 'longlong'
324
+ else
325
+ return "char(#{precision.to_i + 1})"
326
+ end
327
+ end
328
+
329
+
330
+
331
+ private
332
+ def select(sql, name = nil)
333
+ decimals = detect_decimals(sql) || []
334
+ sql = add_order_by_rowid(sql)
335
+
336
+ # OpenBase ignores the return results when there is a group by
337
+ # so limit the result set that we return to rails if need be
338
+ if (sql =~ /GROUP BY/i)
339
+ sql.sub!(/RETURN RESULTS (\d+)( TO (\d+))?/i,"")
340
+
341
+ results = execute(sql, name)
342
+ if ($2)
343
+ results.fetch_offset = $1.to_i
344
+ results.fetch_limit = $3.to_i - $1.to_i
345
+ elsif ($1)
346
+ results.fetch_limit = $1.to_i
347
+ end
348
+ else
349
+ results = execute(sql, name)
350
+ end
351
+
352
+ rows = []
353
+ if ( results.rows_affected )
354
+ results.each_hash do |row| # loop through result rows
355
+ # row.delete("_rowid") if row.key?("_rowid")
356
+ decimals.each do |name, precision, scale|
357
+ row[name] = BigDecimal.new(row[name]) if row[name] === String
358
+ end
359
+ rows << row
360
+ end
361
+ end
362
+ rows
363
+ end
364
+
365
+ def default_value(value,type=nil)
366
+ return value if value.nil?
367
+
368
+ # Boolean type values
369
+ return true if value =~ /true/
370
+ return false if value =~ /false/
371
+ # Alternative boolean default declarations
372
+ return true if (value == 1 && type == "boolean")
373
+ return false if (value == 0 && type == "boolean")
374
+
375
+ # Date / Time magic values
376
+ return Time.now.to_s if value =~ /^now\(\)/i
377
+
378
+ # Empty strings should be set to nil
379
+ return nil if value.empty?
380
+
381
+ # Otherwise return what we got from OpenBase
382
+ # and hope for the best...
383
+ # Take off the leading space and unquote
384
+ value.lstrip!
385
+ value = value[1,value.length-2] if value.first.eql?("'") && value.last.eql?("'")
386
+ return nil if value.eql?("NULL")
387
+ return value
388
+ end
389
+
390
+ def sql_type_name(table_name, col_name, type, length)
391
+ full_name = table_name.to_s + "." + col_name.to_s
392
+ if DECIMAL_COLUMNS.include?(full_name) && type != "longlong"
393
+ return "decimal(#{DECIMAL_COLUMNS[full_name][0]},#{DECIMAL_COLUMNS[full_name][1]})"
394
+ end
395
+ return "#{type}(#{length})" if ( type =~ /char/ )
396
+ type
397
+ end
398
+
399
+ def ob_index_name(row = [])
400
+ name = ""
401
+ name << "UNIQUE " if row[3]
402
+ name << "CLUSTERED " if row[4]
403
+ name << "INDEX"
404
+ name
405
+ end
406
+
407
+ def detect_decimals(sql)
408
+ #=begin
409
+ # Detect any decimal columns that will need to be cast when fetched
410
+ decimals = []
411
+ sql =~ /SELECT\s+(.*)\s+FROM\s+(\w+)/i
412
+ select_clause = $1
413
+ main_table = $2
414
+ if select_clause == "*"
415
+ column_names(main_table).each do |col|
416
+ full_name = main_table + "." + col
417
+ if DECIMAL_COLUMNS.include?(full_name)
418
+ decimals << [col,DECIMAL_COLUMNS[full_name][0].to_i,DECIMAL_COLUMNS[full_name][1].to_i]
419
+ end
420
+ end
421
+ end
422
+ return decimals
423
+ =begin {
424
+ # Change table.* to list of columns in table
425
+ while (sql =~ /SELECT.*\s(\w+)\.\*/)
426
+ table = $1
427
+ cols = columns(table)
428
+ if ( cols.size == 0 ) then
429
+ # Maybe this is a table alias
430
+ sql =~ /FROM(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
431
+ $1 =~ /[\s|,](\w+)\s+#{table}[\s|,]/ # get the tablename for this alias
432
+ cols = columns($1)
433
+ end
434
+ select_columns = []
435
+ cols.each do |col|
436
+ select_columns << table + '.' + col.name
437
+ end
438
+ sql.gsub!(table + '.*',select_columns.join(", ")) if select_columns
439
+ end
440
+
441
+ # Change JOIN clause to table list and WHERE condition
442
+ }
443
+ =begin {
444
+ while (sql =~ /JOIN/)
445
+ sql =~ /((LEFT )?(OUTER )?JOIN (\w+) ON )(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
446
+ join_clause = $1 + $5
447
+ is_outer_join = $3
448
+ join_table = $4
449
+ join_condition = $5
450
+ join_condition.gsub!(/=/,"*") if is_outer_join
451
+ if (sql =~ /WHERE/)
452
+ sql.gsub!(/WHERE/,"WHERE (#{join_condition}) AND")
453
+ else
454
+ sql.gsub!(join_clause,"#{join_clause} WHERE #{join_condition}")
455
+ end
456
+ sql =~ /(FROM .+?)(?:LEFT|OUTER|JOIN|WHERE|$)/
457
+ from_clause = $1
458
+ sql.gsub!(from_clause,"#{from_clause}, #{join_table} ")
459
+ sql.gsub!(join_clause,"")
460
+ end
461
+
462
+
463
+ # ORDER BY _rowid if no explicit ORDER BY
464
+ # This will ensure that find(:first) returns the first inserted row
465
+ if (sql !~ /(ORDER BY)|(GROUP BY)/)
466
+ if (sql =~ /RETURN RESULTS/)
467
+ sql.sub!(/RETURN RESULTS/,"ORDER BY _rowid RETURN RESULTS")
468
+ else
469
+ sql << " ORDER BY _rowid"
470
+ end
471
+ end
472
+ }
473
+ =end
474
+ end
475
+
476
+ def add_order_by_rowid(sql)
477
+ # ORDER BY _rowid if no explicit ORDER BY
478
+ # This will ensure that find(:first) returns the first inserted row
479
+ if (sql !~ /(ORDER BY)|(GROUP BY)/)
480
+ if (sql =~ /RETURN RESULTS/)
481
+ sql.sub!(/RETURN RESULTS/,"ORDER BY _rowid RETURN RESULTS")
482
+ else
483
+ sql << " ORDER BY _rowid"
484
+ end
485
+ end
486
+ sql
487
+ end
488
+
489
+ def record_decimal(table_name, column_name, precision, scale)
490
+ full_name = table_name.to_s + "." + column_name.to_s
491
+ DECIMAL_COLUMNS.store(full_name, [precision.to_i,scale.to_i])
492
+ direct_execute("INSERT INTO #{COLUMN_SUPPORT_TABLE} (name,type,precision,scale) VALUES ('#{full_name}','decimal',#{precision.to_i},#{scale.to_i})")
493
+ end
494
+
495
+ def update_nulls_after_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
496
+ sql =~ /INSERT INTO (\w+) \((.*)\) VALUES\s*\((.*)\)/m
497
+ table = $1
498
+ cols = $2
499
+ values = $3
500
+ cols = cols.split(',')
501
+ values.gsub!(/'[^']*'/,"''")
502
+ values.gsub!(/"[^"]*"/,"\"\"")
503
+ values = values.split(',')
504
+ update_cols = []
505
+ values.each_index { |index| update_cols << cols[index] if values[index] =~ /\s*NULL\s*/ }
506
+ update_sql = "UPDATE #{table} SET"
507
+ update_cols.each { |col| update_sql << " #{col}=NULL," unless col.empty? }
508
+ update_sql.chop!()
509
+ update_sql << " WHERE #{pk}=#{quote(id_value)}"
510
+ direct_execute(update_sql,"Null Correction") if update_cols.size > 0
511
+ end
512
+
513
+ end
514
+ end
515
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :users_plugin do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class DayliteModelsPluginTest < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ test "the truth" do
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: daylite-models
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dmitriy Dzema
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-11 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.3
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: hoe
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.3
54
+ version:
55
+ description:
56
+ email: dimad.ag@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - History.txt
63
+ - Manifest.txt
64
+ files:
65
+ - History.txt
66
+ - LICENSE
67
+ - Manifest.txt
68
+ - README.markdown
69
+ - Rakefile
70
+ - app/models/daylite.rb
71
+ - app/models/daylite/contact.rb
72
+ - app/models/daylite/organization.rb
73
+ - app/models/daylite/organization_task_join.rb
74
+ - app/models/daylite/project.rb
75
+ - app/models/daylite/project_task_join.rb
76
+ - app/models/daylite/role.rb
77
+ - app/models/daylite/role_type.rb
78
+ - app/models/daylite/task.rb
79
+ - app/models/daylite/user.rb
80
+ - config/database.yml.exmp
81
+ - daylite_models.gemspec
82
+ - daylite_models.rb
83
+ - init.rb
84
+ - install.rb
85
+ - lib/active_record/connection_adapters/openbase_adapter.rb
86
+ - tasks/users_plugin_tasks.rake
87
+ - test/daylite_models_plugin_test.rb
88
+ - test/test_helper.rb
89
+ - uninstall.rb
90
+ has_rdoc: true
91
+ homepage:
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --main
95
+ - README.txt
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ version:
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ version:
110
+ requirements: []
111
+
112
+ rubyforge_project: daylite-models
113
+ rubygems_version: 1.3.1
114
+ signing_key:
115
+ specification_version: 2
116
+ summary: Set of ActiveRecord classes to work with the Daylite 3 database
117
+ test_files:
118
+ - test/test_helper.rb