simplec 0.1.1 → 0.1.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +308 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/simplec_manifest.js +2 -0
  6. data/app/assets/fonts/simplec/summernote.eot +0 -0
  7. data/app/assets/fonts/simplec/summernote.ttf +0 -0
  8. data/app/assets/fonts/simplec/summernote.woff +0 -0
  9. data/app/assets/javascripts/simplec/application.js +13 -0
  10. data/app/assets/javascripts/simplec/summernote-config.js +82 -0
  11. data/app/assets/javascripts/simplec/summernote.js +7046 -0
  12. data/app/assets/stylesheets/simplec/application.css +15 -0
  13. data/app/assets/stylesheets/simplec/summernote.scss +691 -0
  14. data/app/constraints/simplec/subdomains.rb +23 -0
  15. data/app/controllers/simplec/application_controller.rb +9 -0
  16. data/app/controllers/simplec/pages_controller.rb +19 -0
  17. data/app/helpers/simplec/application_helper.rb +4 -0
  18. data/app/jobs/simplec/application_job.rb +4 -0
  19. data/app/mailers/simplec/application_mailer.rb +6 -0
  20. data/app/models/simplec/application_record.rb +5 -0
  21. data/app/models/simplec/embedded_image.rb +15 -0
  22. data/app/models/simplec/page.rb +203 -0
  23. data/app/models/simplec/subdomain.rb +30 -0
  24. data/app/views/simplec/fields/_editor.html.erb +5 -0
  25. data/app/views/simplec/fields/_file.html.erb +16 -0
  26. data/app/views/simplec/fields/_image.html.erb +16 -0
  27. data/app/views/simplec/fields/_string.html.erb +4 -0
  28. data/app/views/simplec/fields/_text.html.erb +4 -0
  29. data/app/views/simplec/pages/show.html.erb +2 -0
  30. data/config/routes.rb +8 -0
  31. data/db/migrate/20170809204353_create_simplec_pages.rb +30 -0
  32. data/db/migrate/20170809204511_create_simplec_subdomains.rb +12 -0
  33. data/db/migrate/20170809210304_create_simplec_embedded_images.rb +15 -0
  34. data/lib/simplec/action_controller/extensions.rb +56 -0
  35. data/lib/simplec/action_view/helper.rb +118 -0
  36. data/lib/simplec/embedded_image_actions.rb +37 -0
  37. data/lib/simplec/engine.rb +18 -0
  38. data/lib/simplec/version.rb +3 -0
  39. data/lib/simplec.rb +5 -0
  40. data/lib/tasks/simplec_tasks.rake +4 -0
  41. metadata +41 -2
