Module: Spider::Model

Defined in:
lib/spiderfw/model/model.rb,
lib/spiderfw/model/sync.rb,
lib/spiderfw/model/type.rb,
lib/spiderfw/model/query.rb,
lib/spiderfw/model/element.rb,
lib/spiderfw/model/request.rb,
lib/spiderfw/model/storage.rb,
lib/spiderfw/model/junction.rb,
lib/spiderfw/model/condition.rb,
lib/spiderfw/model/query_set.rb,
lib/spiderfw/model/base_model.rb,
lib/spiderfw/model/model_hash.rb,
lib/spiderfw/model/mixins/list.rb,
lib/spiderfw/model/mixins/tree.rb,
lib/spiderfw/model/proxy_model.rb,
lib/spiderfw/model/query_funcs.rb,
lib/spiderfw/model/inline_model.rb,
lib/spiderfw/model/unit_of_work.rb,
lib/spiderfw/model/active_record.rb,
lib/spiderfw/model/mixins/mixins.rb,
lib/spiderfw/model/storage/db/db.rb,
lib/spiderfw/model/mappers/mapper.rb,
lib/spiderfw/model/storage/schema.rb,
lib/spiderfw/model/identity_mapper.rb,
lib/spiderfw/model/mappers/mappers.rb,
lib/spiderfw/model/mixins/converted.rb,
lib/spiderfw/model/mixins/versioned.rb,
lib/spiderfw/model/mappers/db_mapper.rb,
lib/spiderfw/model/integrated_element.rb,
lib/spiderfw/model/mappers/hash_mapper.rb,
lib/spiderfw/model/mixins/synchronized.rb,
lib/spiderfw/model/mappers/proxy_mapper.rb,
lib/spiderfw/model/mixins/state_machine.rb,
lib/spiderfw/model/storage/base_storage.rb,
lib/spiderfw/model/storage/db/db_schema.rb,
lib/spiderfw/model/storage/db/reflector.rb,
lib/spiderfw/model/storage/null_storage.rb,
lib/spiderfw/model/storage/db/db_storage.rb,
lib/spiderfw/model/extended_models/managed.rb,
lib/spiderfw/model/mappers/document_mapper.rb,
lib/spiderfw/model/storage/connection_pool.rb,
lib/spiderfw/model/storage/db/db_connector.rb,
lib/spiderfw/model/storage/db/adapters/mssql.rb,
lib/spiderfw/model/storage/db/adapters/mysql.rb,
lib/spiderfw/model/storage/document/document.rb,
lib/spiderfw/model/storage/db/adapters/oracle.rb,
lib/spiderfw/model/storage/db/adapters/sqlite.rb,
lib/spiderfw/model/storage/db/connectors/jdbc.rb,
lib/spiderfw/model/storage/db/connectors/oci8.rb,
lib/spiderfw/model/storage/db/connectors/odbc.rb,
lib/spiderfw/model/storage/db/connectors/jdbc_oracle.rb,
lib/spiderfw/model/storage/db/dialects/no_total_rows.rb,
lib/spiderfw/model/storage/document/adapters/mongodb.rb,
lib/spiderfw/model/storage/document/document_storage.rb

Overview

Spider::Model is the namespace containing all data-related classes and modules.

In addition, it implements some helper methods.

See BaseModel for the base class that must be subclassed by app’s models.

Defined Under Namespace

Modules: ActiveRecordModel, ConditionMixin, Converted, Junction, List, MapperIncludeModule, Mappers, Mixins, StateMachine, Storage, Synchronized, Tree, VersionModel, Versioned Classes: BaseModel, Condition, ConditionContext, Element, FormatError, IdentityMapper, IdentityMapperException, InlineModel, IntegratedElement, Managed, Mapper, MapperElementError, MapperError, MapperTask, ModelException, ModelHash, ProxyModel, Query, QuerySet, Request, SortTask, Sorter, Sync, Type, TypeError, UnitOfWork

Constant Summary collapse

RequiredError =

A required element has no value

MapperElementError.create_subclass(_("Element %s is required"))
NotUniqueError =

An uniqueness constraint has been violated.

MapperElementError.create_subclass(_("Another item with the same %s is already present"))

Class Method Summary collapse

Class Method Details

.ar_modelsObject



299
300
301
# File 'lib/spiderfw/model/active_record.rb', line 299

def self.ar_models
    @ar_models
end

.base_type(klass) ⇒ Class

Returns the base type corresponding to a class.

For BaseModels, the class itself will be returned; otherwise, will walk superclasses and DataType info until one of the base_types is found.

Parameters:

Returns:

  • (Class)

    The Base Type



36
37
38
39
40
41
42
# File 'lib/spiderfw/model/model.rb', line 36

