lib/engine2/core.rb in engine2-1.0.4 vs lib/engine2/core.rb in engine2-1.0.5
- old
+ new
@@ -1,6 +1,7 @@
-#coding: utf-8
+# coding: utf-8
+# frozen_string_literal: true
module PrettyJSON
def to_json_pretty
JSON.pretty_generate(self)
end
@@ -11,32 +12,50 @@
# super
to_s('f')
end
end
+class Sequel::SQL::QualifiedIdentifier
+ def to_json(*)
+ "\"#{table}__#{column}\""
+ end
+
+ def to_sym
+ :"#{table}__#{column}"
+ end
+end
+
class Object
def instance_variables_hash
- instance_variables.inject({}) do |h, i|
+ instance_variables.reduce({}) do |h, i|
h[i] = instance_variable_get(i)
h
end
end
end
class Proc
def to_json(*)
loc = source_location
- "\"#<Proc:#{loc.first[/\w+.rb/]}:#{loc.last}>\""
+ loc ? "\"#<Proc:#{loc.first[/\w+.rb/]}:#{loc.last}>\"" : '"source unknown"'
end
def chain &blk
- proc = self
+ prc = self
lambda do |obj|
- obj.instance_eval(&proc)
+ obj.instance_eval(&prc)
obj.instance_eval(&blk)
end
end
+
+ def chain_args &blk
+ prc = self
+ lambda do |*args|
+ instance_exec(*args, &prc)
+ instance_exec(*args, &blk)
+ end
+ end
end
class Hash
include PrettyJSON
@@ -109,31 +128,51 @@
end
end
class Symbol
def icon
- "<span class='glyphicon glyphicon-#{self}'></span>"
+ s = self
+ if s[0, 3] == 'fa_'
+ "<i class='fa fa-#{s[3 .. -1]}'></i>"
+ else
+ "<span class='glyphicon glyphicon-#{s}'></span>"
+ end
end
- def aicon
- "<i class='fa fa-#{self}'></i>"
+ def q col
+ col.qualify self
end
end
+module Faye
+ class WebSocket
+ module API
+ def send! msg
+ msg = msg.to_json if msg.is_a? Hash
+ send msg
+ end
+ end
+ end
+end
+
class << Sequel
attr_accessor :alias_columns_in_joins
def split_keys id
id.split(Engine2::SETTINGS[:key_separator])
end
+
+ def join_keys keys
+ keys.join(Engine2::SETTINGS[:key_separator])
+ end
end
class Sequel::Database
attr_accessor :models, :default_schema
def cache_file
- "#{Engine2::app}/#{opts[:orig_opts][:name]}.dump"
+ "#{Engine2::SETTINGS.path_for(:db_path)}/#{opts[:orig_opts][:name]}.dump"
end
def load_schema_cache_from_file
self.models = {}
load_schema_cache? cache_file if adapter_scheme
@@ -142,11 +181,10 @@
def dump_schema_cache_to_file
dump_schema_cache? cache_file if adapter_scheme
end
end
-Sequel.quote_identifiers = false
Sequel.extension :core_extensions
Sequel::Inflections.clear
Sequel.alias_columns_in_joins = true
# Sequel::Model.plugin :json_serializer, :naked => true
# Sequel::Model.plugin :timestamps
@@ -275,11 +313,11 @@
key.is_a?(Array) ? key : [key]
end
def primary_keys_qualified
# cache it ?
- primary_keys.map{|k|k.qualify(table_name)}
+ primary_keys.map{|k|table_name.q(k)}
end
def primary_keys_hash id
Hash[primary_keys.zip(id)]
end
@@ -288,21 +326,20 @@
Hash[primary_keys_qualified.zip(id)]
end
end
module DatasetMethods
-
def ensure_primary_key
- pk = @model.primary_keys
+ pk = model.primary_keys
raise Engine2::E2Error.new("No primary key defined for model #{model}") unless pk && pk.all?
if opts_select = @opts[:select]
sel_pk = []
opts_select.each do |sel|
name = case sel
when Symbol
- sel.to_s =~ /\w+__(\w+)/ ? $1.to_sym : sel
+ sel
when Sequel::SQL::QualifiedIdentifier
sel.column
when Sequel::SQL::AliasedExpression
sel
# nil #sel.aliaz # ?
@@ -312,98 +349,89 @@
end
if pk.length == sel_pk.length
self
else
- sels = (pk - sel_pk).map{|k| k.qualify(@model.table_name)}
+ sels = (pk - sel_pk).map{|k| model.table_name.q(k)}
select_more(*sels)
end
else
- select(*pk.map{|k| k.qualify(@model.table_name)})
+ select(*pk.map{|k| model.table_name.q(k)})
end
end
+ def extract_select sel, al = nil, &blk
+ case sel
+ when Symbol
+ yield nil, sel, nil
+ when Sequel::SQL::QualifiedIdentifier
+ yield sel.table, sel.column, al
+ when Sequel::SQL::AliasedExpression, Sequel::SQL::Function
+ sel
+ # extract_select sel.expression, sel.aliaz, &blk
+ # expr = sel.expression
+ # yield expr.table, expr.column
+ else
+ raise Engine2::E2Error.new("Unknown selection #{sel}")
+ end
+ end
+
def setup! fields
joins = {}
type_info = model.type_info
model_table_name = model.table_name
- @opts[:select].map! do |sel|
+ @opts[:select] = @opts[:select].map do |sel|
extract_select sel do |table, name, aliaz|
- if table
+ info = if table
if table == model_table_name
- m = model
+ model
else
- a = model.many_to_one_associations[table] # || model.one_to_one_associations[table]
- raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless a
- m = a.associated_class
- end
- # raise Engine2::E2Error.new("Model not found for table #{table} in model #{model}") unless m
- info = m.type_info
+ assoc = model.many_to_one_associations[table] || model.many_to_many_associations[table]
+ raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless assoc
+ assoc.associated_class
+ end.type_info
else
- info = type_info
+ type_info
end
- f_info = info[name]
- raise Engine2::E2Error.new("Column #{name} not found for table #{table || model_table_name}") unless f_info
-
table ||= model_table_name
-
if table == model_table_name
fields << name
else
- fields << :"#{table}__#{name}"
- joins[table] = model.many_to_one_associations[table]
+ fields << table.q(name)
+ joins[table] ||= model.many_to_one_associations[table] || model.many_to_many_associations[table]
end
+ f_info = info[name]
+ raise Engine2::E2Error.new("Column #{name} not found for table #{table || model_table_name}") unless f_info
if f_info[:dummy]
nil
- # elsif f_info[:type] == :blob_store
- # # (~{name => nil}).as :name
- # # Sequel.char_length(name).as name
- # nil
else
+ qname = table.q(name)
if table != model_table_name
- if Sequel.alias_columns_in_joins
- name.qualify(table).as(:"#{table}__#{name}")
- else
- name.qualify(table)
- end
+ Sequel.alias_columns_in_joins ? qname.as(:"#{table}__#{name}") : qname
else
- name.qualify(table)
+ qname
end
end
end
end
- @opts[:select].compact!
+ @opts[:select].compact!.freeze
joins.reduce(self) do |joined, (table, assoc)|
m = assoc.associated_class
- keys = assoc[:qualified_key]
- joined.left_join(table, m.primary_keys.zip(keys.is_a?(Array) ? keys : [keys]))
- end
- end
-
- def extract_select sel, al = nil, &blk
- case sel
- when Symbol
- if sel.to_s =~ /^(\w+)__(\w+?)(?:___(\w+))?$/
- yield $1.to_sym, $2.to_sym, $3 ? $3.to_sym : nil
- else
- yield nil, sel, al
+ case assoc[:type]
+ when :many_to_one
+ keys = assoc[:qualified_key]
+ joined.left_join(table, m.primary_keys.zip(keys.is_a?(Array) ? keys : [keys]))
+ when :many_to_many
+ joined.left_join(assoc[:join_table], assoc[:left_keys].zip(model.primary_keys)).left_join(m.table_name, m.primary_keys.zip(assoc[:right_keys]))
+ else unsupported_association
end
- when Sequel::SQL::QualifiedIdentifier
- yield sel.table, sel.column, al
- when Sequel::SQL::AliasedExpression, Sequel::SQL::Function
- sel
- # extract_select sel.expression, sel.aliaz, &blk
- # expr = sel.expression
- # yield expr.table, expr.column
- else
- raise Engine2::E2Error.new("Unknown selection #{sel}")
end
end
def get_opts
@opts
@@ -431,14 +459,25 @@
end
module Engine2
LOCS ||= Hash.new{|h, k| ":#{k}:"}
PATH ||= File.expand_path('../..', File.dirname(__FILE__))
- SETTINGS ||= {key_separator: '|'}
+ SETTINGS ||= {
+ key_separator: '|',
+ app_path: 'app',
+ db_path: 'db',
+ model_path: 'models',
+ view_path: 'views',
+ asset_path: 'assets',
+ conf_path: 'conf'
+ }
+ def SETTINGS.path_for path
+ "#{self[:app_path]}/#{self[path]}"
+ end unless SETTINGS.frozen?
+
class << self
- attr_reader :app
attr_reader :core_loaded
def database name
Object.const_set(name, yield) unless Object.const_defined?(name)
end
@@ -455,47 +494,51 @@
def model_boot &blk
@model_boot_blk = blk
end
def bootstrap_e2db
- e2_db_file = (defined? JRUBY_VERSION) ? "jdbc:sqlite:#{@app}/engine2.db" : "sqlite://#{@app}/engine2.db"
- const_set :E2DB, connect(e2_db_file, loggers: [Logger.new($stdout)], convert_types: false, name: :engine2)
+ e2_db_path = "#{Engine2::SETTINGS.path_for(:db_path)}/engine2.db"
+ e2_db_url = (defined? JRUBY_VERSION) ? "jdbc:sqlite:#{e2_db_path}" : "sqlite://#{e2_db_path}"
+ const_set :E2DB, connect(e2_db_url, loggers: [Logger.new($stdout)], convert_types: false, name: :engine2)
const_set :DUMMYDB, Sequel::Database.new(uri: 'dummy')
def DUMMYDB.synchronize *args;end
end
def reload
@core_loaded = true
t = Time.now
- Action.count = 0
+ ActionNode.count = 0
SCHEMES.user.clear
Sequel::DATABASES.each do |db|
db.models.each{|n, m| Object.send(:remove_const, n) if Object.const_defined?(n)} unless db == E2DB || db == DUMMYDB
end
- load "#{app}/boot.rb"
+ load "#{Engine2::SETTINGS[:app_path]}/boot.rb"
Sequel::DATABASES.each &:load_schema_cache_from_file
@model_boot_blk.() if @model_boot_blk
load 'engine2/models/Files.rb'
load 'engine2/models/UserInfo.rb'
- Dir["#{app}/models/*"].each{|m| load m}
+ Dir["#{Engine2::SETTINGS.path_for(:model_path)}/*"].each{|m| load m}
puts "MODELS: #{Sequel::DATABASES.reduce(0){|s, d|s + d.models.size}}, Time: #{Time.now - t}"
Sequel::DATABASES.each &:dump_schema_cache_to_file
send(:remove_const, :ROOT) if defined? ROOT
- const_set(:ROOT, Action.new(nil, :api, RootMeta, {}))
+ const_set(:ROOT, ActionNode.new(nil, :api, RootAction, {}))
@boot_blk.(ROOT)
- ROOT.setup_action_tree
- puts "BOOTSTRAP #{app}, Time: #{Time.new - t}"
+ ROOT.setup_node_tree
+ puts "BOOTSTRAP #{Engine2::SETTINGS[:name]}, Time: #{Time.new - t}"
end
- def bootstrap app, settings = {}
- @app = app
+ def bootstrap path, settings = {}
SETTINGS.merge! settings
- SETTINGS[:name] ||= File::basename(app)
+ SETTINGS[:path] = path
+ SETTINGS[:name] ||= File::basename(path)
+ SETTINGS.freeze
+ Handler.set :public_folder, "public"
+ Handler.set :views, [SETTINGS.path_for(:view_path), "#{Engine2::PATH}/views"]
bootstrap_e2db
require 'engine2/pre_bootstrap'
reload
require 'engine2/post_bootstrap'