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.
Files changed (58) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +19 -0
  5. data/README.markdown +86 -0
  6. data/Rakefile +1 -0
  7. data/binder_core.gemspec +24 -0
  8. data/lib/binder_core/asset.rb +45 -0
  9. data/lib/binder_core/binder.rb +26 -0
  10. data/lib/binder_core/compiler.rb +97 -0
  11. data/lib/binder_core/config.rb +98 -0
  12. data/lib/binder_core/console.rb +63 -0
  13. data/lib/binder_core/debug/stack.rb +86 -0
  14. data/lib/binder_core/defaults.rb +42 -0
  15. data/lib/binder_core/definition.rb +12 -0
  16. data/lib/binder_core/file_context.rb +46 -0
  17. data/lib/binder_core/file_ref.rb +38 -0
  18. data/lib/binder_core/folder_context.rb +55 -0
  19. data/lib/binder_core/parser.rb +82 -0
  20. data/lib/binder_core/parsers/asset_parser.rb +7 -0
  21. data/lib/binder_core/parsers/folder_parser.rb +7 -0
  22. data/lib/binder_core/parsers/null_parser.rb +6 -0
  23. data/lib/binder_core/parsers/text_parser.rb +11 -0
  24. data/lib/binder_core/registry.rb +45 -0
  25. data/lib/binder_core/scanner.rb +36 -0
  26. data/lib/binder_core/settings.rb +23 -0
  27. data/lib/binder_core/version.rb +3 -0
  28. data/lib/binder_core.rb +44 -0
  29. data/spec/binder_core/asset_spec.rb +64 -0
  30. data/spec/binder_core/binder_spec.rb +48 -0
  31. data/spec/binder_core/compiler_spec.rb +132 -0
  32. data/spec/binder_core/config_spec.rb +146 -0
  33. data/spec/binder_core/console_spec.rb +60 -0
  34. data/spec/binder_core/debug/stack_spec.rb +90 -0
  35. data/spec/binder_core/defaults_spec.rb +43 -0
  36. data/spec/binder_core/definition_spec.rb +20 -0
  37. data/spec/binder_core/file_context_spec.rb +79 -0
  38. data/spec/binder_core/file_ref_spec.rb +43 -0
  39. data/spec/binder_core/folder_context_spec.rb +104 -0
  40. data/spec/binder_core/parser_spec.rb +72 -0
  41. data/spec/binder_core/parsers/asset_parser_spec.rb +14 -0
  42. data/spec/binder_core/parsers/folder_parser_spec.rb +15 -0
  43. data/spec/binder_core/parsers/null_parser_spec.rb +9 -0
  44. data/spec/binder_core/parsers/text_parser_spec.rb +16 -0
  45. data/spec/binder_core/registry_spec.rb +61 -0
  46. data/spec/binder_core/scanner_spec.rb +79 -0
  47. data/spec/binder_core/settings_spec.rb +32 -0
  48. data/spec/binder_core_spec.rb +87 -0
  49. data/spec/spec_helper.rb +19 -0
  50. data/spec/support/content/test/_hidden_file.txt +1 -0
  51. data/spec/support/content/test/_hidden_folder/childofhiddenfolder.txt +1 -0
  52. data/spec/support/content/test/emptyfolder/_ignore.txt +1 -0
  53. data/spec/support/content/test/logo.png +0 -0
  54. data/spec/support/content/test/subfolder/subtext.txt +1 -0
  55. data/spec/support/content/test/testfile.txt +5 -0
  56. data/spec/support/content/test/unknown.tmp +1 -0
  57. data/spec/support/parsers/test_parsers.rb +20 -0
  58. metadata +147 -0
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ *.gem
2
+ .bundle
3
+ db/*.sqlite3*
4
+ log/*.log
5
+ *.log
6
+ /tmp/
7
+ doc/
8
+ *.swp
9
+ *~
10
+ .project
11
+ .DS_Store
12
+ Gemfile.lock
13
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --drb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in binder_core.gemspec
4
+ gemspec
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"
@@ -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,12 @@
1
+ module BinderCore
2
+ class Definition
3
+ def initialize klass, test
4
+ @klass = klass
5
+ @test = test
6
+ end
7
+
8
+ def test(context)
9
+ return @klass if @test.call(context) == true
10
+ end
11
+ end
12
+ 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