higml 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +20 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +111 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/examples/affiliations.higml +4 -0
- data/examples/application.higml +18 -0
- data/examples/articles.higml +4 -0
- data/examples/contacts.higml +4 -0
- data/examples/homes.higml +2 -0
- data/examples/messages.higml +9 -0
- data/examples/pages.higml +9 -0
- data/examples/profiles.higml +17 -0
- data/examples/searches.higml +35 -0
- data/examples/simulate.higml +4 -0
- data/higml.gemspec +92 -0
- data/lib/higml/applier.rb +57 -0
- data/lib/higml/config.rb +54 -0
- data/lib/higml/node.rb +9 -0
- data/lib/higml/parser.rb +115 -0
- data/lib/higml/value.rb +21 -0
- data/lib/higml.rb +27 -0
- data/spec/fixtures/application.higml +1 -0
- data/spec/fixtures/higml-config.yml +3 -0
- data/spec/fixtures/search.higml +12 -0
- data/spec/fixtures/selectors_with_groups.higml +2 -0
- data/spec/fixtures/with_import.higml +2 -0
- data/spec/higml_spec.rb +35 -0
- data/spec/lib/higml/applier_spec.rb +44 -0
- data/spec/lib/higml/config_spec.rb +36 -0
- data/spec/lib/higml/parser_spec.rb +31 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +22 -0
- metadata +168 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", ">= 1.3.0"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.5.2"
|
12
|
+
gem "rcov", ">= 0"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
git (1.2.5)
|
5
|
+
jeweler (1.5.2)
|
6
|
+
bundler (~> 1.0.0)
|
7
|
+
git (>= 1.2.5)
|
8
|
+
rake
|
9
|
+
rake (0.8.7)
|
10
|
+
rcov (0.9.9)
|
11
|
+
rspec (1.3.1)
|
12
|
+
|
13
|
+
PLATFORMS
|
14
|
+
ruby
|
15
|
+
|
16
|
+
DEPENDENCIES
|
17
|
+
bundler (~> 1.0.0)
|
18
|
+
jeweler (~> 1.5.2)
|
19
|
+
rcov
|
20
|
+
rspec (>= 1.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Daniel Higginbotham
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
higml
|
2
|
+
=====
|
3
|
+
|
4
|
+
Overview
|
5
|
+
--------
|
6
|
+
|
7
|
+
Higml is a terse format for converting an input hash to an output hash.
|
8
|
+
|
9
|
+
Example Higml and input:
|
10
|
+
|
11
|
+
# Higml
|
12
|
+
action:show
|
13
|
+
:pageName Search Results
|
14
|
+
:keywords= keywords
|
15
|
+
|
16
|
+
filter
|
17
|
+
:filter_terms= filter_terms
|
18
|
+
|
19
|
+
# Input hash
|
20
|
+
{
|
21
|
+
:action => "show",
|
22
|
+
:filter => "any value"
|
23
|
+
}
|
24
|
+
|
25
|
+
This output is produced:
|
26
|
+
|
27
|
+
{
|
28
|
+
:pageName => "Search Results",
|
29
|
+
:keywords => ... #depends on value of "keywords" method in given context
|
30
|
+
:filter_terms => ... #depends on value of "filter_terms" method in given context
|
31
|
+
}
|
32
|
+
|
33
|
+
To get a better understand of _what_ Higml does, go through spec/higml_spec.rb and the corresponding .higml files in spec/fixtures.
|
34
|
+
|
35
|
+
Why would you want to use it? The original use case was generating a json object for analytics. The json object had different fields and values depending on what section of the site and individual page were being viewed. When viewing a profile, the json might be
|
36
|
+
|
37
|
+
{
|
38
|
+
pageName: "Profile: 4320",
|
39
|
+
section: "Profiles"
|
40
|
+
}
|
41
|
+
|
42
|
+
whereas search might look like
|
43
|
+
|
44
|
+
{
|
45
|
+
pageName: "Search: 'robert'",
|
46
|
+
section: "Search"
|
47
|
+
}
|
48
|
+
|
49
|
+
The actual json was much more complicated, containing about 20 keys. Writing the mapping between input and output in higml allowed my coworkers and I to quickly understand what the json would be for a given page, or why the json was the way it was. Example .higml files can be found in /examples.
|
50
|
+
|
51
|
+
Syntax
|
52
|
+
------
|
53
|
+
|
54
|
+
Example:
|
55
|
+
|
56
|
+
@import application.higml
|
57
|
+
action:show, action:new
|
58
|
+
:pageName Search Results
|
59
|
+
:keywords= keywords
|
60
|
+
|
61
|
+
q:rutabagas
|
62
|
+
:rutabagas true
|
63
|
+
|
64
|
+
filter
|
65
|
+
:filter_terms= filter_terms
|
66
|
+
|
67
|
+
<dl>
|
68
|
+
<dt>@import appication.higml</dt>
|
69
|
+
<dd>acts as if the contents of application.higml were located where @import is</dd>
|
70
|
+
<dt>action:show, action:new</dt>
|
71
|
+
<dd>If the input hash has a value of "show" _or_ "new" for the key :action, then use the mapping that follows</dd>
|
72
|
+
<dt>:pageName Search Results</dt>
|
73
|
+
<dd>Output hash has a value of "Search Results" for the :pageName key; constant string</dd>
|
74
|
+
<dt>:keywords= keywords</dt>
|
75
|
+
<dd>Value of :keywords in output hash is whatever "keywords" evaluates to in the Higml mapper_context; let's you have dynamic values</dd>
|
76
|
+
<dt>q:rutabagas</dt>
|
77
|
+
<dd>If input hash satisfies action:show _or_ action:new, _and_ it has a value of "rutabagas" for :q, then apply the mapping that follows</dd>
|
78
|
+
<dt>filter</dt>
|
79
|
+
<dd>If input hash has a key of :filter at all (regardless of :q but dependent on :action), then apply mapping that follows</dd>
|
80
|
+
</dl>
|
81
|
+
|
82
|
+
Config
|
83
|
+
------
|
84
|
+
Use Higml.config.x to set these
|
85
|
+
|
86
|
+
<dl>
|
87
|
+
<dt>config_directory</dt>
|
88
|
+
<dd>Where higml-config.yml is stored #this may be overkill :(</dd>
|
89
|
+
<dt>higml_directory</dt>
|
90
|
+
<dd>Where all the .higml files are stored</dd>
|
91
|
+
<dt>global_pairs</dt>
|
92
|
+
<dd>should be hash; used as base hash that result of mapping is merged on to</dd>
|
93
|
+
</dl>
|
94
|
+
|
95
|
+
Contributing to higml
|
96
|
+
---------------------
|
97
|
+
|
98
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
99
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
100
|
+
* Fork the project
|
101
|
+
* Start a feature/bugfix branch
|
102
|
+
* Commit and push until you are happy with your contribution
|
103
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
104
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
105
|
+
|
106
|
+
Copyright
|
107
|
+
---------
|
108
|
+
|
109
|
+
Copyright (c) 2010 Daniel Higginbotham. See LICENSE.txt for
|
110
|
+
further details.
|
111
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "higml"
|
16
|
+
gem.homepage = "http://github.com/flyingmachine/higml"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Higml is a terse format for converting an input hash to an output hash.}
|
19
|
+
gem.description = %Q{Higml is a terse format for converting a static input hash to a dynamic output hash.}
|
20
|
+
gem.email = "daniel@flyingmachinestudios.com"
|
21
|
+
gem.authors = ["Daniel Higginbotham"]
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :default => :spec
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "higml #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
:charSet UTF-8
|
2
|
+
:currencyCode USD
|
3
|
+
:linkLeaveQueryString= false
|
4
|
+
:linkTrackVars None
|
5
|
+
:linkTrackEvents None
|
6
|
+
|
7
|
+
:trackDownloadLinks= true
|
8
|
+
:trackExternalLinks= true
|
9
|
+
:trackInlineStats= true
|
10
|
+
:linkDownloadFileTypes zip,wav,mp3,mov,mpg,avi,doc,pdf,xls,ppt
|
11
|
+
:linkInternalFilters= "javascript:,#{request.host}"
|
12
|
+
|
13
|
+
:pidx= current_user.id
|
14
|
+
:skill_level= current_user_type
|
15
|
+
:site_name My Site
|
16
|
+
:session_id= request.session_options[:id]
|
17
|
+
:timestamp= Time.now.strftime("%Y/%m/%d %H:%M:%S %p")
|
18
|
+
:country= current_user_country
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@import application.higml
|
2
|
+
|
3
|
+
action:show
|
4
|
+
:channel Profile_PersonalInformation
|
5
|
+
:pageName= "Profile_PersonalInformation #{@member.id}"
|
6
|
+
|
7
|
+
viewing_own
|
8
|
+
:channel MyProfile_PersonalInformation
|
9
|
+
:pageName= "MyProfile_PersonalInformation #{@member.id}"
|
10
|
+
|
11
|
+
action:edit
|
12
|
+
:channel MyProfile_Edit
|
13
|
+
:pageName= "MyProfile_Edit #{@member.id}"
|
14
|
+
|
15
|
+
action:edit_preferences
|
16
|
+
:channel ManageSetting
|
17
|
+
:pageName= "ManageSetting #{current_user.id}"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
@import application.higml
|
2
|
+
|
3
|
+
action:show, action:new
|
4
|
+
:channel Search
|
5
|
+
:search_term= @parser.keywords
|
6
|
+
:search_count= total_search_results
|
7
|
+
:search_resource_type= @parser.search_type
|
8
|
+
:search_fields= omniture_search_fields
|
9
|
+
|
10
|
+
action:show
|
11
|
+
:pageName= "Saved Search #{params[:id]}"
|
12
|
+
:search_type saved
|
13
|
+
|
14
|
+
action:new
|
15
|
+
:pageName New Search
|
16
|
+
|
17
|
+
search_type
|
18
|
+
:pageName Performed Search
|
19
|
+
:search_type= omniture_search_type
|
20
|
+
|
21
|
+
filter
|
22
|
+
:filter_type= filter_types
|
23
|
+
:filter_term= filter_terms
|
24
|
+
:search_type filtered
|
25
|
+
|
26
|
+
action:create
|
27
|
+
:channel Search Saved
|
28
|
+
:pageName= "Search Saved #{@search.id}"
|
29
|
+
|
30
|
+
action:destroy
|
31
|
+
:channel Search Deleted
|
32
|
+
:pageName= "Search Deleted #{@search.id}"
|
33
|
+
|
34
|
+
search_origin
|
35
|
+
:search_origin= params[:search_origin]
|
data/higml.gemspec
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{higml}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Daniel Higginbotham"]
|
12
|
+
s.date = %q{2010-12-23}
|
13
|
+
s.description = %q{Higml is a terse format for converting a static input hash to a dynamic output hash.}
|
14
|
+
s.email = %q{daniel@flyingmachinestudios.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.markdown",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"examples/affiliations.higml",
|
29
|
+
"examples/application.higml",
|
30
|
+
"examples/articles.higml",
|
31
|
+
"examples/contacts.higml",
|
32
|
+
"examples/homes.higml",
|
33
|
+
"examples/messages.higml",
|
34
|
+
"examples/pages.higml",
|
35
|
+
"examples/profiles.higml",
|
36
|
+
"examples/searches.higml",
|
37
|
+
"examples/simulate.higml",
|
38
|
+
"higml.gemspec",
|
39
|
+
"lib/higml.rb",
|
40
|
+
"lib/higml/applier.rb",
|
41
|
+
"lib/higml/config.rb",
|
42
|
+
"lib/higml/node.rb",
|
43
|
+
"lib/higml/parser.rb",
|
44
|
+
"lib/higml/value.rb",
|
45
|
+
"spec/fixtures/application.higml",
|
46
|
+
"spec/fixtures/higml-config.yml",
|
47
|
+
"spec/fixtures/search.higml",
|
48
|
+
"spec/fixtures/selectors_with_groups.higml",
|
49
|
+
"spec/fixtures/with_import.higml",
|
50
|
+
"spec/higml_spec.rb",
|
51
|
+
"spec/lib/higml/applier_spec.rb",
|
52
|
+
"spec/lib/higml/config_spec.rb",
|
53
|
+
"spec/lib/higml/parser_spec.rb",
|
54
|
+
"spec/spec.opts",
|
55
|
+
"spec/spec_helper.rb"
|
56
|
+
]
|
57
|
+
s.homepage = %q{http://github.com/flyingmachine/higml}
|
58
|
+
s.licenses = ["MIT"]
|
59
|
+
s.require_paths = ["lib"]
|
60
|
+
s.rubygems_version = %q{1.3.7}
|
61
|
+
s.summary = %q{Higml is a terse format for converting an input hash to an output hash.}
|
62
|
+
s.test_files = [
|
63
|
+
"spec/higml_spec.rb",
|
64
|
+
"spec/lib/higml/applier_spec.rb",
|
65
|
+
"spec/lib/higml/config_spec.rb",
|
66
|
+
"spec/lib/higml/parser_spec.rb",
|
67
|
+
"spec/spec_helper.rb"
|
68
|
+
]
|
69
|
+
|
70
|
+
if s.respond_to? :specification_version then
|
71
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
|
+
s.specification_version = 3
|
73
|
+
|
74
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
75
|
+
s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
|
76
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
77
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
78
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
79
|
+
else
|
80
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
81
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
82
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
83
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
84
|
+
end
|
85
|
+
else
|
86
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
87
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
88
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
89
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Higml
|
2
|
+
class Applier
|
3
|
+
def initialize(input, tree, priority_map, mapper_context)
|
4
|
+
@input = input
|
5
|
+
@tree = tree
|
6
|
+
@mapper_context = mapper_context
|
7
|
+
@priority_map = priority_map
|
8
|
+
end
|
9
|
+
|
10
|
+
def result
|
11
|
+
@result = {}
|
12
|
+
apply_tree
|
13
|
+
Higml.config.global_pairs.merge@result.merge(@priority_map)
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply_tree
|
17
|
+
apply_node(@tree)
|
18
|
+
end
|
19
|
+
|
20
|
+
def apply_node(node)
|
21
|
+
if node.selector.nil? || selector_matches?(node.selector)
|
22
|
+
apply_values(node.values)
|
23
|
+
node.children.each do |child_node|
|
24
|
+
apply_node(child_node)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def apply_values(node_values)
|
30
|
+
node_values.each do |value|
|
31
|
+
next if priority_keys.include? value.key
|
32
|
+
if value.static?
|
33
|
+
@result[value.key] = value.raw_value
|
34
|
+
else
|
35
|
+
@result[value.key] = @mapper_context.instance_eval(value.raw_value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# REFACTOR to selector class
|
41
|
+
def selector_matches?(selector)
|
42
|
+
selector.any? do |group|
|
43
|
+
group.keys.all? do |key|
|
44
|
+
input_has_key?(key) && (@input[key] == group[key] || group[key].nil?)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def input_has_key?(key)
|
50
|
+
@input.has_key?(key) && (@input[key] == true || !@input[key].empty?)
|
51
|
+
end
|
52
|
+
|
53
|
+
def priority_keys
|
54
|
+
@priority_keys ||= @priority_map.keys
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/higml/config.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Higml
|
2
|
+
def self.config
|
3
|
+
@config ||= Config.instance
|
4
|
+
end
|
5
|
+
|
6
|
+
class Config
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
# higml_directory should be a string
|
10
|
+
# global_pairs should be hash; every result is merged to it
|
11
|
+
OPTIONS = %w{higml_directory global_pairs config_directory}
|
12
|
+
attr_accessor *OPTIONS.collect{|o|o.to_sym}
|
13
|
+
|
14
|
+
def config_path
|
15
|
+
File.expand_path(File.join(self.config_directory, "higml-config.yml"))
|
16
|
+
end
|
17
|
+
|
18
|
+
def global_pairs
|
19
|
+
@global_pairs ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(config)
|
23
|
+
case config
|
24
|
+
when Hash: set_with_hash(config)
|
25
|
+
when IO: set_with_io(config)
|
26
|
+
when String: set_with_string(config)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_with_hash(config)
|
31
|
+
OPTIONS.each do |option|
|
32
|
+
self.send("#{option}=", config[option]) if config[option]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# String should be either a filename or YAML
|
37
|
+
def set_with_string(config)
|
38
|
+
if File.exists?(config)
|
39
|
+
set_with_yaml(File.read(config))
|
40
|
+
else
|
41
|
+
set_with_yaml(config)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_with_io(config)
|
46
|
+
set_with_hash(YAML.load(config))
|
47
|
+
config.close
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_with_yaml(config)
|
51
|
+
set_with_hash(YAML.load(config))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/higml/node.rb
ADDED
data/lib/higml/parser.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Higml
|
2
|
+
class Parser
|
3
|
+
attr_accessor :source
|
4
|
+
|
5
|
+
def initialize(source)
|
6
|
+
@source = source
|
7
|
+
prepend_imports
|
8
|
+
end
|
9
|
+
|
10
|
+
def prepend_imports
|
11
|
+
already_imported = []
|
12
|
+
process_imports(@source, already_imported)
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_imports(current_source, already_imported)
|
16
|
+
current_source.each_line do |line|
|
17
|
+
line = Line.new(line)
|
18
|
+
if line.type == :import
|
19
|
+
filename = line.to_import
|
20
|
+
next if already_imported.include?(filename)
|
21
|
+
already_imported << filename
|
22
|
+
new_source = File.read(File.join(Higml.config.higml_directory, filename))
|
23
|
+
@source = new_source + "\n" + @source
|
24
|
+
process_imports(new_source, already_imported)
|
25
|
+
else
|
26
|
+
break
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_tree
|
32
|
+
current_node = Node.new
|
33
|
+
node_stack = [current_node]
|
34
|
+
indentation_level = 0
|
35
|
+
|
36
|
+
source.each_line do |line|
|
37
|
+
next if line.strip.empty?
|
38
|
+
line = Line.new(line)
|
39
|
+
|
40
|
+
case line.type
|
41
|
+
when :value
|
42
|
+
# TODO move this comment
|
43
|
+
# Every drop in indentation corresponds to a new node;
|
44
|
+
# It doesn't make sense to drop indentation level and
|
45
|
+
# define further values
|
46
|
+
current_node.values << line.to_value
|
47
|
+
when :selector
|
48
|
+
if line.indentation_level >= indentation_level
|
49
|
+
node_stack.push current_node
|
50
|
+
else
|
51
|
+
node_stack_to_pop = indentation_level - line.indentation_level - 1
|
52
|
+
node_stack.slice!(node_stack.size - node_stack_to_pop, node_stack.size)
|
53
|
+
end
|
54
|
+
current_node = Node.new
|
55
|
+
current_node.selector = line.to_selector
|
56
|
+
node_stack.last.children << current_node
|
57
|
+
else
|
58
|
+
next
|
59
|
+
end
|
60
|
+
indentation_level = line.indentation_level
|
61
|
+
end
|
62
|
+
node_stack[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class Line
|
67
|
+
VALUE_INDICATOR = 58
|
68
|
+
IMPORT_INDICATOR = 64
|
69
|
+
COMMENT_INDICATOR = 47
|
70
|
+
|
71
|
+
attr_reader :source, :stripped_source
|
72
|
+
|
73
|
+
def initialize(source)
|
74
|
+
@source = source
|
75
|
+
@stripped_source = source.strip
|
76
|
+
end
|
77
|
+
|
78
|
+
# Every two spaces is one indentation level
|
79
|
+
def indentation_level
|
80
|
+
spaces = (@source.size - @stripped_source.size)
|
81
|
+
spaces == 0 ? 0 : spaces / 2
|
82
|
+
end
|
83
|
+
|
84
|
+
def type
|
85
|
+
case @stripped_source[0]
|
86
|
+
when VALUE_INDICATOR: :value
|
87
|
+
when IMPORT_INDICATOR: :import
|
88
|
+
when COMMENT_INDICATOR: :comment
|
89
|
+
else
|
90
|
+
:selector
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_selector
|
95
|
+
@stripped_source.split(",").collect do |group|
|
96
|
+
selector = {}
|
97
|
+
group = group.strip
|
98
|
+
group.split(" ").each do |pair|
|
99
|
+
key, value = pair.split(":")
|
100
|
+
selector[key.to_sym] = value || nil
|
101
|
+
end
|
102
|
+
selector
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_value
|
107
|
+
Value.new(self)
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_import
|
111
|
+
/@import (.*)/.match(@stripped_source)[1]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/higml/value.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Higml
|
2
|
+
class Value
|
3
|
+
attr_accessor :type, :key, :raw_value
|
4
|
+
|
5
|
+
def initialize(line)
|
6
|
+
match = /:(.*?)(=?) (.*)$/.match(line.stripped_source)
|
7
|
+
@key = match[1].to_sym
|
8
|
+
@raw_value = match[3]
|
9
|
+
@type = match[2].empty? ? :static : :dynamic
|
10
|
+
end
|
11
|
+
|
12
|
+
def static?
|
13
|
+
@type == :static
|
14
|
+
end
|
15
|
+
|
16
|
+
def dynamic?
|
17
|
+
@type == :dynamic
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/higml.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'yaml'
|
3
|
+
require 'higml/applier'
|
4
|
+
require 'higml/config'
|
5
|
+
require 'higml/node'
|
6
|
+
require 'higml/parser'
|
7
|
+
require 'higml/value'
|
8
|
+
|
9
|
+
module Higml
|
10
|
+
class << self
|
11
|
+
def values_for(input, selector_config, priority_map, mapper_context)
|
12
|
+
tree = tree_for_selector_config(selector_config)
|
13
|
+
Higml::Applier.new(input, tree, priority_map, mapper_context).result
|
14
|
+
end
|
15
|
+
|
16
|
+
def tree_for_selector_config(selector_config)
|
17
|
+
case selector_config
|
18
|
+
when String
|
19
|
+
possible_file_path = File.join(Higml.config.higml_directory, selector_config)
|
20
|
+
selector_config = File.read(possible_file_path) if File.exists?(possible_file_path)
|
21
|
+
Higml::Parser.new(selector_config).to_tree
|
22
|
+
when Higml::Node
|
23
|
+
selector_config
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
:applicationName WOO Application
|
data/spec/higml_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "HIGML integration" do
|
4
|
+
before :each do
|
5
|
+
@input = {
|
6
|
+
:action => "show",
|
7
|
+
:filter => { :tags => "cat, shakespeare"}
|
8
|
+
}
|
9
|
+
@mapper_context = Context.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return the final values using input, higml filename, priority values" do
|
13
|
+
priority_map = {:keywords => "tongue paper"}
|
14
|
+
values = Higml.values_for(@input, 'search.higml', priority_map, @mapper_context)
|
15
|
+
|
16
|
+
values.should == {
|
17
|
+
:channel => "Search",
|
18
|
+
:pageName => "Search Results",
|
19
|
+
:keywords => priority_map[:keywords], # takes priority over @mapper_context.keywords
|
20
|
+
:filter_terms => @mapper_context.filter_terms
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should allow you to use an existing tree instead of an sc filename" do
|
25
|
+
tree = Higml::Parser.new(File.read(File.join(Higml.config.higml_directory, 'search.higml'))).to_tree
|
26
|
+
values = Higml.values_for(@input, tree, {}, @mapper_context)
|
27
|
+
|
28
|
+
values.should == {
|
29
|
+
:channel => "Search",
|
30
|
+
:pageName => "Search Results",
|
31
|
+
:keywords => @mapper_context.keywords,
|
32
|
+
:filter_terms => @mapper_context.filter_terms
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe "Higml::Applier" do
|
4
|
+
it "should use a tree to build a result from input" do
|
5
|
+
tree = Higml::Parser.new(File.read(File.join(Higml.config.higml_directory, 'search.higml'))).to_tree
|
6
|
+
|
7
|
+
input = {
|
8
|
+
:action => "show",
|
9
|
+
:filter => { :tags => "cat, shakespeare"}
|
10
|
+
}
|
11
|
+
|
12
|
+
priority_map = {:keywords => "tongue paper"}
|
13
|
+
mapper_context = Context.new
|
14
|
+
|
15
|
+
values = Higml::Applier.new(input, tree, priority_map, mapper_context).result
|
16
|
+
|
17
|
+
values.should == {
|
18
|
+
:channel => "Search",
|
19
|
+
:pageName => "Search Results",
|
20
|
+
:keywords => priority_map[:keywords],
|
21
|
+
:filter_terms => mapper_context.filter_terms
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should test all groups in a selector" do
|
26
|
+
tree = Higml::Parser.new(File.read(File.join(Higml.config.higml_directory, 'selectors_with_groups.higml'))).to_tree
|
27
|
+
|
28
|
+
input_group_1 = {
|
29
|
+
:action => "show",
|
30
|
+
:filter => { :tags => "cat, shakespeare"}
|
31
|
+
}
|
32
|
+
|
33
|
+
input_group_2 = {
|
34
|
+
:action => "new",
|
35
|
+
:filter => { :tags => "cat, shakespeare"}
|
36
|
+
}
|
37
|
+
|
38
|
+
priority_map = {}
|
39
|
+
|
40
|
+
mapper_context = Context.new
|
41
|
+
Higml::Applier.new(input_group_1, tree, priority_map, mapper_context).result.should == {:filter => "filtered!"}
|
42
|
+
Higml::Applier.new(input_group_2, tree, priority_map, mapper_context).result.should == {:filter => "filtered!"}
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe "Higml::Config" do
|
4
|
+
before :all do
|
5
|
+
Higml.config.config_directory = FIXTURE_DIRECTORY
|
6
|
+
end
|
7
|
+
before :each do
|
8
|
+
Higml.config.higml_directory = nil
|
9
|
+
Higml.config.global_pairs = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
after :all do
|
13
|
+
Higml.config.higml_directory = FIXTURE_DIRECTORY
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set the config from a yaml filename" do
|
17
|
+
Higml.config.set(Higml.config.config_path)
|
18
|
+
Higml.config.higml_directory.should == "dir"
|
19
|
+
Higml.config.global_pairs.should == {:keywords => :chassis}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set the config from a file" do
|
23
|
+
Higml.config.set(File.open(Higml.config.config_path))
|
24
|
+
Higml.config.higml_directory.should == "dir"
|
25
|
+
Higml.config.global_pairs.should == {:keywords => :chassis}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set the config from a hash" do
|
29
|
+
Higml.config.set({
|
30
|
+
"higml_directory" => "a",
|
31
|
+
"global_pairs" => {:k => :v}
|
32
|
+
})
|
33
|
+
Higml.config.higml_directory.should == "a"
|
34
|
+
Higml.config.global_pairs.should == {:k => :v}
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe "Higml::Parser" do
|
4
|
+
it "should build a node tree from a properly formatted file" do
|
5
|
+
tree = Higml::Parser.new(File.read(File.join(Higml.config.higml_directory, 'search.higml'))).to_tree
|
6
|
+
|
7
|
+
tree.values[0].key.should == :channel
|
8
|
+
tree.values[0].raw_value.should == "Search"
|
9
|
+
|
10
|
+
tree.children[0].selector.should == [{:action => "new"}]
|
11
|
+
tree.children[0].values[0].key.should == :pageName
|
12
|
+
tree.children[0].values[0].raw_value.should == "New Search"
|
13
|
+
|
14
|
+
tree.children[1].selector.should == [{:action => "show"}]
|
15
|
+
tree.children[1].values[0].key.should == :pageName
|
16
|
+
tree.children[1].values[0].raw_value.should == "Search Results"
|
17
|
+
tree.children[1].values[0].type.should == :static
|
18
|
+
|
19
|
+
tree.children[1].values[1].key.should == :keywords
|
20
|
+
tree.children[1].values[1].raw_value.should == "keywords"
|
21
|
+
tree.children[1].values[1].type.should == :dynamic
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should import values" do
|
25
|
+
tree = Higml::Parser.new(File.read(File.join(Higml.config.higml_directory, 'with_import.higml'))).to_tree
|
26
|
+
tree.values[0].key.should == :applicationName
|
27
|
+
tree.values[0].raw_value.should == "WOO Application"
|
28
|
+
tree.values[1].key.should == :test
|
29
|
+
tree.values[1].raw_value.should == "yes"
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
require 'higml'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/autorun'
|
7
|
+
|
8
|
+
FIXTURE_DIRECTORY = File.join(File.dirname(__FILE__), 'fixtures')
|
9
|
+
Higml.config.higml_directory = FIXTURE_DIRECTORY
|
10
|
+
|
11
|
+
Spec::Runner.configure do |config|
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
class Context
|
16
|
+
def keywords
|
17
|
+
"juice mirror"
|
18
|
+
end
|
19
|
+
def filter_terms
|
20
|
+
"cat shakespeare"
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: higml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 0.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Daniel Higginbotham
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-23 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :development
|
24
|
+
name: rspec
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 27
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 3
|
34
|
+
- 0
|
35
|
+
version: 1.3.0
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
prerelease: false
|
39
|
+
type: :development
|
40
|
+
name: bundler
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 23
|
47
|
+
segments:
|
48
|
+
- 1
|
49
|
+
- 0
|
50
|
+
- 0
|
51
|
+
version: 1.0.0
|
52
|
+
requirement: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
prerelease: false
|
55
|
+
type: :development
|
56
|
+
name: jeweler
|
57
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 7
|
63
|
+
segments:
|
64
|
+
- 1
|
65
|
+
- 5
|
66
|
+
- 2
|
67
|
+
version: 1.5.2
|
68
|
+
requirement: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
prerelease: false
|
71
|
+
type: :development
|
72
|
+
name: rcov
|
73
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirement: *id004
|
83
|
+
description: Higml is a terse format for converting a static input hash to a dynamic output hash.
|
84
|
+
email: daniel@flyingmachinestudios.com
|
85
|
+
executables: []
|
86
|
+
|
87
|
+
extensions: []
|
88
|
+
|
89
|
+
extra_rdoc_files:
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.markdown
|
92
|
+
files:
|
93
|
+
- .document
|
94
|
+
- .rspec
|
95
|
+
- Gemfile
|
96
|
+
- Gemfile.lock
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.markdown
|
99
|
+
- Rakefile
|
100
|
+
- VERSION
|
101
|
+
- examples/affiliations.higml
|
102
|
+
- examples/application.higml
|
103
|
+
- examples/articles.higml
|
104
|
+
- examples/contacts.higml
|
105
|
+
- examples/homes.higml
|
106
|
+
- examples/messages.higml
|
107
|
+
- examples/pages.higml
|
108
|
+
- examples/profiles.higml
|
109
|
+
- examples/searches.higml
|
110
|
+
- examples/simulate.higml
|
111
|
+
- higml.gemspec
|
112
|
+
- lib/higml.rb
|
113
|
+
- lib/higml/applier.rb
|
114
|
+
- lib/higml/config.rb
|
115
|
+
- lib/higml/node.rb
|
116
|
+
- lib/higml/parser.rb
|
117
|
+
- lib/higml/value.rb
|
118
|
+
- spec/fixtures/application.higml
|
119
|
+
- spec/fixtures/higml-config.yml
|
120
|
+
- spec/fixtures/search.higml
|
121
|
+
- spec/fixtures/selectors_with_groups.higml
|
122
|
+
- spec/fixtures/with_import.higml
|
123
|
+
- spec/higml_spec.rb
|
124
|
+
- spec/lib/higml/applier_spec.rb
|
125
|
+
- spec/lib/higml/config_spec.rb
|
126
|
+
- spec/lib/higml/parser_spec.rb
|
127
|
+
- spec/spec.opts
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
has_rdoc: true
|
130
|
+
homepage: http://github.com/flyingmachine/higml
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
requirements: []
|
157
|
+
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 1.3.7
|
160
|
+
signing_key:
|
161
|
+
specification_version: 3
|
162
|
+
summary: Higml is a terse format for converting an input hash to an output hash.
|
163
|
+
test_files:
|
164
|
+
- spec/higml_spec.rb
|
165
|
+
- spec/lib/higml/applier_spec.rb
|
166
|
+
- spec/lib/higml/config_spec.rb
|
167
|
+
- spec/lib/higml/parser_spec.rb
|
168
|
+
- spec/spec_helper.rb
|