def self.base_type(klass)
    k = klass
    while (k && !base_types.include?(k))
        k = simplify_type(k)
    end
    return k
end

.base_typesArray

Returns a list of the base types, which must be handled by all mappers.

String, Spider::DataTypes::Text, Fixnum, Float, BigDecimal, Date, DateTime, Spider::DataTypes::Bool.

These types must be handled by all mappers.

Returns:

  • (Array)

    An array of base types



25
26
27
# File 'lib/spiderfw/model/model.rb', line 25

def self.base_types
    @base_types
end

.create_ar_classes(ar, container) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/spiderfw/model/active_record.rb', line 303

def self.create_ar_classes(ar, container)
    @ar_models ||= []
    return if ar.spider_model
    name = ar.name.split(':')[-1]
    if (container.const_defined?(name))
        current = container.const_get(name)
    end
    unless current
        mod = Class.new(Spider::Model::BaseModel)
        container.const_set(name, mod)
    else
        mod = current
    end
    unless mod.is_a?(ActiveRecordModel)
        mod.instance_eval do
            include ActiveRecordModel
        end
    end
    mod.ar_defined = true unless current
    mod.ar = ar
    ar.spider_model = mod
    ar.reflections.each do |name, reflection|
        begin 
            create_ar_classes(reflection.klass, container) unless reflection.klass.spider_model
            through = reflection.through_reflection.klass
            create_ar_classes(through, container) if through && !through.spider_model
        rescue NameError
        end
    end
    @ar_models << mod
end

.get(model, val = nil, set_loaded = false) ⇒ BaseModel

Retrieves an object corresponding to gived values from the IdentityMapper, or puts it there if not found.

Parameters:

  • BaseModel] (Class)

    The model

  • set_loaded (bool) (defaults to: false)

    If true, when instantiating an object from hash values, set the values as if they were loaded from the storage

Returns:

  • (BaseModel)

    The object retrieved from the IdentityMapper



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/spiderfw/model/model.rb', line 80

def self.get(model, val=nil, set_loaded=false)
    if (val && !val.is_a?(Hash))
        if (model.primary_keys.length == 1)
            val = {model.primary_keys[0].name => val}
        else
            raise ModelException, "Can't get without primary keys"
        end
    end
    if identity_mapper
        return identity_mapper.get(model, val, set_loaded)
    else
        return model.new(val)
    end
end

.identity_mapperIdentityMapper

Returns The current IdentityMapper, if active.

Returns:



109
110
111
# File 'lib/spiderfw/model/model.rb', line 109

def self.identity_mapper
    Spider.current[:identity_mapper]
end

.identity_mapper=(im) ⇒ IdentityMapper

Parameters:

  • im (IdentityMapper)

    The IdentityMapper to activate for the current request

Returns:



115
116
117
# File 'lib/spiderfw/model/model.rb', line 115

def self.identity_mapper=(im)
    Spider.current[:identity_mapper] = im
end

.in_unit(&proc) ⇒ void

This method returns an undefined value.

Executes a block inside a Unit Of Work and Identity Mapper

Parameters:

  • proc (Proc)

    The block to run



221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/spiderfw/model/model.rb', line 221

def self.in_unit(&proc)
    uow = self.unit_of_work
    self.start_unit_of_work unless uow
    self.with_identity_mapper do
        begin
            yield Spider::Model.unit_of_work
        
            self.unit_of_work.commit unless uow
        ensure
            self.stop_unit_of_work unless uow
        end
    end
    
end

.load_fixtures(file, truncate = false) ⇒ Object

Load YAML data to the storage

Parameters:

  • file (String)

    File to load data from

  • truncate (bool) (defaults to: false)

    If true, delete all data from the models in the file before inserting new data



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/spiderfw/model/model.rb', line 289

def self.load_fixtures(file, truncate=false)
    if (file =~ /\.([^\.]+)$/)
        extension = $1
    else
        raise ArgumentError, "Can't determine type of fixtures file #{file}"
    end
    data = {}
    case extension
    when 'yml'
        require 'yaml'
        data = YAML.load_file(file)
    end
     # Ruby 1.9: steps are not needed with ordered hashes
    data = [data] unless data.is_a?(Array)
    loaded = []
    data.each do |step|
        step.each do |mod_name, mod_data|
            mod = const_get_full(mod_name)
            mod.mapper.truncate! if truncate
            mod_data.each do |row|
                h = {}
                row.each do |k, v|
                    if v.is_a?(String)
                        if v[0..1] == '@@'
                            v = v[1..-1]
                        elsif v[0].chr == '@'
                            v = eval(v[1..-1].to_s)
                        end
                    end
                    h[k] = v
                end
                obj = mod.new(h)
                obj.insert
                loaded << obj
            end
        end
    end
    loaded
