regulate 0.0.1

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 (81) hide show
  1. data/.gitignore +14 -0
  2. data/.rvmrc +1 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +117 -0
  6. data/LICENSE +20 -0
  7. data/README.md +3 -0
  8. data/Rakefile +10 -0
  9. data/app/controllers/regulate/admin/pages_controller.rb +69 -0
  10. data/app/controllers/regulate/pages_controller.rb +13 -0
  11. data/app/models/regulate/page.rb +21 -0
  12. data/app/views/regulate/admin/pages/_form.html.erb +36 -0
  13. data/app/views/regulate/admin/pages/edit.html.erb +1 -0
  14. data/app/views/regulate/admin/pages/index.html.erb +10 -0
  15. data/app/views/regulate/admin/pages/new.html.erb +1 -0
  16. data/app/views/regulate/pages/show.html.erb +14 -0
  17. data/config/regulate.yml +10 -0
  18. data/config/routes.rb +22 -0
  19. data/lib/generators/regulate/install_generator.rb +39 -0
  20. data/lib/generators/regulate/views_generator.rb +73 -0
  21. data/lib/generators/templates/regulate.css +2 -0
  22. data/lib/generators/templates/regulate.js +44 -0
  23. data/lib/generators/templates/regulate.rb +13 -0
  24. data/lib/generators/templates/regulate.yml +10 -0
  25. data/lib/regulate.rb +30 -0
  26. data/lib/regulate/engine.rb +50 -0
  27. data/lib/regulate/git.rb +17 -0
  28. data/lib/regulate/git/errors.rb +22 -0
  29. data/lib/regulate/git/interface.rb +249 -0
  30. data/lib/regulate/git/model.rb +16 -0
  31. data/lib/regulate/git/model/base.rb +282 -0
  32. data/lib/regulate/version.rb +4 -0
  33. data/public/javascripts/jquery.min.js +167 -0
  34. data/public/javascripts/regulate_admin.js +166 -0
  35. data/regulate.gemspec +29 -0
  36. data/test/dummy/Rakefile +7 -0
  37. data/test/dummy/app/controllers/application_controller.rb +3 -0
  38. data/test/dummy/app/helpers/application_helper.rb +2 -0
  39. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  40. data/test/dummy/config.ru +4 -0
  41. data/test/dummy/config/application.rb +45 -0
  42. data/test/dummy/config/boot.rb +10 -0
  43. data/test/dummy/config/database.yml +22 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +26 -0
  46. data/test/dummy/config/environments/production.rb +49 -0
  47. data/test/dummy/config/environments/test.rb +35 -0
  48. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/dummy/config/initializers/inflections.rb +10 -0
  50. data/test/dummy/config/initializers/mime_types.rb +5 -0
  51. data/test/dummy/config/initializers/secret_token.rb +7 -0
  52. data/test/dummy/config/initializers/session_store.rb +8 -0
  53. data/test/dummy/config/locales/en.yml +5 -0
  54. data/test/dummy/config/regulate.yml +10 -0
  55. data/test/dummy/config/routes.rb +58 -0
  56. data/test/dummy/public/404.html +26 -0
  57. data/test/dummy/public/422.html +26 -0
  58. data/test/dummy/public/500.html +26 -0
  59. data/test/dummy/public/favicon.ico +0 -0
  60. data/test/dummy/public/javascripts/application.js +2 -0
  61. data/test/dummy/public/javascripts/controls.js +965 -0
  62. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  63. data/test/dummy/public/javascripts/effects.js +1123 -0
  64. data/test/dummy/public/javascripts/prototype.js +6001 -0
  65. data/test/dummy/public/javascripts/rails.js +175 -0
  66. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  67. data/test/dummy/script/rails +6 -0
  68. data/test/git_test.rb +79 -0
  69. data/test/integration/navigation_test.rb +7 -0
  70. data/test/models/regulate_git_model_base_lint_test.rb +7 -0
  71. data/test/models/regulate_git_model_base_test.rb +23 -0
  72. data/test/models/regulate_page_test.rb +19 -0
  73. data/test/regulate_test.rb +19 -0
  74. data/test/routing_test.rb +32 -0
  75. data/test/support/integration_case.rb +5 -0
  76. data/test/test_helper.rb +27 -0
  77. data/test/tmp/config/initializers/regulate.rb +12 -0
  78. data/test/tmp/config/regulate.yml +10 -0
  79. data/test/tmp/public/javascripts/regulate.js +49 -0
  80. data/test/tmp/public/stylesheets/regulate.css +3 -0
  81. metadata +312 -0
