yogo-project 0.3.2

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.
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