end

.no_context(&proc) ⇒ UnitOfWork

Executes a block without Identity Mapper and Unit Of Work

Parameters:

  • proc (Proc)

    The block to run

Returns:

  • (UnitOfWork)

    The previously active Unit Of Work (if any)



193
194
195
196
197
198
199
200
201
202
# File 'lib/spiderfw/model/model.rb', line 193

def self.no_context(&proc)
    uow = self.unit_of_work
    self.unit_of_work = nil
    im = self.identity_mapper            
    self.identity_mapper = nil
    yield
    self.identity_mapper = im
    self.unit_of_work = uow
    
end

.no_identity_mapper(&proc) ⇒ IdentityMapper

Executes a block without Identity Mapper

Parameters:

  • proc (Proc)

    The block to run without the Identity Mapper

Returns:



183
184
185
186
187
188
# File 'lib/spiderfw/model/model.rb', line 183

def self.no_identity_mapper(&proc)
    im = self.identity_mapper
    self.identity_mapper = nil
    yield
    self.identity_mapper = im
end

.no_unit_of_work(&proc) ⇒ UnitOfWork

Executes a block without running in Unit Of Work

Parameters:

  • proc (Proc)

    The block to run without a unit of work

Returns:

  • (UnitOfWork)

    The previously active Unit Of Work (if any)



168
169
170
171
172
173
# File 'lib/spiderfw/model/model.rb', line 168

def self.no_unit_of_work(&proc)
    uow = self.unit_of_work
    self.unit_of_work = nil
    yield
    self.unit_of_work = uow
end

.put(obj, check = false) ⇒ BaseModel

Puts an object into the IdentityMapper

Parameters:

  • object (BaseMode)

    to place into the IdentityMapper

  • check (bool) (defaults to: false)

    If true, if the object already exists in the IdentityMapper it will be merged. If false, if the object already exists it will be overwritten.

Returns:

  • (BaseModel)

    The object, as present in the IdentityMapper after the put



100
101
102
103
104
105
106
# File 'lib/spiderfw/model/model.rb', line 100

def self.put(obj, check=false)
    if (identity_mapper)
        return identity_mapper.put(obj, check)
    else
        return obj
    end
end

.ruby_type(klass) ⇒ Class

Returns The Ruby class corresponding to a Spider DataType.

Parameters:

  • A (Class)

    DataType subclass

Returns:

  • (Class)

    The Ruby class corresponding to a Spider DataType



46
47
48
49
50
51
52
53
54
55
# File 'lib/spiderfw/model/model.rb', line 46

def self.ruby_type(klass)
    map_types = {
        Spider::DataTypes::Text => String,
        Spider::DataTypes::Bool => FalseClass,
        Spider::DataTypes::Binary => String,
        Spider::DataTypes::FilePath => String
    }
    return map_types[klass] if map_types[klass]
    return klass
end

.simplify_type(klass) ⇒ Class

An iteration in the search for base type.

Parameters:

Returns:

  • (Class)

    simplified type



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/spiderfw/model/model.rb', line 61

def self.simplify_type(klass)
    map_types = {
        
    }
    return klass if base_types.include?(klass)
    return klass if klass <= Spider::Model::BaseModel
    return t if t = map_types[klass]
    return klass.maps_to if klass.subclass_of?(Spider::DataType) && klass.maps_to
    return klass.superclass if klass.superclass
    return nil
end

.sort(models, options = {}) ⇒ Array

Sorts an Array of models, placing subclasses before superclasses.

If :association_dependencies is true, models having an association to another model will be placed after the associated model.

This can be used to insert a dump of data, ensuring later models only depend on already inserted objects.

Parameters:

  • models (Array)

    An array of BaseModel subclasses

  • options (Hash) (defaults to: {})

    Options can be:

    • :association_dependencies If true, sort associated models before the model associating them

Returns:

  • (Array)

    The sorted array



379
380
381
382
383
384
385
# File 'lib/spiderfw/model/model.rb', line 379

def self.sort(models, options={})
    options = {
        :association_dependencies => true
    }.merge(options)
    sorter = Sorter.new(models, options)
    sorter.sort
end

.start_unit_of_workUnitOfWork

Starts a new Unit Of Work

Returns:



121
122
123
124
# File 'lib/spiderfw/model/model.rb', line 121

def self.start_unit_of_work
    uow = UnitOfWork.new
    uow.start
end

.stop_unit_of_workvoid

This method returns an undefined value.

Stops the current Unit Of Work



128
129
130
# File 'lib/spiderfw/model/model.rb', line 128