@@ -0,0 +1,2 @@
1
+ .editable {}
2
+
@@ -0,0 +1,44 @@
1
+ (function($){
2
+ var Regulate = window.Regulate || Regulate || {};
3
+
4
+ Regulate = {
5
+ init: function Regulate_init(){
6
+ // some code that needs to be executed after doc ready
7
+ Regulate.bindEvents();
8
+ },
9
+ bindEvents: function Regulate_liveEvents(){
10
+ // more globally bound events
11
+ },
12
+ helpers: function Regulate_helpers(){
13
+ // helper for searching through arrays
14
+ Regulate.helpers.arraySearch = function(a){
15
+ var o = {};
16
+ for(var i=0;i<a.length;i++){
17
+ o[a[i]]='';
18
+ }
19
+ return o;
20
+ };
21
+ //j. resigs array remove overload
22
+ Array.prototype.remove = function(from, to) {
23
+ var rest = this.slice((to || from) + 1 || this.length);
24
+ this.length = from < 0 ? this.length + from : from;
25
+ return this.push.apply(this, rest);
26
+ };
27
+ // duck punch Array.indexOf into IE browsers
28
+ if(!Array.indexOf){
29
+ Array.prototype.indexOf = function(obj){
30
+ for(var i=0; i<this.length; i++){
31
+ if(this[i]==obj){
32
+ return i;
33
+ }
34
+ }
35
+ return -1;
36
+ };
37
+ }
38
+ }
39
+ };
40
+
41
+ window.Regulate = Regulate;
42
+ $(document).ready(Regulate.init);
43
+
44
+ })(jQuery);
@@ -0,0 +1,13 @@
1
+ Regulate.setup do |config|
2
+
3
+ # Role that allows a user to manage pages and define what parts are editable, default is :admin
4
+ # The namespace that you would like to use for the routes. The default is 'cms'.
5
+ # @example Setting a custom route namespace
6
+ # Regulate.setup do |config|
7
+ # config.route_namespace = 'my_custom_route_namespace'
8
+ # end
9
+ #
10
+ # config.route_namespace = 'my_custom_route_namespace'
11
+
12
+ end
13
+
@@ -0,0 +1,10 @@
1
+ development:
2
+ repo: db/repos/development.git
3
+ test: &test
4
+ repo: db/repos/test.git
5
+ production:
6
+ repo: db/repos/production.git
7
+ daily:
8
+ repo: db/repos/daily.git
9
+ cucumber:
10
+ <<: *test
data/lib/regulate.rb ADDED
@@ -0,0 +1,30 @@
1
+ # Requires
2
+ require 'active_support/dependencies'
3
+
4
+ # Our top level module to contain all of our engine gem functionality
5
+ module Regulate
6
+
7
+ # Autoloads
8
+ autoload :Git , 'regulate/git'
9
+
10
+ # Our host application root path
11
+ # We set this when the engine is initialized
12
+ mattr_accessor :app_root
13
+
14
+ # The repo we'll be using
15
+ # We set this when the engine is initialized
16
+ mattr_accessor :repo
17
+
18
+ # Route namespace
19
+ mattr_accessor :route_namespace
20
+ @@route_namespace = "cms"
21
+
22
+ # Yield self on setup for nice config blocks
23
+ def self.setup
24
+ yield self
25
+ end
26
+
27
+ end # module Regulate
28
+
29
+ # Requires
30
+ require 'regulate/engine' if defined?(Rails)
@@ -0,0 +1,50 @@
1
+ module Regulate
2
+
3
+ # Our instance of the Rails::Engine class that will allow us to load our functionality
4
+ # in to a host app
5
+ class Engine < Rails::Engine
6
+
7
+ initializer 'regulate.load_app_instance_data' do |app|
8
+
9
+ Regulate.setup do |config|
10
+
11
+ # Set our host app root path
12
+ config.app_root = Rails.root
13
+
14
+ # Check for the existence of a custom YAML file in the host app
15
+ if( File.exists?( File.join( config.app_root , 'config' , 'regulate.yml') ) )
16
+ yaml_path = File.join(config.app_root, "config", "regulate.yml")
17
+ else
18
+ # Use the provided default in our gem
19
+ yaml_path = File.join(root , "config", "regulate.yml")
20
+ end
21
+
22
+ yaml_data = YAML.load_file(yaml_path)[Rails.env]
23
+
24
+ # Now set our repo
25
+ repo_path = File.join(config.app_root, yaml_data['repo'])
26
+ repo = File.join(repo_path, ".git")
27
+ init = false
28
+
29
+ if !File.directory?(repo_path)
30
+ FileUtils.mkdir_p(repo_path)
31
+ init = true
32
+ elsif !File.directory?(repo)
33
+ init = true
34
+ end
35
+
36
+ Grit::Repo.init(repo_path) if init
37
+ config.repo = Grit::Repo.new(repo_path)
38
+
39
+ end
40
+
41
+ end
42
+
43
+ initializer "static assets" do |app|
44
+ app.middleware.use ::ActionDispatch::Static, "#{root}/public"
45
+ end
46
+
47
+ end # class Engine
48
+
49
+ end # module Regulate
50
+
@@ -0,0 +1,17 @@
1
+ # Requires
2
+ require 'grit'
3
+
4
+ module Regulate
5
+
6
+ # Our module to contain all get related functionality
7
+ module Git
8
+
9
+ # Autoloads
10
+ autoload :Errors , 'regulate/git/errors'
11
+ autoload :Model , 'regulate/git/model'
12
+ autoload :Interface , 'regulate/git/interface'
13
+
14
+ end # module Git
15
+
16
+ end # module Regulate
17
+
@@ -0,0 +1,22 @@
1
+ module Regulate
2
+
3
+ module Git
4
+
5
+ # The module that will hold all Git related errors
6
+ module Errors
7
+
8
+ # Used when attempting to save a Git resource that already exists
9
+ class DuplicatePageError < StandardError; end
10
+
11
+ # Used when there is a problem with a Git resource
12
+ class InvalidGitResourceError < StandardError; end
13
+
14
+ # Used when attempting to persist a Git resource that doesn't already exist
15
+ class PageDoesNotExist < StandardError; end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,249 @@
1
+ module Regulate
2
+
3
+ module Git
4
+
5
+ # The class that we'll use to interact with our Git repositories
6
+ class Interface
7
+
8
+ class << self
9
+
10
+ # Find all commits for a given id
11
+ # Essentially looks for all folders in our repo whose names match the given id
12
+ #
13
+ # @param [String] id The id to search for
14
+ # @return [Array] An array of Grit::Commit objects
15
+ def commits( id )
16
+ Regulate.repo.log( 'master' , id )
17
+ end
18
+
19
+ # Find and return relavant data for a given id
20
+ #
21
+ # @param [String] id The id to search for
22
+ # @return [String] The contents of the attributes.json file for the given id
23
+ def find( id )
24
+ begin
25
+ ( Regulate.repo.tree / File.join( id , 'attributes.json' ) ).data
26
+ rescue
27
+ nil
28
+ end
29
+ end
30
+
31
+ # Return an array of attributes.json data strings for all IDs in the repo
32
+ #
33
+ # @return [Array] An array of JSON strings
34
+ def find_all
35
+ Regulate.repo.tree.contents.collect { |tree|
36
+ attributes_json = ( tree / 'attributes.json' )
37
+ attributes_json.data unless attributes_json.nil?
38
+ }.compact
39
+ end
40
+
41
+ # Find and return the rendered HTML for a given id
42
+ #
43
+ # @param [String] id The id to search for
44
+ # @return [String] The contents of the rendered.html file for the given id
45
+ def find_rendered( id )
46
+ begin
47
+ ( Regulate.repo.tree / File.join( id , 'rendered.html' ) ).data
48
+ rescue
49
+ nil
50
+ end
51
+ end
52
+
53
+ # Find the contents of a file given it's version SHA
54
+ # @param [String] version The version SHA hash of the blob
55
+ # @return [String] The contents of the blob at that version
56
+ def find_by_version( id , version )
57
+ begin
58
+ ( Regulate.repo.commit(version).tree / File.join( id , 'attributes.json' ) ).data
59
+ rescue
60
+ nil
61
+ end
62
+ end
63
+
64
+ # Save changes to a resource directory with a commit
65
+ # All of the keys in the example are expected!
66
+ #
67
+ # @param [Hash] options A hash containing all the data we'll need to make a commit
68
+ # @example A sample save call
69
+ # Regulate::Git::Interface.save({
70
+ # :id => id,
71
+ # :commit_message => commit_message,
72
+ # :author_name => author_name,
73
+ # :author_email => author_email,
74
+ # :attributes => attributes_json,
75
+ # :rendered => rendered_html
76
+ # })
77
+ def save( options = {} )
78
+ # Begin a commit operation
79
+ commit( options , :create ) do |index|
80
+ # Persist changes to both of our needed files
81
+ index.add(File.join(options[:id], 'attributes.json'), options[:attributes])
82
+ index.add(File.join(options[:id], 'rendered.html'), options[:rendered])
83
+ end
84
+ end
85
+
86
+ # Delete a resource directory with a commit
87
+ # All of the keys in the example are expected!
88
+ #
89
+ # @param [Hash] options A hash containing all the data we'll need to make a commit
90
+ # @example A sample delete call
91
+ # Regulate::Git::Interface.delete({
92
+ # :id => id,
93
+ # :commit_message => commit_message,
94
+ # :author_name => author_name,
95
+ # :author_email => author_email
96
+ # })
97
+ def delete( options = {} )
98
+ # Begin a commit operation
99
+ commit( options , :delete ) do |index|
100
+ # Delete both of our files in the resource directory to delete the directory
101
+ index.delete(File.join(options[:id],'attributes.json'))
102
+ index.delete(File.join(options[:id],'rendered.html'))
103
+ end
104
+ end
105
+
106
+ # Create a commit
107
+ #
108
+ # @param [Hash] options A hash containing all the data we'll need to make a commit
109
+ # @param [Symbol] type The type of commit that we're making - used to create commit messages and set other standard data
110
+ # @param [Proc] &blk The meat of the commit to be performed in other convenience functions
111
+ # @yield [index] Our actual commit operations performed in their respective convenience functions
112
+ # @yieldparam [Grit::Index] Our repo index
113
+ def commit( options , type , &blk )
114
+ # Build up commit data
115
+ commit = build_commit(options[:commit_message],
116
+ options[:author_name],
117
+ options[:author_email],
118
+ type)
119
+
120
+ # Get the index
121
+ index = Regulate.repo.index
122
+
123
+ # Read the tree if we are on master
124
+ if parent_commit = Regulate.repo.commit('master')
125
+ index.read_tree(parent_commit.tree.id)
126
+ end
127
+
128
+ yield index
129
+
130
+ # Determine if we have a parent so we can do stuff like git log
131
+ parents = parent_commit ? [parent_commit] : []
132
+
133
+ # Make a new commit and return the sha
134
+ actor = Grit::Actor.new(commit[:name], commit[:email])
135
+ sha = index.commit(commit[:message], parents, actor)
136
+
137
+ # Update our working directory so that the repo truly reflects the desired state
138
+ # Do this for each of our files that we might have made changes to
139
+ update_working_dir( index, File.join(options[:id],'attributes.json') )
140
+ update_working_dir( index, File.join(options[:id], 'rendered.html') )
141
+
142
+ sha
143
+ end
144
+
145
+ # Update the given file in the repository's working directory if there
146
+ # is a working directory present.
147
+ #
148
+ # index - The Grit::Index with which to sync.
149
+ # dir - The String directory in which the file lives.
150
+ # name - The String name of the page (may be in human format).
151
+ # format - The Symbol format of the page.
152
+ #
153
+ # Returns nothing.
154
+ # @see https://github.com/github/gollum/blob/79a1fb860d261d7f836fe75f5f8b4b88fc541a0c/lib/gollum/wiki.rb#L417
155
+ #
156
+ # @param [Grit::Index] index The git index that we are working on
157
+ # @param [String] path The path/path-to-file that we want to update to represent the true state of the repo
158
+ # @return [NilClass]
159
+ def update_working_dir( index , path )
160
+ unless Regulate.repo.bare
161
+ Dir.chdir(::File.join(Regulate.repo.path, '..')) do
162
+ if file_path_scheduled_for_deletion?(index.tree, path)
163
+ Regulate.repo.git.rm({'f' => true}, '--', path)
164
+ else
165
+ Regulate.repo.git.checkout({}, 'HEAD', '--', path)
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ # Determine if a given file is scheduled to be deleted in the next commit
172
+ # for the given Index.
173
+ #
174
+ # map - The Hash map:
175
+ # key - The String directory or filename.
176
+ # val - The Hash submap or the String contents of the file.
177
+ # path - The String path of the file including extension.
178
+ #
179
+ # Returns the Boolean response.
180
+ # @see https://github.com/github/gollum/blob/79a1fb860d261d7f836fe75f5f8b4b88fc541a0c/lib/gollum/wiki.rb#L459
181
+ #
182
+ # @param [Grit::Tree] map The tree containing our path that we are checking
183
+ # @param [String] path The path/path-to-file to check
184
+ # @return [TrueClass,FalseClass]
185
+ def file_path_scheduled_for_deletion?( map , path )
186
+ parts = path.split('/')
187
+ if parts.size == 1
188
+ deletions = map.keys.select { |k| !map[k] }
189
+ deletions.any? { |d| d == parts.first }
190
+ else
191
+ part = parts.shift
192
+ if rest = map[part]
193
+ file_path_scheduled_for_deletion?(rest, parts.join('/'))
194
+ else
195
+ false
196
+ end
197
+ end
198
+ end
199
+
200
+
201
+ # Setup some standard commit data
202
+ #
203
+ # @param [String] commit_message Our commit message
204
+ # @param [String] author_name Our author name
205
+ # @param [String] author_email Our author email
206
+ # @param [String] mode The type of action being taken in the commit
207
+ def build_commit( commit_message , author_name , author_email , mode )
208
+ {
209
+ :name => author_name.nil? ? "Anonymous" : author_name,
210
+ :email => author_email ||= "anon@anonymous.com",
211
+ :message => commit_message ||= mode.eql?(:create) ? "Creating new page." : "Updating page."
212
+ }
213
+ end
214
+
215
+ # Return whether or not a resource directory exists in the repo with the given ID
216
+ #
217
+ # @param [String] id The ID that we are checking
218
+ # @return [TrueClass,FalseClass]
219
+ def exists?( id )
220
+ Regulate.repo.commits.any? && !(current_tree / File.join(id, 'attributes.json')).nil?
221
+ end
222
+
223
+ # Return a commit object representing the last commit on the active repo
224
+ #
225
+ # @return [Grit::Commit] A Grit::Commit object representing the most recent commit on the active repository
226
+ def last_commit
227
+ branch = 'master'
228
+ return nil unless Regulate.repo.commits(branch).any?
229
+
230
+ # We should be able to use just repo.commits(branch).first here but
231
+ # this is a workaround for this bug:
232
+ # http://github.com/mojombo/grit/issues/issue/38
233
+ Regulate.repo.commits("#{branch}^..#{branch}").first || Regulate.repo.commits(branch).first
234
+ end
235
+
236
+ # The tree for the last commit on the active repo
237
+ def current_tree
238
+ c = last_commit
239
+ c ? c.tree : nil
240
+ end
241
+
242
+ end # Interface class methods ( class << self )
243
+
244
+ end # class Interface
245
+
246
+ end # module Git
247
+
248
+ end # module Regulate
249
+