@@ -0,0 +1,9 @@
1
+ module Simplec
2
+ class ApplicationController < ::ActionController::Base
3
+ protect_from_forgery with: :exception
4
+
5
+ after_action do
6
+ Thread.current[:simplec_subdomain] = nil
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ require_dependency "simplec/application_controller"
2
+
3
+ module Simplec
4
+ class PagesController < ApplicationController
5
+
6
+ def show
7
+ @page = page("/#{params[:path]}")
8
+ render layout: layout(@page)
9
+ end
10
+
11
+ private
12
+
13
+ def layout(page)
14
+ [page.layout, page.subdomain.default_layout, 'public']. # TODO allow config for public
15
+ reject(&:blank?).first
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module Simplec
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Simplec
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Simplec
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Simplec
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Simplec
2
+ class EmbeddedImage < ApplicationRecord
3
+ belongs_to :embeddable,
4
+ polymorphic: true
5
+
6
+ dragonfly_accessor :asset
7
+
8
+ def url
9
+ return unless self.asset
10
+ return self.asset.url unless persisted?
11
+ self.asset.url(ei: Base64.urlsafe_encode64(self.id.to_s))
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,203 @@
1
+ # Simplec::Page
2
+ #
3
+ # This class represents a page in the system.
4
+ #
5
+ # Each page has a class located in:
6
+ # app/models/page/NAME.html.erb
7
+ #
8
+ # Each page has a partial template in:
9
+ # app/views/pages/_NAME.html.erb
10
+ #
11
+ # Where NAME is the demodulized, snake-case name of the Page Subclasss. For
12
+ # example:
13
+ #
14
+ # class Page::Home < Page
15
+ # field :h1
16
+ # end
17
+ #
18
+ # Would live in app/models/page/home.html.erb and have a template in
19
+ # app/views/pages/_home.html.erb.
20
+ #
21
+ # Each page has the following attributes by default:
22
+ # - page.title
23
+ #
24
+ # Read the documentation below for the class method .field, it is the most
25
+ # used method.
26
+ #
27
+ #
28
+ module Simplec
29
+ class Page < ApplicationRecord
30
+
31
+
32
+ # TODO Document FILE_FIELDS constant
33
+ #
34
+ FILE_FIELDS = [:file, :image].freeze
35
+
36
+ belongs_to :subdomain,
37
+ optional: false
38
+ belongs_to :parent,
39
+ class_name: 'Page',
40
+ optional: true
41
+ has_many :childern,
42
+ class_name: 'Page',
43
+ foreign_key: :parent_id
44
+ has_many :embedded_images,
45
+ as: :embeddable,
46
+ dependent: :delete_all
47
+
48
+ validates :type,
49
+ presence: true
50
+ validates :path,
51
+ presence: true,
52
+ uniqueness: { scope: :subdomain_id }
53
+ validates :layout,
54
+ inclusion: {in: :layouts, allow_blank: true}
55
+ validates :title,
56
+ presence: true
57
+
58
+ before_validation :match_parent_subdomain
59
+ before_validation :build_path
60
+ after_save :link_embedded_images!
61
+
62
+ # Define a field on the page.
63
+ #
64
+ # - name - name of field
65
+ # - options[:type] - :string (default), :text, :editor, :file, :image
66
+ # :string - yields a text input
67
+ # :text - yields a textarea
68
+ # :editor - yields a summernote editor
69
+ # :file - yields a file field
70
+ # :image - yields a file field with image preview
71
+ #
72
+ # There is as template for each type for customization located in:
73
+ # app/views/shared/fields/_TYPE.html.erb
74
+ #
75
+ # Defines a field on a subclass. This creates a getter and setter for the
76
+ # name passed in. The options are used when building the administration forms.
77
+ #
78
+ # Regular dragonfly validations are available on :file and :image fields.
79
+ # http://markevans.github.io/dragonfly/models#validations
80
+ #
81
+ def self.field(name, options={})
82
+ fields[name] = {name: name, type: :string}.merge(options)
83
+ if FILE_FIELDS.member?(fields[name][:type])
84
+ dragonfly_accessor name
85
+ data_field :"#{name}_uid"
86
+ data_field :"#{name}_name"
87
+ else
88
+ data_field(name)
89
+ end
90
+ end
91
+
92
+ # List fields
93
+ #
94
+ def self.fields
95
+ @fields ||= Hash.new
96
+ end
97
+
98
+ # Return a constantized type, whitelisted by known subclasses.
99
+ #
100
+ def self.type(type)
101
+ ::Page rescue raise '::Page not defined, define it in app/models'
102
+ raise 'Unsupported Page Type; define in app/models/page/' unless ::Page.subclasses.map(&:name).
103
+ member?(type)
104
+ type.constantize
105
+ end
106
+
107
+ # Return names of fields.
108
+ #
109
+ # type: :file, is the only option
110
+ #
111
+ def self.field_names(type=nil)
112
+ _fields = case type
113
+ when :file
114
+ fields.select {|k, v| FILE_FIELDS.member?(v[:type])}
115
+ else
116
+ fields
117
+ end
118
+ _fields.keys
119
+ end
120
+
121
+ # Return field options for building forms.
122
+ #
123
+ def field_options
124
+ self.class.fields.values
125
+ end
126
+
127
+ # List parents, closest to furthest.
128
+ #
129
+ def parents
130
+ page, parents = self, Array.new
131
+ while page.parent
132
+ page = page.parent
133
+ parents << page
134
+ end
135
+ parents
136
+ end
137
+
138
+ # Before validation hook.
139
+ #
140
+ # Build the path of the page to be used in routing.
141
+ #
142
+ def build_path
143
+ _pages = self.parents.reverse + [self]
144
+ self.path = "/#{_pages.map(&:slug).reject(&:blank?).join('/')}"
145
+ end
146
+
147
+ # Before validation hook
148
+ #
149
+ # All pages need to have a matching subdomain to parent page
150
+ #
151
+ def match_parent_subdomain
152
+ return unless self.parent
153
+ self.subdomain = self.parent.subdomain
154
+ end
155
+
156
+ def find_embedded_images
157
+ text = self.fields.values.join(' ')
158
+ matches = text.scan(/ei=([^&]*)/)
159
+ encoded_ids = matches.map(&:first)
160
+ ids = encoded_ids.map { |eid| Base64.urlsafe_decode64(URI.unescape(eid)) }
161
+ EmbeddedImage.includes(:embeddable).find(ids)
162
+ end
163
+
164
+ def link_embedded_images!
165
+ images = self.find_embedded_images
166
+ images.each do |image|
167
+ raise AlreadyLinkedEmbeddedImage if image.embeddable &&
168
+ image.embeddable != self
169
+ image.update!(embeddable: self)
170
+ end
171
+ end
172
+
173
+ def layouts
174
+ @layouts ||= Subdomain.new.layouts
175
+ end
176
+
177
+ module Normalizers
178
+ def slug=(val)
179
+ val = val ? val.to_s.split('/').reject(&:blank?).join('/') : val
180
+ super val
181
+ end
182
+
183
+ def slug
184
+ if self.parent_id && self.parent.nil?
185
+ self.path.to_s.split('/').reject(&:blank?).join('/')
186
+ else
187
+ super
188
+ end
189
+ end
190
+ end
191
+ prepend Normalizers
192
+
193
+ class AlreadyLinkedEmbeddedImage < StandardError; end
194
+
195
+ private
196
+
197
+ def self.data_field(name)
198
+ define_method(name) { fields[name.to_s] }
199
+ define_method("#{name}=") { |val| fields[name.to_s] = val }
200
+ end
201
+ end
202
+ end
203
+
@@ -0,0 +1,30 @@
1
+ module Simplec
2
+ class Subdomain < ApplicationRecord
3
+ has_many :pages
4
+ has_and_belongs_to_many :document_sets
5
+ has_and_belongs_to_many :documents
6
+
7
+ validates :name,
8
+ presence: true,
9
+ exclusion: { in: %w(admin) }
10
+ validates :default_layout,
11
+ inclusion: {in: :layouts, allow_blank: true}
12
+
13
+ def layouts
14
+ @layouts ||= Dir[Rails.root.join('app/views/layouts').to_s + "/*.html.*"].
15
+ map{|n| File.basename(n).split('.', 2).first }.
16
+ reject{|n| n =~ /\A_/ || n =~ /mailer/ || n =~ /application/ || n =~ /sessions/}.
17
+ sort
18
+ end
19
+
20
+ module Normalizers
21
+
22
+ # Force lowercase name
23
+ #
24
+ def name=(val)
25
+ super (val ? val.to_s.strip.downcase : val)
26
+ end
27
+ end
28
+ prepend Normalizers
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ <div class="form-group">
2
+ <%= f.label name, class: 'control-label' %>
3
+ <%= f.hidden_field name, id: "#{name}-#{f.object.object_id}"%>
4
+ <%= tag :div, class: 'editor-field', data: {input: "##{name}-#{f.object.object_id}"} %></div>
5
+ </div>
@@ -0,0 +1,16 @@
1
+ <div class="form-group">
2
+ <%= f.label name, class: 'control-label' %>
3
+ <% if f.object.send(name) %>
4
+ <div>
5
+ <p class="form-control-static"><%= f.object.send(name).name %></p>
6
+ <div class="checkbox">
7
+ <label>
8
+ <%= f.check_box :"remove_#{name}" %>
9
+ Remove
10
+ </label>
11
+ </div>
12
+ </div>
13
+ <% end %>
14
+ <%= f.file_field name %>
15
+ <%= f.hidden_field :"retained_#{name}" %>
16
+ </div>
@@ -0,0 +1,16 @@
1
+ <div class="form-group">
2
+ <%= f.label name, class: 'control-label' %>
3
+ <% if f.object.send(name) %>
4
+ <div>
5
+ <%= image_tag f.object.send(name).thumb('100x').url %>
6
+ <div class="checkbox">
7
+ <label>
8
+ <%= f.check_box :"remove_#{name}" %>
9
+ Remove
10
+ </label>
11
+ </div>
12
+ </div>
13
+ <% end %>
14
+ <%= f.file_field name %>
15
+ <%= f.hidden_field :"retained_#{name}" %>
16
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="form-group">
2
+ <%= f.label name, class: 'control-label' %>
3
+ <%= f.text_field name, class: 'form-control' %>
4
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="form-group">
2
+ <%= f.label name, class: 'control-label' %>
3
+ <%= f.text_area name, class: 'form-control' %>
4
+ </div>
@@ -0,0 +1,2 @@
1
+ <%= head @page %>
2
+ <%= template @page %>
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Simplec::Engine.routes.draw do
2
+
3
+ scope constraints: Simplec::Subdomains do
4
+ root 'pages#show'
5
+ get '*path', to: 'pages#show'
6
+ end
7
+
8
+ end
@@ -0,0 +1,30 @@
1
+ class CreateSimplecPages < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :simplec_pages, id: :uuid, default: 'gen_random_uuid()' do |t|
4
+ t.string :type
5
+ t.uuid :subdomain_id
6
+ t.uuid :parent_id
7
+ t.string :path
8
+ t.string :slug
9
+ t.string :title
10
+ t.string :meta_description
11
+ t.jsonb :fields
12
+ t.string :layout
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :simplec_pages, :type
18
+ add_index :simplec_pages, :subdomain_id
19
+ add_index :simplec_pages, :parent_id
20
+ add_index :simplec_pages, :path
21
+ add_index :simplec_pages, [:subdomain_id, :path], unique: true
22
+
23
+ reversible do |dir|
24
+ dir.up {
25
+ execute "ALTER TABLE simplec_pages ALTER COLUMN fields SET DEFAULT '{}'::JSONB"
26
+ }
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ class CreateSimplecSubdomains < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :simplec_subdomains, id: :uuid, default: 'gen_random_uuid()' do |t|
4
+ t.string :name
5
+ t.string :default_layout
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :simplec_subdomains, :name, unique: true
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSimplecEmbeddedImages < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :simplec_embedded_images, id: :uuid, default: 'gen_random_uuid()' do |t|
4
+ t.string :embeddable_type
5
+ t.integer :embeddable_id
6
+ t.string :asset_uid
7
+ t.string :asset_name
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :simplec_embedded_images, [:embeddable_type, :embeddable_id],
13
+ name: 'simplec_embedded_images_type_id_index'
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ module Simplec
2
+ module ActionController
3
+ module Extensions
4
+
5
+ def self.included(receiver)
6
+ receiver.helper_method :subdomain, :page,
7
+ :simplec_path, :simplec_url
8
+ end
9
+
10
+ def subdomain(name=nil)
11
+ name ||= request.subdomain
12
+ @_subdomains ||= Hash.new
13
+ return @_subdomains[name] if @_subdomains[name]
14
+ @_subdomains[name] = Subdomain.find_by!(name: name)
15
+ end
16
+
17
+ def page(path, options={})
18
+ @_page ||= Hash.new
19
+ return @_page[path] if @_page[path]
20
+
21
+ _subdomain = subdomain
22
+ _subdomain = Subdomain.find_by!(
23
+ name: options[:subdomain]
24
+ ) if options[:subdomain]
25
+
26
+ @_page[path] = _subdomain.pages.find_by!(path: path)
27
+ end
28
+
29
+ def simplec_path(page_or_path, options={})
30
+ # TODO cache page_paths
31
+ _page = page_or_path.is_a?(Page) ?
32
+ page_or_path : page(page_or_path, options)
33
+
34
+ unless _page
35
+ raise ActiveRecord::RecordNotFound if options[:raise]
36
+ return nil
37
+ end
38
+
39
+ _page.path
40
+ end
41
+
42
+ def simplec_url(page_or_path, options={})
43
+ # TODO cache page_urls
44
+ _page = page_or_path.is_a?(Page) ?
45
+ page_or_path : page(page_or_path, options)
46
+
47
+ unless _page
48
+ raise ActiveRecord::RecordNotFound if options[:raise]
49
+ return nil
50
+ end
51
+
52
+ URI.join(root_url(subdomain: _page.subdomain.try(:name)), _page.path).to_s
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,118 @@
1
+ module Simplec
2
+ module ActionView
3
+ module Helper
4
+
5
+ def head(page)
6
+ content_for :title, page.title
7
+ content_for :meta_description, page.meta_description
8
+ end
9
+
10
+ def title
11
+ content_for :title
12
+ end
13
+
14
+ def meta_description_tag
15
+ tag :meta, name: 'description', content: content_for(:meta_description)
16
+ end
17
+
18
+ def template(page)
19
+ _template = page.type.demodulize.downcase
20
+ render "pages/#{_template}"
21
+ end
22
+
23
+ def page_field(f, options={})
24
+ render "simplec/fields/#{options[:type]}", options.merge(f: f)
25
+ end
26
+
27
+ # Retreive a document.
28
+ #
29
+ # slug - matching a Document slug.
30
+ # options[:subdomain] - a string matching a Subdomain name, find a document
31
+ # from another subdomain
32
+ # options[:raise] - if true, casuse a 404 if document isn't found
33
+ #
34
+ # Example:
35
+ #
36
+ # -# Save as...
37
+ # <%= link_to doc('/permission/general').name,
38
+ # doc('/permission/general').path,
39
+ # download: true %>
40
+ #
41
+ # -# Display in new window...
42
+ # <%= link_to doc('/permission/general').name,
43
+ # doc('/permission/general').path,
44
+ # target: '_blank' %>
45
+ #
46
+ def doc(slug, options={})
47
+ @_docs ||= Hash.new
48
+ key = "[#{options[:subdomain]}][#{slug}]"
49
+ return @_docs[key] if @_docs[key]
50
+ @_docs[key] = subdomain(options[:subdomain]).
51
+ documents.find_by(slug: slug)
52
+ raise ActiveRecord::RecordNotFound if options[:raise] && !@_docs[key]
53
+ @_docs[key]
54
+ end
55
+
56
+ # Retreive a document.
57
+ #
58
+ # slug - matching a Document slug.
59
+ # options[:subdomain] - a string matching a Subdomain name, find a document
60
+ # set from another subdomain
61
+ # options[:raise] - if true, casuse a 404 if document set isn't found
62
+ #
63
+ # Example:
64
+ #
65
+ # <% docset('/faith').each do |doc| %>
66
+ # <%= link_to doc.name, doc.path %>
67
+ # <% end %>
68
+ #
69
+ def docset(slug, options={})
70
+ @_docsets ||= Hash.new
71
+ key = "[#{options[:subdomain]}][#{slug}]"
72
+ return @_docsets[key] if @_docsets[key]
73
+ set = subdomain(options[:subdomain]).
74
+ document_sets.find_by(slug: slug)
75
+ raise ActiveRecord::RecordNotFound if options[:raise] && !set
76
+ @_docsets[key] = set.documents
77
+ end
78
+
79
+ # page_or_path - a page object or a path of a page
80
+ # options[:subdomain] - a string matching a Subdomain name, find a page
81
+ # from another subdomain
82
+ # options[:raise] - if true, casuse a 404 if a page set isn't found
83
+ #
84
+ # Example:
85
+ #
86
+ # <% subpages('/give-volunteer', conditions: ->{ order(title: :asc) }).each do |page| %>
87
+ # ... do something with page ...
88
+ # <% end%>
89
+ def subpages(page_or_path, options={})
90
+ if options[:conditions] && !options[:conditions].respond_to?(:call)
91
+ raise ArgumentError, <<-ERROR
92
+ #{options[:conditions]} was passed as :conditions but is not callable.
93
+ "Pass a callable instead: `conditions: -> { where(approved: true) }`
94
+ ERROR
95
+ end
96
+
97
+ @_subpages ||= Hash.new # TODO apply new conditions after cache
98
+ key = "[#{options[:subdomain]}][#{page_or_path}]"
99
+
100
+ unless @_subpages[key]
101
+ page = page_or_path.is_a?(Page) ? page_or_path : nil
102
+ page ||= subdomain(options[:subdomain]).pages.find_by(path: page_or_path)
103
+
104
+ unless page
105
+ raise ActiveRecord::RecordNotFound if options[:raise]
106
+ return @_subpages[key] = Array.new
107
+ end
108
+
109
+ @_subpages[key] = page.subpages
110
+ end
111
+
112
+ @_subpages[key].respond_to?(:merge) && options[:conditions] ?
113
+ @_subpages[key].merge(options[:conditions]) : @_subpages[key]
114
+ end
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,37 @@
1
+ module Simplec
2
+ module EmbeddedImageActions
3
+ module ClassMethods; end
4
+
5
+ module InstanceMethods
6
+
7
+ def create
8
+ @embedded_image = EmbeddedImage.new(embedded_image_params)
9
+ if @embedded_image.save
10
+ respond_to do |format|
11
+ format.json {
12
+ render :show, status: 201, location: @embedded_image.url
13
+ }
14
+ end
15
+ else
16
+ respond_to do |format|
17
+ format.json {
18
+ render status: 422, json: @embedded_image.errors
19
+ }
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def embedded_image_params
27
+ params.permit(:asset_url, :asset_name)
28
+ end
29
+
30
+ end
31
+
32
+ def self.included(receiver)
33
+ receiver.extend ClassMethods
34
+ receiver.send :include, InstanceMethods
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require 'simplec/action_controller/extensions'
2
+ require 'simplec/action_view/helper'
3
+
4
+ module Simplec
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Simplec
7
+
8
+ initializer "simplec_controller_extensions" do
9
+ ActiveSupport.on_load(:action_controller_base) {
10
+ prepend Simplec::ActionController::Extensions
11
+ helper Simplec::ActionView::Helper
12
+ }
13
+ ActiveSupport.on_load(:active_record) {
14
+ Dir["#{Rails.root}/app/models/page/*.rb"].each {|file| require_dependency file }
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Simplec
2
+ VERSION = '0.1.2'
3
+ end
data/lib/simplec.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "simplec/engine"
2
+
3
+ module Simplec
4
+ # TODO configuration options
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :simplec do
3
+ # # Task goes here
4
+ # end