def self.stop_unit_of_work
    Spider.current[:unit_of_work].stop
end

.sync_schema(model_or_app, force = false, options = {}) ⇒ void

This method returns an undefined value.

Syncs the schema for a model, or for all models within an app, with the storage.

Parameters:

  • BaseModel|Module>Spider::App] (Class)

    model_or_app

  • force (bool) (defaults to: false)

    If true, allow operations that could cause data loss

  • options (Hash) (defaults to: {})

    Options can be:

    • :no_sync Don’t actually run the sync, only check the operations to run

    • :drop_tables Drop unneeded tables



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/spiderfw/model/model.rb', line 243

def self.sync_schema(model_or_app, force=false, options={})
    
    # FIXME: this is clearly db specific. Move somewhere else.
    models = []
    mod = const_get_full(model_or_app)
    if (mod.is_a?(Module) && mod.include?(Spider::App))
        mod.models.each { |m| models << m }
    elsif (mod.subclass_of?(Spider::Model::BaseModel))
        models << mod
    end
    storages = []
    tables = []
    models.each do |m|
        unless (options[:no_sync])
            Spider::Logger.debug("SYNCING #{m}")
            m.mapper.sync_schema(force, options) if m.mapper.respond_to?(:sync_schema)
		    m.after_sync if m.respond_to?(:after_sync)	
		end
        if (options[:drop_tables] && m.mapper.respond_to?(:schema))
            storages << m.mapper.storage unless storages.include?(m.mapper.storage)
            tables += m.mapper.schema.get_schemas.keys
        end
    end
    if (options[:drop_tables])
        dt = options[:drop_tables]
        tables.flatten
        storage_tables = {}
        storages.each do |s|
            s.list_tables.each do |t|
                storage_tables[t] = s
            end
        end
        tables_to_drop = []
        storage_tables.each do |table_name, storage|
            if !tables.include?(table_name) && (dt == true || table_name.index(dt) == 0)
                tables_to_drop << table_name
            end
        end
        raise Spider::Model::Mappers::SchemaSyncUnsafeConversion.new(tables_to_drop) unless tables_to_drop.empty?
        tables_to_drop.each{ |t| storage.drop_table(t) }
    end
end

.unit_of_work(&proc) ⇒ UnitOfWork

Returns the current Unit Of Work, if no block was passed; otherwise, the Unit Of Work that was used to run the block.

Parameters:

  • proc (Proc)

    If supplied and no Unit Of Work is running, executes the block inside a new Unit Of Work

Returns:

  • (UnitOfWork)

    the current Unit Of Work, if no block was passed; otherwise, the Unit Of Work that was used to run the block



136
137
138
139
140
141
142
143
144
# File 'lib/spiderfw/model/model.rb', line 136

def self.unit_of_work(&proc)
    uow = Spider.current[:unit_of_work]
    if !uow
        if proc
            uow = UnitOfWork.new(&proc)
        end
    end
    return uow
end

.unit_of_work=(uow) ⇒ UnitOfWork

Sets the UnitOfWork to use for the current request

Parameters:

Returns:



149
150
151
# File 'lib/spiderfw/model/model.rb', line 149

def self.unit_of_work=(uow)
    Spider.current[:unit_of_work] = uow
end

.unit_of_work_running?bool

Returns True if there is an active Unit Of Work, false otherwise.

Returns:

  • (bool)

    True if there is an active Unit Of Work, false otherwise



176
177
178
# File 'lib/spiderfw/model/model.rb', line 176

def self.unit_of_work_running?
    self.unit_of_work && self.unit_of_work.running?
end

.with_identity_mapper(&proc) ⇒ IdentityMapper

Executes a block in the context of the current IdentityMapper, if one is active. If no IdentityMapper is running, the code is executed inside a new Identity Mapper

Parameters:

  • proc (Proc)

    The block to run

Returns:



208
209
210
211
212
213
214
215
216
# File 'lib/spiderfw/model/model.rb', line 208

def self.with_identity_mapper(&proc)
    if identity_mapper
        yield identity_mapper
    else
        IdentityMapper.new do |im|
            yield im
        end
    end
end

.with_unit_of_work(&proc) ⇒ UnitOfWork

Executes a block inside a new Unit Of Work

Note: you should almost always use in_unit instead, since a Unit Of Work without an Identity Mapper can be problematic.

Parameters:

  • proc (Proc)

    The block to execute

Returns:

  • (UnitOfWork)

    The Unit Of Work that was used to run the block



159
160
161
162
163
# File 'lib/spiderfw/model/model.rb', line 159

def self.with_unit_of_work(&proc)
    with_identity_mapper do
        return unit_of_work(&proc)
    end
end