binder_core 0.1.0
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.
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/README.markdown +86 -0
- data/Rakefile +1 -0
- data/binder_core.gemspec +24 -0
- data/lib/binder_core/asset.rb +45 -0
- data/lib/binder_core/binder.rb +26 -0
- data/lib/binder_core/compiler.rb +97 -0
- data/lib/binder_core/config.rb +98 -0
- data/lib/binder_core/console.rb +63 -0
- data/lib/binder_core/debug/stack.rb +86 -0
- data/lib/binder_core/defaults.rb +42 -0
- data/lib/binder_core/definition.rb +12 -0
- data/lib/binder_core/file_context.rb +46 -0
- data/lib/binder_core/file_ref.rb +38 -0
- data/lib/binder_core/folder_context.rb +55 -0
- data/lib/binder_core/parser.rb +82 -0
- data/lib/binder_core/parsers/asset_parser.rb +7 -0
- data/lib/binder_core/parsers/folder_parser.rb +7 -0
- data/lib/binder_core/parsers/null_parser.rb +6 -0
- data/lib/binder_core/parsers/text_parser.rb +11 -0
- data/lib/binder_core/registry.rb +45 -0
- data/lib/binder_core/scanner.rb +36 -0
- data/lib/binder_core/settings.rb +23 -0
- data/lib/binder_core/version.rb +3 -0
- data/lib/binder_core.rb +44 -0
- data/spec/binder_core/asset_spec.rb +64 -0
- data/spec/binder_core/binder_spec.rb +48 -0
- data/spec/binder_core/compiler_spec.rb +132 -0
- data/spec/binder_core/config_spec.rb +146 -0
- data/spec/binder_core/console_spec.rb +60 -0
- data/spec/binder_core/debug/stack_spec.rb +90 -0
- data/spec/binder_core/defaults_spec.rb +43 -0
- data/spec/binder_core/definition_spec.rb +20 -0
- data/spec/binder_core/file_context_spec.rb +79 -0
- data/spec/binder_core/file_ref_spec.rb +43 -0
- data/spec/binder_core/folder_context_spec.rb +104 -0
- data/spec/binder_core/parser_spec.rb +72 -0
- data/spec/binder_core/parsers/asset_parser_spec.rb +14 -0
- data/spec/binder_core/parsers/folder_parser_spec.rb +15 -0
- data/spec/binder_core/parsers/null_parser_spec.rb +9 -0
- data/spec/binder_core/parsers/text_parser_spec.rb +16 -0
- data/spec/binder_core/registry_spec.rb +61 -0
- data/spec/binder_core/scanner_spec.rb +79 -0
- data/spec/binder_core/settings_spec.rb +32 -0
- data/spec/binder_core_spec.rb +87 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/content/test/_hidden_file.txt +1 -0
- data/spec/support/content/test/_hidden_folder/childofhiddenfolder.txt +1 -0
- data/spec/support/content/test/emptyfolder/_ignore.txt +1 -0
- data/spec/support/content/test/logo.png +0 -0
- data/spec/support/content/test/subfolder/subtext.txt +1 -0
- data/spec/support/content/test/testfile.txt +5 -0
- data/spec/support/content/test/unknown.tmp +1 -0
- data/spec/support/parsers/test_parsers.rb +20 -0
- metadata +147 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Ruaidhri Devery
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
Binder is an experimental framework for writing basic data compilers. Binder provides hooks for custom parsers which control how filesystem data is converted to native Ruby objects.
|
2
|
+
|
3
|
+
Binder began as an attempt to create a light weight filesystem based CMS that works with GIT. It progressed into a more generic framework to accommodate other ideas for how compiling data from the filesystem could be useful. The system is designed to make it as easy as possible to prototype those ideas.
|
4
|
+
|
5
|
+
n.b. Please keep in mind, the term 'file' can refer to either a single file or a directory
|
6
|
+
|
7
|
+
##How it works##
|
8
|
+
The compiler takes care of traversing the files system, all you have to do is set rules for how it should parse specific kinds of files. You do this by defining a Parser. The definition requires two parameters, first a reference to your parser class and second a test block which will run against a file context. If the test block is run and returns true then your parser class is applied to the file and no further parser definitions are tested on that file. If no parser is matched then the file is effectively skipped.
|
9
|
+
|
10
|
+
Once a Parser has been applied its job is to obtain, format and output data from that file. If the parser does nothing, again the file is essentially skipped. Binder provides a small but powerful set of functionality to your parser. You have access to the file to read it into memory whatever way you like.
|
11
|
+
|
12
|
+
###descend###
|
13
|
+
If the file is a directory you can instruct the compiler to step in by calling 'descend'. It returns the full output data from that operation.
|
14
|
+
|
15
|
+
###create_asset###
|
16
|
+
You can also choose to add a file to your output as a reference rather than actual data by calling 'new_asset'.
|
17
|
+
|
18
|
+
###add###
|
19
|
+
Once you are done and have your output data you must 'add' it. From that point it is the responsibility of the the parent (folder) parser to pass that data down the chain.
|
20
|
+
|
21
|
+
Binder comes with some very basic default parsers defined which you are free to extend. These handle reading utf text files, descending folders and adding images files as assets. You can/should be overriding these by adding your own.
|
22
|
+
|
23
|
+
Tip: A parser that descends into a folder can itself create a whole set of local parser definitions only to be applied within the that folder and its children... powerful stuff ;)
|
24
|
+
|
25
|
+
##Configure the compiler##
|
26
|
+
You can configure the compiler with the following options:
|
27
|
+
|
28
|
+
BinderCore::Compiler.configure do
|
29
|
+
config.content_folder = "file/path/to/input/root/folder/"
|
30
|
+
config.base_asset_folder = "file/path/to/asset/output/folder/"
|
31
|
+
config.base_asset_url = "/public/asset/base/path/"
|
32
|
+
# size limit is a safety valve, 100 by default
|
33
|
+
config.folder_size_limit_mb = 100
|
34
|
+
end
|
35
|
+
|
36
|
+
##Define a Binder and add Parsers##
|
37
|
+
Parsers are added with a test block. That test block is passed a FileContext during the compile process. If it returns true then this parser (and only this parser) is applied to that file.
|
38
|
+
|
39
|
+
BinderCore.define :my_feed do |config|
|
40
|
+
config.add_parser MyCustomParsers::FeedRootFolderParser do |context|
|
41
|
+
context.route == []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
##Create a custom Parser##
|
46
|
+
|
47
|
+
module MyCustomParsers
|
48
|
+
class FeedRootFolderParser < BinderCore::Parser
|
49
|
+
# implement a parse method
|
50
|
+
def parse
|
51
|
+
# this is a directory so tell the compiler to descend into it
|
52
|
+
# it returns the compiled data
|
53
|
+
data = descend do |config|
|
54
|
+
# you can add parsers within parsers
|
55
|
+
# these are only applied to descendent files/folder
|
56
|
+
config.add_parser MyCustomMarkdownParser do |context|
|
57
|
+
context.file.ext.downcase == ".markdown"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# add the data object to the feed
|
61
|
+
add data
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
##Assets##
|
67
|
+
You can add a file as an asset to the data object. After compiling an optional asset linking phase will replace all the asset references with a url string. Here is how to add an asset in your parse method:
|
68
|
+
|
69
|
+
def parse
|
70
|
+
add new_asset
|
71
|
+
end
|
72
|
+
|
73
|
+
Your binder object provides you with an array of all the assets and you can handle those whatever way you want.
|
74
|
+
|
75
|
+
##Compile a feed##
|
76
|
+
|
77
|
+
feed_binder = BinderCore.compile :my_feed
|
78
|
+
feed_binder.link_assets
|
79
|
+
data = feed_binder.data
|
80
|
+
assets = feed_binder.assets
|
81
|
+
|
82
|
+
If you want more information about how this works, its all there in the source code. Any questions or feedback give me a shout.
|
83
|
+
|
84
|
+
##Idea Chalkboard:##
|
85
|
+
|
86
|
+
This is a TODO...
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/binder_core.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "binder_core/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "binder_core"
|
7
|
+
s.version = BinderCore::VERSION
|
8
|
+
s.authors = ["Ruaidhri Devery"]
|
9
|
+
s.email = ["info@fluid.ie"]
|
10
|
+
s.homepage = "https://github.com/rur/Binder-Core"
|
11
|
+
s.summary = %q{Folder content compiler}
|
12
|
+
s.description = %q{Ruby framework for compiling data from a file system}
|
13
|
+
|
14
|
+
s.rubyforge_project = "binder_core"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module BinderCore
|
2
|
+
class Asset
|
3
|
+
attr_accessor :file, :name, :base_url, :asset_url, :route
|
4
|
+
|
5
|
+
def initialize(path)
|
6
|
+
@file = BinderCore::FileRef.new path
|
7
|
+
self.name = file.name
|
8
|
+
@route = []
|
9
|
+
@base_url = ""
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_name(value)
|
13
|
+
self.name = value
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def name=(value)
|
18
|
+
raise ArgumentError, "Asset name for '#{file.path}' cannot be empty" if value.empty?
|
19
|
+
@name = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def feed_path
|
23
|
+
[ *route, name ].join("/")
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_route(route)
|
27
|
+
@route = route
|
28
|
+
@route.freeze
|
29
|
+
refresh_asset_url
|
30
|
+
route
|
31
|
+
end
|
32
|
+
|
33
|
+
def base_url=(url)
|
34
|
+
@base_url = url || ""
|
35
|
+
refresh_asset_url
|
36
|
+
url
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def refresh_asset_url
|
42
|
+
@asset_url = File.join( @base_url, [*@route,@file.full_name].join("/"))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BinderCore
|
2
|
+
class Binder
|
3
|
+
attr_reader :name, :config, :console
|
4
|
+
attr_accessor :raw
|
5
|
+
|
6
|
+
def initialize(name, user_config = nil)
|
7
|
+
@name = name.is_a?(Symbol) ? name : name.to_s
|
8
|
+
@config = lambda do |config|
|
9
|
+
user_config.call(config) if user_config
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def link_assets
|
14
|
+
data = @raw[:data]||{}
|
15
|
+
assets = @raw[:assets] || []
|
16
|
+
data = Compiler.link_assets(data)
|
17
|
+
|
18
|
+
{:data => data, :assets => assets.dup }
|
19
|
+
end
|
20
|
+
|
21
|
+
# :NODOC:
|
22
|
+
def init_console(console)
|
23
|
+
@console = console
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module BinderCore
|
2
|
+
class Compiler
|
3
|
+
# Compile a configured Binder object data by scanning files and folders using parsers
|
4
|
+
def self.compile(binder)
|
5
|
+
binder.init_console Console.new Settings.propegate
|
6
|
+
# assemble pre, default and user config procs into single compiler config proc
|
7
|
+
pre_config = lambda do |config|
|
8
|
+
config.set_console binder.console
|
9
|
+
config.set_path File.join( binder.console.defaults( :content_folder ), binder.name.to_s )
|
10
|
+
config.set_route []
|
11
|
+
config.set_name binder.name
|
12
|
+
end
|
13
|
+
default_config = BinderCore::Default.config
|
14
|
+
user_config = binder.config
|
15
|
+
compiler_config = lambda do |config|
|
16
|
+
pre_config.call(config)
|
17
|
+
default_config.call(config)
|
18
|
+
user_config.call(config)
|
19
|
+
config.verify_context
|
20
|
+
end
|
21
|
+
# use that compiler proc to trigger the parsing process
|
22
|
+
compiled = Scanner.scan compiler_config
|
23
|
+
binder.raw = { :data => compiled.data, :assets => compiled.assets }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Replace asset object values in the supplied data collection with their asset url
|
27
|
+
def self.link_assets(raw_data)
|
28
|
+
linker raw_data
|
29
|
+
end
|
30
|
+
|
31
|
+
# Configure the global compiler settings, all Binder compiler
|
32
|
+
# process will inherit from these settings.
|
33
|
+
#
|
34
|
+
# note: If you want to apply compiler settings on an individual Binder Definition
|
35
|
+
# you can do so in the BinderCore#define method using the config object
|
36
|
+
def self.configure(&block)
|
37
|
+
Default.settings
|
38
|
+
Settings.configure &block
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def self.linker(collection) #:nodoc:
|
44
|
+
dest = collection.dup
|
45
|
+
itr = Iterator.new dest
|
46
|
+
while itr.has_next?
|
47
|
+
value = itr.next
|
48
|
+
if value.is_a? Asset
|
49
|
+
itr.replace value.asset_url
|
50
|
+
elsif value.is_a? Array or value.is_a? Hash
|
51
|
+
itr.replace linker value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
dest
|
55
|
+
end
|
56
|
+
|
57
|
+
class Iterator #:nodoc:
|
58
|
+
def initialize(collection=nil)
|
59
|
+
init collection unless collection.nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
def init(collection)
|
63
|
+
@collection = collection
|
64
|
+
@keys = []
|
65
|
+
@que = []
|
66
|
+
@step = -1
|
67
|
+
|
68
|
+
if collection.is_a? Array
|
69
|
+
collection.length.times { |n| @keys << n }
|
70
|
+
@que = collection.dup
|
71
|
+
elsif collection.is_a? Hash
|
72
|
+
collection.keys.each do |key|
|
73
|
+
@keys << key
|
74
|
+
@que << collection[key]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_next?
|
80
|
+
@step < count-1
|
81
|
+
end
|
82
|
+
|
83
|
+
def count
|
84
|
+
@que.length
|
85
|
+
end
|
86
|
+
|
87
|
+
def next
|
88
|
+
@step += 1
|
89
|
+
@que[@step]
|
90
|
+
end
|
91
|
+
|
92
|
+
def replace(new_val)
|
93
|
+
@collection[@keys[@step]] = new_val
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module BinderCore
|
2
|
+
# This is used to configure both file and folder context objects, interchangeably
|
3
|
+
class Config
|
4
|
+
def initialize(context)
|
5
|
+
@context = context
|
6
|
+
end
|
7
|
+
|
8
|
+
def set_console(console)
|
9
|
+
@context.console = console
|
10
|
+
@console = console
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_path(path)
|
14
|
+
@context.file = BinderCore::FileRef.new path
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_rules(rules)
|
18
|
+
@context.rules += rules
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_parsers(parsers)
|
22
|
+
@context.parsers += parsers
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_parser( klass, &test )
|
26
|
+
defn = BinderCore::Definition.new(klass, test)
|
27
|
+
@context.parsers.unshift(defn)
|
28
|
+
defn
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_rule( klass, &test )
|
32
|
+
defn = BinderCore::Definition.new(klass, test)
|
33
|
+
@context.rules.unshift(defn)
|
34
|
+
defn
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_name(name)
|
38
|
+
@context.name = name if(@context.respond_to?(:name))
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_route(route)
|
42
|
+
@context.route = route
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_data(data)
|
46
|
+
@context.data = data
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_assets(assets)
|
50
|
+
@context.assets = assets
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_params(params)
|
54
|
+
@context.params.merge!(params)
|
55
|
+
end
|
56
|
+
|
57
|
+
def params
|
58
|
+
@context.params
|
59
|
+
end
|
60
|
+
|
61
|
+
def verify_context
|
62
|
+
unless valid_file?(@context.file.path) then raise InvalidContextError, "Invalid file at path:#{@context.file.path}" end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def valid_file?(path)
|
68
|
+
size_limit_mb = @console.defaults(:folder_size_limit_mb)
|
69
|
+
size_limit_bt = size_limit_mb.to_f*1024*1024
|
70
|
+
|
71
|
+
if !File.exists?(path) then error_message = "File path:#{path} does not exist"
|
72
|
+
elsif !File.readable?(path) then error_message = "You do not have permission to access the file at path:#{path}"
|
73
|
+
elsif !file_within_size_limit? size_limit_bt, path then error_message = "File contents at path:#{path} is larger then the limit of #{size_limit_mb}MB"
|
74
|
+
end
|
75
|
+
|
76
|
+
return true if error_message.nil?
|
77
|
+
# otherwise there was an error so...
|
78
|
+
@console.error error_message
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def file_within_size_limit?( limit, path )
|
83
|
+
def calculate_size(limit, file)
|
84
|
+
if File.directory? file
|
85
|
+
i = 0
|
86
|
+
Dir.open(file).each do |f|
|
87
|
+
i += calculate_size(limit, file + "/" + f).to_i unless f == '.' || f == '..'
|
88
|
+
break if i > limit
|
89
|
+
end
|
90
|
+
return i
|
91
|
+
else
|
92
|
+
return File.size?(file)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
calculate_size(limit, path) < limit
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "binder_core/debug/stack"
|
2
|
+
|
3
|
+
module BinderCore
|
4
|
+
# TODO: Document this
|
5
|
+
class Console
|
6
|
+
attr_reader :stack
|
7
|
+
|
8
|
+
def initialize(settings)
|
9
|
+
@settings = settings
|
10
|
+
@continue = true
|
11
|
+
@stack = Debug::Stack.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def defaults(param)
|
15
|
+
if param.is_a? String or param.is_a? Symbol then
|
16
|
+
return @settings.send(param) if @settings.prop_list.include? param.to_sym
|
17
|
+
end
|
18
|
+
param.keys.each do |key|
|
19
|
+
@settings.send "#{key}=", param[key] if @settings.prop_list.include? "#{key}=".to_sym
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def log(message = nil)
|
24
|
+
@log ||= []
|
25
|
+
return @log if message.nil?
|
26
|
+
@log.insert 0, message
|
27
|
+
end
|
28
|
+
|
29
|
+
def warn(message)
|
30
|
+
self.warnings.insert 0, message
|
31
|
+
end
|
32
|
+
|
33
|
+
def warnings
|
34
|
+
@warnings ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
def error(message)
|
38
|
+
self.errors.insert 0, :message => message, :stack => stack.trace
|
39
|
+
halt
|
40
|
+
end
|
41
|
+
|
42
|
+
def errors
|
43
|
+
@errors ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
def continue?
|
47
|
+
@continue
|
48
|
+
end
|
49
|
+
|
50
|
+
#:NODOC:
|
51
|
+
# Possible future feature: Figure out a way to allow the compilation process to be restarted
|
52
|
+
# This might be useful for some kind of debug-mode
|
53
|
+
# def continue
|
54
|
+
# @continue = true
|
55
|
+
# end
|
56
|
+
|
57
|
+
# Stop the compilation process
|
58
|
+
# NB: it cannot be restarted
|
59
|
+
def halt
|
60
|
+
@continue = false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# This handles a stack record during the compile phase
|
2
|
+
module BinderCore
|
3
|
+
module Debug
|
4
|
+
class Stack
|
5
|
+
attr_reader :route
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@entry_hash = {}
|
9
|
+
@route = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# FIXME: it will not properly register the root file context and parser
|
13
|
+
|
14
|
+
def add_folder(route,folder)
|
15
|
+
entry = create_entry route, folder.file.path, folder
|
16
|
+
@entry_hash[route.join("~")] = [entry]
|
17
|
+
self.route = route
|
18
|
+
entry
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_file(route,file)
|
22
|
+
entry = create_entry route, file.file.path, file
|
23
|
+
@entry_hash[route.join("~")] ||= []
|
24
|
+
@entry_hash[route.join("~")].insert 1, entry
|
25
|
+
self.route = route
|
26
|
+
entry
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_parser(route,parser)
|
30
|
+
entry = create_entry route, parser.file.path, parser
|
31
|
+
@entry_hash[route.join("~")] ||= []
|
32
|
+
@entry_hash[route.join("~")].insert 2, entry
|
33
|
+
self.route = route
|
34
|
+
entry
|
35
|
+
end
|
36
|
+
|
37
|
+
def trace
|
38
|
+
output = []
|
39
|
+
tmp = []
|
40
|
+
route.each do |step|
|
41
|
+
tmp << step
|
42
|
+
ky = tmp.join("~").to_s
|
43
|
+
output.concat @entry_hash[ky]
|
44
|
+
end
|
45
|
+
output.reverse
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def route=(route)
|
50
|
+
prune_entry_hash route
|
51
|
+
@route = route.dup
|
52
|
+
@route.freeze
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_entry(route,path,inst) #:NODOC:
|
56
|
+
entry = Entry.new
|
57
|
+
entry.route = route.dup
|
58
|
+
entry.file_path = path
|
59
|
+
entry.class_name = inst.class.to_s
|
60
|
+
entry.freeze
|
61
|
+
entry
|
62
|
+
end
|
63
|
+
|
64
|
+
def prune_entry_hash( route ) #:NODOC: to hide my shame ;)
|
65
|
+
pruned_hash = {}
|
66
|
+
tmp = []
|
67
|
+
route.each do |step|
|
68
|
+
tmp << step
|
69
|
+
ky = tmp.join("~").to_s
|
70
|
+
pruned_hash[ky] = @entry_hash[ky]
|
71
|
+
end
|
72
|
+
@entry_hash = pruned_hash
|
73
|
+
end
|
74
|
+
|
75
|
+
# This is created for every entry in the stack
|
76
|
+
# It holds the feed route, the file or folder path
|
77
|
+
# and the class that is dealing with it when the entry is created
|
78
|
+
class Entry
|
79
|
+
attr_accessor :route, :file_path, :class_name
|
80
|
+
def to_s
|
81
|
+
return "#{@class_name} at route:(#{@route.join(',')}) to file:#{@file_path}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "binder_core/parsers/null_parser"
|
2
|
+
require "binder_core/parsers/text_parser"
|
3
|
+
require "binder_core/parsers/folder_parser"
|
4
|
+
require "binder_core/parsers/asset_parser"
|
5
|
+
|
6
|
+
module BinderCore
|
7
|
+
module Default
|
8
|
+
def self.settings
|
9
|
+
Settings.configure do
|
10
|
+
config.base_asset_folder = ""
|
11
|
+
config.base_asset_url = ""
|
12
|
+
config.content_folder = ""
|
13
|
+
config.folder_size_limit_mb = 100
|
14
|
+
end
|
15
|
+
end
|
16
|
+
# this is the default config proc
|
17
|
+
def self.config
|
18
|
+
lambda do |config|
|
19
|
+
config.add_parser BinderCore::NullParser do |cxt|
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
config.add_parser BinderCore::TextParser do |cxt|
|
24
|
+
%w{.txt .html .htm .json}.include? cxt.file.ext.downcase
|
25
|
+
end
|
26
|
+
|
27
|
+
config.add_parser BinderCore::FolderParser do |cxt|
|
28
|
+
cxt.file.dir?
|
29
|
+
end
|
30
|
+
|
31
|
+
config.add_parser BinderCore::AssetParser do |cxt|
|
32
|
+
%w{.jpg .jpeg .png .gif}.include? cxt.file.ext.downcase
|
33
|
+
end
|
34
|
+
|
35
|
+
config.add_rule BinderCore::NullParser do |cxt|
|
36
|
+
cxt.file.name.start_with?(".","_")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module BinderCore
|
2
|
+
# this object is passed to parser definition test proc
|
3
|
+
# it is then wrapped by the obtained parser instance
|
4
|
+
class FileContext
|
5
|
+
|
6
|
+
attr_accessor :name, :file, :data, :parsers, :rules, :route, :assets, :console, :params
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@rules = []
|
10
|
+
@parsers = []
|
11
|
+
@route = []
|
12
|
+
@assets = []
|
13
|
+
@params = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def file=(file)
|
17
|
+
@file = file
|
18
|
+
@name = file.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_asset( new_asset )
|
22
|
+
new_asset.route = @route.dup
|
23
|
+
|
24
|
+
new_asset.base_url = console.defaults( :base_asset_url )
|
25
|
+
|
26
|
+
@assets << new_asset
|
27
|
+
new_asset
|
28
|
+
end
|
29
|
+
|
30
|
+
def descend(&user_config)
|
31
|
+
feed_config = lambda{ |config|
|
32
|
+
config.set_rules @rules
|
33
|
+
config.set_parsers @parsers
|
34
|
+
config.set_assets @assets
|
35
|
+
config.set_console console
|
36
|
+
config.add_params @params.dup
|
37
|
+
config.set_path file.path
|
38
|
+
config.set_route route.dup << self.name
|
39
|
+
user_config.call config
|
40
|
+
config.verify_context
|
41
|
+
}
|
42
|
+
|
43
|
+
BinderCore::Scanner.descend( feed_config )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|