yogo-project 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/.gitignore +27 -0
  3. data/Gemfile +34 -0
  4. data/Gemfile.lock +198 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +17 -0
  7. data/Rakefile +56 -0
  8. data/VERSION +1 -0
  9. data/features/step_definitions/yogo_project_steps.rb +0 -0
  10. data/features/support/env.rb +1 -0
  11. data/features/yogo_project.feature +9 -0
  12. data/lib/datamapper/adapters/postgres.rb +9 -0
  13. data/lib/datamapper/adapters/sqlite.rb +9 -0
  14. data/lib/yogo-project.rb +2 -0
  15. data/lib/yogo/collection.rb +12 -0
  16. data/lib/yogo/collection/asset.rb +12 -0
  17. data/lib/yogo/collection/asset/model.rb +11 -0
  18. data/lib/yogo/collection/asset/model_configuration.rb +21 -0
  19. data/lib/yogo/collection/asset/model_properties.rb +47 -0
  20. data/lib/yogo/collection/base.rb +69 -0
  21. data/lib/yogo/collection/base/collection_repository.rb +21 -0
  22. data/lib/yogo/collection/base/model.rb +20 -0
  23. data/lib/yogo/collection/base/model_collection_context.rb +25 -0
  24. data/lib/yogo/collection/base/model_configuration.rb +34 -0
  25. data/lib/yogo/collection/data.rb +61 -0
  26. data/lib/yogo/collection/data/model.rb +72 -0
  27. data/lib/yogo/collection/data/model_configuration.rb +42 -0
  28. data/lib/yogo/collection/data/model_properties.rb +15 -0
  29. data/lib/yogo/collection/field_view.rb +26 -0
  30. data/lib/yogo/collection/form.rb +22 -0
  31. data/lib/yogo/collection/property.rb +127 -0
  32. data/lib/yogo/collection/static.rb +13 -0
  33. data/lib/yogo/collection/static/model_configuration.rb +31 -0
  34. data/lib/yogo/configuration.rb +9 -0
  35. data/lib/yogo/datamapper/model/common/properties.rb +16 -0
  36. data/lib/yogo/datamapper/model/configuration.rb +31 -0
  37. data/lib/yogo/datamapper/model/operations/add.rb +34 -0
  38. data/lib/yogo/datamapper/model/operations/clear.rb +29 -0
  39. data/lib/yogo/datamapper/model/stored/configuration.rb +25 -0
  40. data/lib/yogo/datamapper/repository_manager.rb +30 -0
  41. data/lib/yogo/datamapper/repository_manager/model.rb +40 -0
  42. data/lib/yogo/datamapper/repository_manager/resource.rb +124 -0
  43. data/lib/yogo/example/dynamic/project_full.rb +48 -0
  44. data/lib/yogo/example/dynamic/project_remix.rb +16 -0
  45. data/lib/yogo/example/voeis/managed/data_stream.rb +39 -0
  46. data/lib/yogo/example/voeis/managed/site.rb +51 -0
  47. data/lib/yogo/example/voeis/project.rb +57 -0
  48. data/lib/yogo/operation.rb +49 -0
  49. data/lib/yogo/project.rb +32 -0
  50. data/lib/yogo/property_ext.rb +7 -0
  51. data/spec/resource/.gitdir +0 -0
  52. data/spec/resource/text_file_asset.txt +1 -0
  53. data/spec/spec_helper.rb +49 -0
  54. data/spec/yogo/all_collection_data_models_spec.rb +34 -0
  55. data/spec/yogo/all_collection_managers_spec.rb +18 -0
  56. data/spec/yogo/all_collections_spec.rb +54 -0
  57. data/spec/yogo/all_data_collections_spec.rb +51 -0
  58. data/spec/yogo/asset_collection_spec.rb +28 -0
  59. data/spec/yogo/data_collection_spec.rb +16 -0
  60. data/yogo-project.gemspec +128 -0
  61. metadata +222 -0
@@ -0,0 +1,22 @@
1
+ require 'dm-core'
2
+ require 'dm-types/uuid'
3
+ require 'dm-types/yaml'
4
+
5
+ require 'yogo/collection/property'
6
+ require 'yogo/collection/field_view'
7
+
8
+ module Yogo
9
+ module Collection
10
+ class Form
11
+ include ::DataMapper::Resource
12
+
13
+ property :id, UUID, :key => true, :default => lambda { |p,r| Yogo::Configuration.random_uuid }
14
+ property :name, String, :required => true
15
+
16
+ belongs_to :collection
17
+ has n, :field_views
18
+ has n, :fields, :through => :field_views
19
+ has n, :available_fields, :through => :collection, :model => 'Yogo::Collection::Property'
20
+ end # FieldView
21
+ end # Collection
22
+ end # Yogo
@@ -0,0 +1,127 @@
1
+ require 'dm-types/uuid'
2
+ require 'dm-types/yaml'
3
+
4
+ require 'yogo/configuration'
5
+
6
+ module Yogo
7
+ module Collection
8
+ class Property
9
+ include ::DataMapper::Resource
10
+
11
+ property :id, UUID, :key => true, :default => lambda { |p,r| Configuration.random_uuid }
12
+ property :name, String, :required => true
13
+ property :options, Yaml, :default => {}.to_yaml
14
+ property :type, Discriminator
15
+
16
+ property :data_collection_id, UUID
17
+ belongs_to :data_collection, :model => 'Yogo::Collection::Data'
18
+
19
+ # validates_uniqueness_of :name, :scope => :data_collection_id
20
+
21
+ def field_name
22
+ self.to_s
23
+ end
24
+
25
+ def to_s
26
+ 'field_' + self.id.to_s.gsub('-','_')
27
+ end
28
+
29
+ def as_json(opts=nil)
30
+ {
31
+ :id => self.id.to_s,
32
+ :type => self.type.to_s,
33
+ :field_name => self.to_s,
34
+ :name => self.name,
35
+ :options => self.options,
36
+ :data_collection => self.data_collection_id.to_s
37
+ }
38
+ end
39
+
40
+ def update_attributes(hash)
41
+ attrs = {}
42
+ attrs[:name] = hash[:name] || hash['name'] || self.name
43
+ attrs[:type] = hash[:type] || hash['type'] || self.type || self.class.name
44
+ attrs[:options] = hash[:options] || hash['options'] || self.options
45
+ self.attributes = attrs
46
+ end
47
+
48
+ def model_method
49
+ :property
50
+ end
51
+
52
+ def add_to_model(model)
53
+ prop_name = self.to_s.intern
54
+ prop_type = self.class
55
+ prop_options = self.options || {}
56
+
57
+ model.send(model_method, prop_name, prop_type, prop_options)
58
+ end
59
+
60
+ COMMON_PROPERTIES = [:String, :Text, :Integer, :Float, :Boolean, :Date, :Time, :DateTime]
61
+ COMMON_PROPERTIES.each do |type|
62
+ class_eval %{
63
+ class #{type} < Property; end
64
+ }
65
+ end
66
+ end # Property
67
+
68
+
69
+ class Relationship < Property
70
+ property :target_collection_id, UUID
71
+ belongs_to :target_collection, :model => 'Yogo::Collection::Data'
72
+
73
+ def as_json(options=nil)
74
+ hash = super
75
+ hash[:target_collection_id] = self.target_collection_id.to_s
76
+ hash
77
+ end
78
+
79
+ def target_model
80
+ target_collection.data_model
81
+ end
82
+
83
+ def model_method
84
+ :has
85
+ end
86
+
87
+ def check_target
88
+ raise ":target not set" unless self.target && !self.target.empty?
89
+ end
90
+
91
+ def add_to_model(model, n, options={})
92
+ check_target
93
+ model.send(model_method, n, ActiveSupport::Inflector.tableize(self.target).intern, options.merge(self.options))
94
+ end
95
+
96
+ class ManyToMany < self
97
+ def add_to_model(model)
98
+ super(model, Infinity, :through => ::DataMapper::Resource)
99
+ end
100
+ end
101
+
102
+ class OneToMany < self
103
+ def add_to_model(model)
104
+ super(model)
105
+ end
106
+ end
107
+
108
+ class OneToOne < self
109
+ def add_to_model(model)
110
+ super(model, 1)
111
+ end
112
+ end
113
+
114
+ class ManyToOne < self
115
+ def model_method
116
+ :belongs_to
117
+ end
118
+
119
+ def add_to_model(model)
120
+ check_target
121
+ model.send(model_method, ActiveSupport::Inflector.underscore(self.target).intern, options.merge(self.options))
122
+ end
123
+ end
124
+
125
+ end
126
+ end # Collection
127
+ end # Yogo
@@ -0,0 +1,13 @@
1
+ require 'yogo/collection/data'
2
+
3
+ module Yogo
4
+ module Collection
5
+ class Static < Data
6
+ require 'yogo/collection/static/model_configuration'
7
+ include Static::ModelConfiguration
8
+
9
+ property :static_model, String
10
+
11
+ end # Asset
12
+ end # Collection
13
+ end # Yogo
@@ -0,0 +1,31 @@
1
+ module Yogo
2
+ module Collection
3
+ class Static
4
+ module ModelConfiguration
5
+ def model_generate
6
+ model_name = self.static_model
7
+ ActiveSupport::Inflector.constantize(model_name)
8
+ end
9
+
10
+ def before_model_generate
11
+ end
12
+
13
+ def after_model_generate(model)
14
+ model
15
+ end
16
+
17
+ def model_update(model)
18
+ model.extend(Base::ModelCollectionContext) unless model.respond_to?(:current_collection)
19
+ end
20
+
21
+ def before_model_update(model)
22
+ model
23
+ end
24
+
25
+ def after_model_update(model)
26
+ model
27
+ end
28
+ end # ModelConfiguration
29
+ end # Static
30
+ end # Collection
31
+ end # Yogo
@@ -0,0 +1,9 @@
1
+ require 'configatron'
2
+
3
+ module Yogo
4
+ Configuration = Configatron::Store.new
5
+ Configuration.random_uuid = Configatron::Dynamic.new { UUIDTools::UUID.timestamp_create }
6
+ Configuration.collection.set_default(:data_repository_name, :collection_data)
7
+ Configuration.collection.asset.set_default(:storage_dir, 'asset_collection')
8
+ end
9
+
@@ -0,0 +1,16 @@
1
+ require 'yogo/operation'
2
+ require 'yogo/datamapper/model/operations/add'
3
+ require 'dm-types/uuid'
4
+
5
+ module Yogo
6
+ module DataMapper
7
+ module Model
8
+ module Common
9
+ module Properties
10
+ UUIDKey = Operations::Add::Property.partial(X, :id, ::DataMapper::Property::UUID, {:key => true, :default => lambda{|*args| UUIDTools::UUID.timestamp_create}})
11
+
12
+ end # Properties
13
+ end # Common
14
+ end # Model
15
+ end # DataMapper
16
+ end # Yogo
@@ -0,0 +1,31 @@
1
+ require 'yogo/datamapper/model/operations/clear'
2
+ require 'yogo/datamapper/model/operations/add'
3
+
4
+ module Yogo
5
+ module DataMapper
6
+ module Model
7
+ module Configuration
8
+
9
+ def operation_definitions
10
+ @_operation_definitions ||= []
11
+ end
12
+
13
+ def operation(op_name, *args)
14
+ op_def = [op_name.to_s, args].flatten
15
+ operation_definitions << op_def unless operation_definitions.include?(op_def)
16
+ end
17
+
18
+ def to_proc
19
+ ops = operation_definitions.map{|op_def| Operations[op_def.first] }
20
+ partial_ops = []
21
+ ops.each_with_index do |op, i|
22
+ next unless op
23
+ partial_ops[i] = op.partial(X, *operation_definitions[i][1..-1])
24
+ end
25
+ partial_ops.compact!
26
+ partial_ops.reduce{|composed, partial_op| composed * partial_op}
27
+ end
28
+ end # Configuration
29
+ end # Model
30
+ end # DataMapper
31
+ end # Yogo
@@ -0,0 +1,34 @@
1
+ require 'yogo/operation'
2
+
3
+ module Yogo
4
+ module DataMapper
5
+ module Model
6
+ module Operations
7
+ module Add
8
+ Property = Operation.on(::DataMapper::Model) do |model, name, type, options|
9
+ type ||= String
10
+ options ||= {}
11
+ model.property(name, type, options)
12
+ model
13
+ end
14
+
15
+
16
+ Relationship = Operation.on(::DataMapper::Model) do |model, method, *args|
17
+ model.send(method, *args)
18
+ model
19
+ end
20
+
21
+ HasRelationship = Relationship.partial(X, :has, X, X, X)
22
+
23
+ HasN = HasRelationship.partial(X, Infinity, X, X)
24
+
25
+ HasOne = HasRelationship.partial(X, 1, X, X)
26
+
27
+ BelongsTo = Relationship.partial(X, :belongs_to, X, X)
28
+
29
+
30
+ end # Add
31
+ end # Operations
32
+ end # Model
33
+ end # DataMapper
34
+ end # Yogo
@@ -0,0 +1,29 @@
1
+ require 'yogo/operation'
2
+
3
+ module Yogo
4
+ module DataMapper
5
+ module Model
6
+ module Operations
7
+ module Clear
8
+ Properties = Operation.on(::DataMapper::Model) do |model|
9
+ model.properties.clear
10
+ model.properties.instance_variable_get(:@properties).clear #clear out the name index
11
+ model
12
+ end
13
+
14
+ Relationships = Operation.on(::DataMapper::Model) do |model|
15
+ model.relationships.clear
16
+ model
17
+ end
18
+
19
+ Validators = Operation.on(::DataMapper::Model) do |model|
20
+ model.validators.clear!
21
+ model
22
+ end
23
+
24
+ All = Properties * Relationships * Validators
25
+ end # Clear
26
+ end # Operations
27
+ end # Model
28
+ end # DataMapper
29
+ end # Yogo
@@ -0,0 +1,25 @@
1
+ require 'yogo/configuration'
2
+ require 'yogo/datamapper/model/configuration'
3
+ require 'yogo/datamapper/model/common/properties'
4
+
5
+ require 'dm-types/uuid'
6
+ require 'dm-types/yaml'
7
+
8
+ module Yogo
9
+ module DataMapper
10
+ module Model
11
+ module Stored
12
+ module Configuration
13
+ include ::DataMapper::Resource
14
+ include Dynamic::Configuration
15
+
16
+ is :remixable
17
+
18
+ Common::Properties::UUIDKey[self]
19
+
20
+ property :operation_definitions, Yaml, :default => lambda {|*args| []}
21
+ end # Configuration
22
+ end # Stored
23
+ end # Model
24
+ end # DataMapper
25
+ end # Yogo
@@ -0,0 +1,30 @@
1
+ require 'yogo/datamapper/repository_manager/model'
2
+ require 'yogo/datamapper/repository_manager/resource'
3
+
4
+ module Yogo
5
+ module DataMapper
6
+ module RepositoryManager
7
+ def self.included(base)
8
+ base.class_eval do
9
+ extend(RepositoryManager::Model)
10
+ include(RepositoryManager::Resource)
11
+ end
12
+ end
13
+ end # RepositoryManager
14
+ end # DataMapper
15
+ end # Yogo
16
+
17
+ module DataMapper
18
+ module Is
19
+ module RepositoryManager
20
+ def is_repository_manager
21
+ include(Yogo::DataMapper::RepositoryManager)
22
+ end
23
+ end # RepositoryManager
24
+ end # Is
25
+
26
+ if const_defined?("Model")
27
+ Model.append_extensions(Is::RepositoryManager)
28
+ end
29
+ end # module DataMapper
30
+
@@ -0,0 +1,40 @@
1
+ module Yogo
2
+ module DataMapper
3
+ module RepositoryManager
4
+ module Model
5
+ # Class method for informing Project instances about what kinds of models
6
+ # might be stored inside thier Project#managed_repository.
7
+ #
8
+ # @param [DataMapper::Model] model class that might be stored in Project managed_repositories
9
+ # @return [Array<DataMapper::Model>] list of currently managed models
10
+ def manage(*args)
11
+ @managed_models ||= []
12
+ models = args
13
+
14
+ @managed_models += models
15
+ @managed_models.uniq!
16
+
17
+ @managed_models
18
+ end
19
+
20
+ # Models that are currently managed by Project instances.
21
+ # @return [Array<DataMapper::Model>] list of currently managed models
22
+ def managed_models
23
+ @managed_models
24
+ end
25
+
26
+ # Ensure that Relation models are also managed
27
+ def finalize_managed_models!
28
+ models = []
29
+ @managed_models.each do |m|
30
+ models += m.relationships.values.map{|r| r.child_model }
31
+ models += m.relationships.values.map{|r| r.parent_model }
32
+ end
33
+ @managed_models += models
34
+ @managed_models.uniq!
35
+ @managed_models
36
+ end
37
+ end # Model
38
+ end # RepositoryManager
39
+ end # DataMapper
40
+ end # Yogo
@@ -0,0 +1,124 @@
1
+ module Yogo
2
+ module DataMapper
3
+ module RepositoryManager
4
+ module Resource
5
+ # Ensure that models that we might store in the Project#managed_repository
6
+ # are properly migrated/upgrade whenever the Project changes.
7
+ # @author Ryan Heimbuch
8
+ # @see Project#prepare_models
9
+ def self.included(base)
10
+ base.class_eval do
11
+ after :save, :prepare_models
12
+ after :create, :create_storage
13
+ before :destroy, :destroy_storage
14
+ end
15
+ end
16
+
17
+ def create_storage
18
+ ::DataMapper.repository.adapter.create_db(managed_repository_database_name)
19
+ puts "Making new storage"
20
+ end
21
+
22
+ def destroy_storage
23
+ puts "Nuking storage"
24
+ end
25
+
26
+ # @author Ryan Heimbuch
27
+ #
28
+ # Override required from Yogo::DataMapper::Repository#managed_repository_name
29
+ #
30
+ # @return [Symbol] the name for the DataMapper::Repository that the Project manages
31
+ def managed_repository_name
32
+ ActiveSupport::Inflector.tableize(id.to_s).to_sym
33
+ end
34
+
35
+ def managed_repository_database_name
36
+ "voeis_project_#{managed_repository_name}"
37
+ end
38
+
39
+ # @author Ryan Heimbuch
40
+ #
41
+ # @return [Hash] The adapter configuration for the Project managed_repository
42
+ # @see DataMapper.setup
43
+ def adapter_config
44
+ # Read the configuration from the existing database.yml file
45
+ config = Rails.configuration.database_configuration
46
+ adapter_conf = config['yogo-db'].dup
47
+ adapter_conf['database'] = "#{adapter_conf['path']}#{managed_repository_database_name}"
48
+ adapter_conf.delete("path")
49
+ return adapter_conf
50
+ end
51
+
52
+ def adapter
53
+ begin
54
+ ::DataMapper.repository(managed_repository_name).adapter
55
+ rescue ::DataMapper::RepositoryNotSetupError
56
+ ::DataMapper.setup(managed_repository_name, adapter_config)
57
+ retry
58
+ end
59
+ end
60
+
61
+ def managed_repository(&block)
62
+ adapter # ensure the adapter get's setup or exists
63
+ if block_given?
64
+ ::DataMapper.repository(managed_repository_name, &block)
65
+ else
66
+ ::DataMapper.repository(managed_repository_name)
67
+ end
68
+ end
69
+
70
+ # Ensure that models that models managed by the Project
71
+ # are properly migrated/upgraded inside the Project managed repository.
72
+ #
73
+ # @author Ryan Heimbuch
74
+ # @todo Refactor this method into a module in yogo-project
75
+ def prepare_models
76
+ adapter # ensure the adapter exists or is setup
77
+ managed_repository.scope {
78
+ self.class.finalize_managed_models!
79
+ self.class.managed_models.each do |klass|
80
+ klass.auto_upgrade!
81
+ end
82
+ }
83
+ end
84
+
85
+ # Builds a "new", unsaved datamapper resource, that is explicitly
86
+ # bound to the Project#managed_repository.
87
+ # If you want to create a new resource that will be saved inside the
88
+ # repository of a Project, you should always use this method.
89
+ #
90
+ # @example Create a new site that is stored in myProject.managed_repository
91
+ # managedSite = myProject.build_managed(Voeis::Site, :name => ...)
92
+ #
93
+ # @example Doing any of these will NOT work consistently (if at all)
94
+ # managedSite1 = Voeis::Site.new(:name => ...)
95
+ # managedSite1.save # WILL NOT save in myProject.managed_repository
96
+ #
97
+ # managedSite2 = myProject.managed_repository{Voeis::Site.new(:name => ...)}
98
+ # managedSite2.save # WILL NOT save in myProject.managed_repository
99
+ #
100
+ # Boring Details:
101
+ # Initially "new" model resources do not bind themselves to any repository.
102
+ # At some point a "new" resource will persist itself and bind itself exclusively
103
+ # to the repository that it "persisted into". This step is fiddly to catch, and
104
+ # happens deep inside the DataMapper code. It is MUCH easier to explictly bind
105
+ # the "new" resource to a particular repository immediately after calling #new.
106
+ # This requires using reflection to modify the internal state of the resource object,
107
+ # so it is best sealed inside a single method, rather than scattered throughout
108
+ # the codebase.
109
+ #
110
+ # @todo Refactor into module in yogo-project
111
+ # @author Ryan Heimbuch
112
+ def build_managed(model_klass, attributes={})
113
+ unless self.class.managed_models.include? model_klass
114
+ self.class.manage(model_klass)
115
+ prepare_models
116
+ end
117
+ res = model_klass.new(attributes)
118
+ res.instance_variable_set(:@_repository, managed_repository)
119
+ res
120
+ end
121
+ end # RepositoryModels
122
+ end # RepositoryManager
123
+ end # DataMapper
124
+ end # Yogo