faml 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 12548082a8aaf67316ba96345010c92ded15aa41
4
- data.tar.gz: bf0e80233a7227814421df27fe15b8fe05271aad
3
+ metadata.gz: fbb7777d09caaf6c398326ad22eb8f86a6fb4f2b
4
+ data.tar.gz: f5ce8d0e1e6f7a0d43e1e153bdf47f2905e147a1
5
5
  SHA512:
6
- metadata.gz: 13d98c0fd7b3fa67895b874a99bc02606e3eb334be9d169038d975f86f09c7715f602a94d577ea63ef9ae7222795fbe7987555a6cfd8bd104216722ad6ef2765
7
- data.tar.gz: 2ac8f294c6bfe8eab1ace2a755f6f5eceff9fd0b59770653a0d8c321c300a876d09f105e7d4f09d7dd1a130015588ea10fb05bd689bd6b3adfb90ae844fa8d3c
6
+ metadata.gz: 963b5897db86f1fdb168725f38194b5a29ffc5d40b5d4ebae219b19bd4bc7c7b82fd06b1ca23c7636917c71a8a9407c415fcf695505e0b0bb4e3b5ebf1dc9a1b
7
+ data.tar.gz: 41b0e8980767ae9fad3dcec64dbda59dfbb8a2d40bad2cf3c7545072ac2168bad850511f89c7ce904c6fcfb98765f4a452147d0c4346b88c7354dd2b744dd0d1
data/.clang-format ADDED
@@ -0,0 +1,3 @@
1
+ Language: Cpp
2
+ BasedOnStyle: Google
3
+ Standard: C++03
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.7.0 (2015-11-30)
2
+ - Rewrite AttributeBuilder in C++
3
+ - It improves performance, memory usage, and readability
4
+
1
5
  ## 0.6.5 (2015-11-28)
2
6
  - Delete falsey values after merging all attributes
3
7
  - `%span(foo=true){foo: false}` now renders `<span></span>`, because old attributes have priority even if the value is falsey.
@@ -0,0 +1,345 @@
1
+ #include <ruby.h>
2
+ #include <ruby/version.h>
3
+ #include "houdini.h"
4
+ #include <algorithm>
5
+ #include <map>
6
+ #include <sstream>
7
+ #include <string>
8
+ #include <vector>
9
+
10
+ /* faml requires Ruby >= 2.0.0 */
11
+ /* RARRAY_AREF() is available since Ruby 2.1 */
12
+ #if RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR < 1
13
+ #define RARRAY_AREF(a, i) RARRAY_PTR(a)[i]
14
+ #endif
15
+ /* rb_utf8_str_new() is available since Ruby 2.2 */
16
+ #if RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR < 2
17
+ #include <ruby/encoding.h>
18
+ #define rb_utf8_str_new(ptr, len) rb_enc_str_new(ptr, len, rb_utf8_encoding())
19
+ #endif
20
+
21
+ #define FOREACH_FUNC(func) reinterpret_cast<int (*)(ANYARGS)>(func)
22
+
23
+ VALUE rb_mAttributeBuilder;
24
+ static ID id_flatten;
25
+
26
+ static inline std::string string_from_value(VALUE v) {
27
+ return std::string(RSTRING_PTR(v), RSTRING_LEN(v));
28
+ }
29
+
30
+ enum attribute_value_type {
31
+ ATTRIBUTE_TYPE_TRUE,
32
+ ATTRIBUTE_TYPE_FALSE,
33
+ ATTRIBUTE_TYPE_VALUE,
34
+ };
35
+
36
+ struct attribute_value {
37
+ attribute_value_type type_;
38
+ std::string str_;
39
+
40
+ attribute_value(attribute_value_type type) : type_(type) {}
41
+ attribute_value(const char* cstr, long len)
42
+ : type_(ATTRIBUTE_TYPE_VALUE), str_(cstr, len) {}
43
+ attribute_value(const std::string& str)
44
+ : type_(ATTRIBUTE_TYPE_VALUE), str_(str) {}
45
+
46
+ static attribute_value from_value(VALUE value) {
47
+ if (RB_TYPE_P(value, T_TRUE)) {
48
+ return attribute_value(ATTRIBUTE_TYPE_TRUE);
49
+ } else if (!RTEST(value)) {
50
+ return attribute_value(ATTRIBUTE_TYPE_FALSE);
51
+ } else {
52
+ value = rb_convert_type(value, T_STRING, "String", "to_s");
53
+ return attribute_value(string_from_value(value));
54
+ }
55
+ }
56
+ };
57
+
58
+ typedef std::map<std::string, attribute_value> attributes_type;
59
+
60
+ void upsert_attribute(attributes_type& m, const std::string& key,
61
+ const attribute_value& value) {
62
+ const std::pair<attributes_type::iterator, bool> r =
63
+ m.insert(std::make_pair(key, value));
64
+ if (!r.second) {
65
+ r.first->second = value;
66
+ }
67
+ }
68
+
69
+ struct attribute_holder {
70
+ std::vector<attribute_value> ids_, classes_;
71
+ attributes_type m_;
72
+
73
+ inline void upsert(const std::string& key, const attribute_value& value) {
74
+ return upsert_attribute(m_, key, value);
75
+ }
76
+ };
77
+
78
+ static VALUE to_value(const attributes_type& m) {
79
+ VALUE h = rb_hash_new();
80
+ for (attributes_type::const_iterator it = m.begin(); it != m.end(); ++it) {
81
+ VALUE k = rb_utf8_str_new(it->first.data(), it->first.size());
82
+ VALUE v = Qnil;
83
+ switch (it->second.type_) {
84
+ case ATTRIBUTE_TYPE_TRUE:
85
+ v = Qtrue;
86
+ break;
87
+ case ATTRIBUTE_TYPE_FALSE:
88
+ v = Qnil;
89
+ break;
90
+ case ATTRIBUTE_TYPE_VALUE:
91
+ v = rb_utf8_str_new(it->second.str_.data(), it->second.str_.size());
92
+ break;
93
+ }
94
+ rb_hash_aset(h, k, v);
95
+ }
96
+ return h;
97
+ }
98
+
99
+ static void concat_array_attribute(std::vector<attribute_value>& ary,
100
+ VALUE value) {
101
+ if (RB_TYPE_P(value, T_ARRAY)) {
102
+ value = rb_funcall(value, id_flatten, 0);
103
+ } else {
104
+ value = rb_Array(value);
105
+ }
106
+ const long len = RARRAY_LEN(value);
107
+ for (long i = 0; i < len; i++) {
108
+ ary.push_back(attribute_value::from_value(RARRAY_AREF(value, i)));
109
+ }
110
+ }
111
+
112
+ static attributes_type normalize_data(VALUE data);
113
+
114
+ static int normalize_data_i(VALUE key, VALUE value, VALUE arg) {
115
+ attributes_type* normalized = reinterpret_cast<attributes_type*>(arg);
116
+ key = rb_convert_type(key, T_STRING, "String", "to_s");
117
+ std::string key_ = string_from_value(key);
118
+ std::replace(key_.begin(), key_.end(), '_', '-');
119
+
120
+ if (RB_TYPE_P(value, T_HASH)) {
121
+ const attributes_type sub = normalize_data(value);
122
+ for (attributes_type::const_iterator it = sub.begin(); it != sub.end();
123
+ ++it) {
124
+ upsert_attribute(*normalized, key_ + "-" + it->first, it->second);
125
+ }
126
+ } else {
127
+ upsert_attribute(*normalized, key_, attribute_value::from_value(value));
128
+ }
129
+ return ST_CONTINUE;
130
+ }
131
+
132
+ static attributes_type normalize_data(VALUE data) {
133
+ Check_Type(data, T_HASH);
134
+ attributes_type m;
135
+ rb_hash_foreach(data, FOREACH_FUNC(normalize_data_i),
136
+ reinterpret_cast<VALUE>(&m));
137
+ return m;
138
+ }
139
+
140
+ static int merge_one_i(VALUE key, VALUE value, VALUE arg) {
141
+ attribute_holder* attributes = reinterpret_cast<attribute_holder*>(arg);
142
+
143
+ key = rb_convert_type(key, T_STRING, "String", "to_s");
144
+ const std::string key_ = string_from_value(key);
145
+ if (key_ == "class") {
146
+ concat_array_attribute(attributes->classes_, value);
147
+ } else if (key_ == "id") {
148
+ concat_array_attribute(attributes->ids_, value);
149
+ } else if (key_ == "data" && RB_TYPE_P(value, T_HASH)) {
150
+ const attributes_type data = normalize_data(value);
151
+ for (attributes_type::const_iterator it = data.begin(); it != data.end();
152
+ ++it) {
153
+ attributes->upsert("data-" + it->first, it->second);
154
+ }
155
+ } else {
156
+ attributes->upsert(key_, attribute_value::from_value(value));
157
+ }
158
+ return ST_CONTINUE;
159
+ }
160
+
161
+ static void merge_one(attribute_holder& attributes, VALUE h) {
162
+ Check_Type(h, T_HASH);
163
+ rb_hash_foreach(h, FOREACH_FUNC(merge_one_i),
164
+ reinterpret_cast<VALUE>(&attributes));
165
+ }
166
+
167
+ static void join_class_attribute(attribute_holder& attributes) {
168
+ const std::vector<attribute_value>& classes = attributes.classes_;
169
+ std::vector<std::string> ary;
170
+
171
+ for (std::vector<attribute_value>::const_iterator it = classes.begin();
172
+ it != classes.end(); ++it) {
173
+ switch (it->type_) {
174
+ case ATTRIBUTE_TYPE_FALSE:
175
+ break;
176
+ case ATTRIBUTE_TYPE_TRUE:
177
+ ary.push_back("true");
178
+ break;
179
+ case ATTRIBUTE_TYPE_VALUE:
180
+ size_t prev = 0, pos;
181
+ while ((pos = it->str_.find_first_of(' ', prev)) != std::string::npos) {
182
+ if (pos != prev) {
183
+ ary.push_back(std::string(it->str_, prev, pos - prev));
184
+ }
185
+ prev = pos + 1;
186
+ }
187
+ ary.push_back(std::string(it->str_, prev, it->str_.size() - prev));
188
+ break;
189
+ }
190
+ }
191
+ if (ary.empty()) {
192
+ return;
193
+ }
194
+
195
+ std::sort(ary.begin(), ary.end());
196
+ ary.erase(std::unique(ary.begin(), ary.end()), ary.end());
197
+ std::ostringstream oss;
198
+ for (std::vector<std::string>::const_iterator it = ary.begin();
199
+ it != ary.end(); ++it) {
200
+ if (it != ary.begin()) {
201
+ oss << ' ';
202
+ }
203
+ oss << *it;
204
+ }
205
+ attributes.upsert("class", attribute_value(oss.str()));
206
+ }
207
+
208
+ static void join_id_attribute(attribute_holder& attributes) {
209
+ const std::vector<attribute_value>& ids = attributes.ids_;
210
+ std::ostringstream oss;
211
+ bool first = true;
212
+
213
+ for (std::vector<attribute_value>::const_iterator it = ids.begin();
214
+ it != ids.end(); ++it) {
215
+ switch (it->type_) {
216
+ case ATTRIBUTE_TYPE_FALSE:
217
+ break;
218
+ case ATTRIBUTE_TYPE_TRUE:
219
+ if (!first) {
220
+ oss << '_';
221
+ }
222
+ oss << "true";
223
+ first = false;
224
+ break;
225
+ case ATTRIBUTE_TYPE_VALUE:
226
+ if (!first) {
227
+ oss << '_';
228
+ }
229
+ oss << it->str_;
230
+ first = false;
231
+ break;
232
+ }
233
+ }
234
+ if (first) {
235
+ return;
236
+ }
237
+
238
+ attributes.upsert("id", attribute_value(oss.str()));
239
+ }
240
+
241
+ static void delete_falsey_values(attributes_type& m) {
242
+ for (attributes_type::iterator it = m.begin(); it != m.end();) {
243
+ if (it->second.type_ == ATTRIBUTE_TYPE_FALSE) {
244
+ attributes_type::iterator jt = it;
245
+ ++it;
246
+ m.erase(jt);
247
+ } else {
248
+ ++it;
249
+ }
250
+ }
251
+ }
252
+
253
+ static attributes_type merge(VALUE object_ref, int argc, VALUE* argv) {
254
+ int i;
255
+ attribute_holder attributes;
256
+
257
+ for (i = 0; i < argc; i++) {
258
+ merge_one(attributes, argv[i]);
259
+ }
260
+ if (!NIL_P(object_ref)) {
261
+ merge_one(attributes, object_ref);
262
+ }
263
+
264
+ join_class_attribute(attributes);
265
+ join_id_attribute(attributes);
266
+ delete_falsey_values(attributes.m_);
267
+
268
+ return attributes.m_;
269
+ }
270
+
271
+ static void put_attribute(std::ostringstream& oss,
272
+ const std::string& attr_quote, const std::string& key,
273
+ const std::string& value) {
274
+ oss << " " << key << "=" << attr_quote;
275
+
276
+ gh_buf ob = GH_BUF_INIT;
277
+ if (houdini_escape_html0(&ob, (const uint8_t*)value.data(), value.size(),
278
+ 0)) {
279
+ oss << std::string(ob.ptr, ob.size);
280
+ gh_buf_free(&ob);
281
+ } else {
282
+ oss << value;
283
+ }
284
+ oss << attr_quote;
285
+ }
286
+
287
+ static void build_attribute(std::ostringstream& oss,
288
+ const std::string& attr_quote, int is_html,
289
+ const std::string& key,
290
+ const attribute_value& value) {
291
+ if (value.type_ == ATTRIBUTE_TYPE_TRUE) {
292
+ if (is_html) {
293
+ oss << ' ' << key;
294
+ } else {
295
+ put_attribute(oss, attr_quote, key, key);
296
+ }
297
+ } else {
298
+ put_attribute(oss, attr_quote, key, value.str_);
299
+ }
300
+ }
301
+
302
+ static VALUE m_build(int argc, VALUE* argv, RB_UNUSED_VAR(VALUE self)) {
303
+ VALUE object_ref;
304
+ int is_html;
305
+
306
+ rb_check_arity(argc, 3, UNLIMITED_ARGUMENTS);
307
+ Check_Type(argv[0], T_STRING);
308
+ const std::string attr_quote = string_from_value(argv[0]);
309
+ is_html = RTEST(argv[1]);
310
+ object_ref = argv[2];
311
+ const attributes_type attributes = merge(object_ref, argc - 3, argv + 3);
312
+
313
+ std::ostringstream oss;
314
+ for (attributes_type::const_iterator it = attributes.begin();
315
+ it != attributes.end(); ++it) {
316
+ build_attribute(oss, attr_quote, is_html, it->first, it->second);
317
+ }
318
+
319
+ const std::string str = oss.str();
320
+ return rb_utf8_str_new(str.data(), str.size());
321
+ }
322
+
323
+ static VALUE m_normalize_data(RB_UNUSED_VAR(VALUE self), VALUE data) {
324
+ return to_value(normalize_data(data));
325
+ }
326
+
327
+ static VALUE m_merge(int argc, VALUE* argv, RB_UNUSED_VAR(VALUE self)) {
328
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
329
+ return to_value(merge(argv[0], argc - 1, argv + 1));
330
+ }
331
+
332
+ extern "C" {
333
+ void Init_attribute_builder(void) {
334
+ VALUE mFaml = rb_define_module("Faml");
335
+ rb_mAttributeBuilder = rb_define_module_under(mFaml, "AttributeBuilder");
336
+ rb_define_singleton_method(rb_mAttributeBuilder, "build",
337
+ RUBY_METHOD_FUNC(m_build), -1);
338
+ rb_define_singleton_method(rb_mAttributeBuilder, "normalize_data",
339
+ RUBY_METHOD_FUNC(m_normalize_data), 1);
340
+ rb_define_singleton_method(rb_mAttributeBuilder, "merge",
341
+ RUBY_METHOD_FUNC(m_merge), -1);
342
+
343
+ id_flatten = rb_intern("flatten");
344
+ }
345
+ };
@@ -2,10 +2,11 @@
2
2
  require 'mkmf'
3
3
 
4
4
  $CFLAGS << ' -Wall -W'
5
+ $CXXFLAGS << ' -Wall -W -std=c++03'
5
6
  houdini_dir = File.expand_path('../../vendor/houdini', __dir__)
6
7
  $INCFLAGS << " -I#{houdini_dir}"
7
8
 
8
- $srcs = %w[attribute_builder.c]
9
+ $srcs = %w[attribute_builder.cc]
9
10
  %w[
10
11
  buffer.c
11
12
  houdini_html_e.c
@@ -1,7 +1,7 @@
1
1
  # Incompatibilities
2
2
  ## Versions
3
3
  - Haml 4.1.0.beta.1
4
- - Faml 0.6.5
4
+ - Faml 0.7.0
5
5
  - Hamlit 1.7.2
6
6
 
7
7
  ## Table of contents
data/lib/faml/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen-string-literal: true
2
2
  module Faml
3
- VERSION = '0.6.5'
3
+ VERSION = '0.7.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-28 00:00:00.000000000 Z
11
+ date: 2015-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -299,6 +299,7 @@ extensions:
299
299
  - ext/attribute_builder/extconf.rb
300
300
  extra_rdoc_files: []
301
301
  files:
302
+ - ".clang-format"
302
303
  - ".gitignore"
303
304
  - ".gitmodules"
304
305
  - ".rspec"
@@ -320,7 +321,7 @@ files:
320
321
  - benchmark/view.haml
321
322
  - benchmark/view.slim
322
323
  - bin/faml
323
- - ext/attribute_builder/attribute_builder.c
324
+ - ext/attribute_builder/attribute_builder.cc
324
325
  - ext/attribute_builder/extconf.rb
325
326
  - faml.gemspec
326
327
  - gemfiles/rails_4.0.gemfile
@@ -1,366 +0,0 @@
1
- #include <ruby.h>
2
- #include <ruby/encoding.h>
3
- #include <ruby/version.h>
4
- #include "houdini.h"
5
-
6
- #if (RUBY_API_VERSION_MAJOR > 2) || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 1)
7
- /* define nothing */
8
- #else
9
- # define RARRAY_AREF(a, i) RARRAY_PTR(a)[i]
10
- # define rb_ary_new_capa rb_ary_new2
11
- #endif
12
-
13
- #define FOREACH_FUNC(func) ((int (*)(ANYARGS))(func))
14
-
15
- VALUE rb_mAttributeBuilder;
16
- static ID id_keys, id_sort_bang, id_uniq_bang, id_merge_bang, id_flatten;
17
- static ID id_id, id_class, id_underscore, id_hyphen, id_space, id_equal;
18
-
19
- static void
20
- concat_array_attribute(VALUE attributes, VALUE hash, VALUE key)
21
- {
22
- VALUE v;
23
-
24
- Check_Type(hash, T_HASH);
25
- v = rb_hash_delete(hash, key);
26
- if (!NIL_P(v)) {
27
- VALUE ary;
28
-
29
- if (RB_TYPE_P(v, T_ARRAY)) {
30
- v = rb_funcall(v, id_flatten, 0);
31
- } else {
32
- v = rb_Array(v);
33
- }
34
- ary = rb_hash_lookup(attributes, key);
35
- Check_Type(ary, T_ARRAY);
36
- rb_ary_concat(ary, v);
37
- }
38
- }
39
-
40
- static int
41
- stringify_keys_i(VALUE key, VALUE value, VALUE arg)
42
- {
43
- key = rb_convert_type(key, T_STRING, "String", "to_s");
44
- rb_hash_aset(arg, key, value);
45
- return ST_CONTINUE;
46
- }
47
-
48
- static VALUE
49
- stringify_keys(VALUE hash)
50
- {
51
- VALUE h = rb_hash_new();
52
- rb_hash_foreach(hash, FOREACH_FUNC(stringify_keys_i), h);
53
- return h;
54
- }
55
-
56
- struct normalize_data_i2_arg {
57
- VALUE key, normalized;
58
- };
59
-
60
- static VALUE
61
- substitute_underscores(VALUE str)
62
- {
63
- int frozen;
64
- long i, len;
65
-
66
- /* gsub('_', '-') */
67
- Check_Type(str, T_STRING);
68
- len = RSTRING_LEN(str);
69
- frozen = OBJ_FROZEN(str);
70
- for (i = 0; i < len; i++) {
71
- if (RSTRING_PTR(str)[i] == '_') {
72
- if (frozen) {
73
- str = rb_str_dup(str);
74
- frozen = 0;
75
- }
76
- rb_str_update(str, i, 1, rb_const_get(rb_mAttributeBuilder, id_hyphen));
77
- }
78
- }
79
-
80
- return str;
81
- }
82
-
83
- static int
84
- normalize_data_i2(VALUE key, VALUE value, VALUE ptr)
85
- {
86
- struct normalize_data_i2_arg *arg = (struct normalize_data_i2_arg *)ptr;
87
- VALUE k = rb_str_dup(arg->key);
88
-
89
- rb_str_cat(k, "-", 1);
90
- rb_str_append(k, key);
91
- rb_hash_aset(arg->normalized, k, value);
92
- return ST_CONTINUE;
93
- }
94
-
95
- static VALUE normalize_data(VALUE data);
96
-
97
- static int
98
- normalize_data_i(VALUE key, VALUE value, VALUE normalized)
99
- {
100
- key = rb_convert_type(key, T_STRING, "String", "to_s");
101
- key = substitute_underscores(key);
102
-
103
- if (RB_TYPE_P(value, T_HASH)) {
104
- struct normalize_data_i2_arg arg;
105
- arg.key = key;
106
- arg.normalized = normalized;
107
- rb_hash_foreach(normalize_data(value), FOREACH_FUNC(normalize_data_i2), (VALUE)(&arg));
108
- } else if (RB_TYPE_P(value, T_TRUE) || !RTEST(value)) {
109
- /* Keep Qtrue and falsey value */
110
- rb_hash_aset(normalized, key, value);
111
- } else {
112
- rb_hash_aset(normalized, key, rb_convert_type(value, T_STRING, "String", "to_s"));
113
- }
114
- return ST_CONTINUE;
115
- }
116
-
117
- static VALUE
118
- normalize_data(VALUE data)
119
- {
120
- VALUE normalized;
121
-
122
- Check_Type(data, T_HASH);
123
- normalized = rb_hash_new();
124
- rb_hash_foreach(data, FOREACH_FUNC(normalize_data_i), normalized);
125
- return normalized;
126
- }
127
-
128
- static int
129
- put_data_attribute(VALUE key, VALUE val, VALUE hash)
130
- {
131
- VALUE k = rb_str_buf_new(5 + RSTRING_LEN(key));
132
- rb_str_buf_cat(k, "data-", 5);
133
- rb_str_buf_append(k, key);
134
- rb_hash_aset(hash, k, val);
135
- return ST_CONTINUE;
136
- }
137
-
138
- static void
139
- normalize(VALUE hash)
140
- {
141
- VALUE keys = rb_funcall(hash, id_keys, 0);
142
- const long len = RARRAY_LEN(keys);
143
- long i;
144
- for (i = 0; i < len; i++) {
145
- VALUE key = RARRAY_AREF(keys, i);
146
- VALUE value = rb_hash_lookup(hash, key);
147
-
148
- /* key must be String because it is already stringified by stringify_keys */
149
- Check_Type(key, T_STRING);
150
- if (RB_TYPE_P(value, T_HASH) && RSTRING_LEN(key) == 4 && memcmp(RSTRING_PTR(key), "data", 4) == 0) {
151
- VALUE data;
152
-
153
- rb_hash_delete(hash, key);
154
- data = normalize_data(value);
155
- rb_hash_foreach(data, FOREACH_FUNC(put_data_attribute), hash);
156
- } else if (RB_TYPE_P(value, T_TRUE) || !RTEST(value)) {
157
- /* Keep Qtrue and falsey value */
158
- } else {
159
- rb_hash_aset(hash, key, rb_convert_type(value, T_STRING, "String", "to_s"));
160
- }
161
- }
162
- }
163
-
164
- static void
165
- merge_one(VALUE attributes, VALUE arg)
166
- {
167
- VALUE h;
168
-
169
- Check_Type(arg, T_HASH);
170
- h = stringify_keys(arg);
171
- concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_class));
172
- concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_id));
173
- normalize(h);
174
- rb_funcall(attributes, id_merge_bang, 1, h);
175
- }
176
-
177
- static void
178
- join_class_attribute(VALUE attributes, VALUE key)
179
- {
180
- long len;
181
- VALUE val;
182
-
183
- val = rb_hash_delete(attributes, key);
184
- Check_Type(val, T_ARRAY);
185
- len = RARRAY_LEN(val);
186
- if (len != 0) {
187
- long i;
188
- VALUE ary = rb_ary_new_capa(len);
189
- for (i = 0; i < len; i++) {
190
- VALUE v = RARRAY_AREF(val, i);
191
- if (RTEST(v)) {
192
- rb_ary_concat(ary, rb_str_split(rb_convert_type(v, T_STRING, "String", "to_s"), " "));
193
- }
194
- }
195
- rb_funcall(ary, id_sort_bang, 0);
196
- rb_funcall(ary, id_uniq_bang, 0);
197
- rb_hash_aset(attributes, key, rb_ary_join(ary, rb_const_get(rb_mAttributeBuilder, id_space)));
198
- }
199
- }
200
-
201
- static void
202
- join_id_attribute(VALUE attributes, VALUE key)
203
- {
204
- long len;
205
- VALUE val;
206
-
207
- val = rb_hash_delete(attributes, key);
208
- Check_Type(val, T_ARRAY);
209
- len = RARRAY_LEN(val);
210
- if (len != 0) {
211
- long i;
212
- VALUE ary = rb_ary_new_capa(len);
213
- for (i = 0; i < len; i++) {
214
- VALUE v = RARRAY_AREF(val, i);
215
- if (RTEST(v)) {
216
- rb_ary_push(ary, rb_convert_type(v, T_STRING, "String", "to_s"));
217
- }
218
- }
219
- rb_hash_aset(attributes, key, rb_ary_join(ary, rb_const_get(rb_mAttributeBuilder, id_underscore)));
220
- }
221
- }
222
-
223
- static int
224
- delete_falsey_values_i(RB_UNUSED_VAR(VALUE key), VALUE value, RB_UNUSED_VAR(VALUE arg))
225
- {
226
- if (RTEST(value)) {
227
- return ST_CONTINUE;
228
- } else {
229
- return ST_DELETE;
230
- }
231
- }
232
-
233
- static void
234
- delete_falsey_values(VALUE attributes)
235
- {
236
- rb_hash_foreach(attributes, FOREACH_FUNC(delete_falsey_values_i), Qnil);
237
- }
238
-
239
- static VALUE
240
- merge(VALUE object_ref, int argc, VALUE *argv)
241
- {
242
- VALUE attributes, id_str, class_str;
243
- int i;
244
-
245
- attributes = rb_hash_new();
246
- id_str = rb_const_get(rb_mAttributeBuilder, id_id);
247
- class_str = rb_const_get(rb_mAttributeBuilder, id_class);
248
- rb_hash_aset(attributes, id_str, rb_ary_new());
249
- rb_hash_aset(attributes, class_str, rb_ary_new());
250
-
251
- for (i = 0; i < argc; i++) {
252
- merge_one(attributes, argv[i]);
253
- }
254
- if (!NIL_P(object_ref)) {
255
- merge_one(attributes, object_ref);
256
- }
257
-
258
- join_class_attribute(attributes, class_str);
259
- join_id_attribute(attributes, id_str);
260
- delete_falsey_values(attributes);
261
-
262
- return attributes;
263
- }
264
-
265
- static void
266
- put_attribute(VALUE buf, VALUE attr_quote, VALUE key, VALUE value)
267
- {
268
- gh_buf ob = GH_BUF_INIT;
269
-
270
- Check_Type(value, T_STRING);
271
- if (houdini_escape_html0(&ob, (const uint8_t *)RSTRING_PTR(value), RSTRING_LEN(value), 0)) {
272
- value = rb_enc_str_new(ob.ptr, ob.size, rb_utf8_encoding());
273
- gh_buf_free(&ob);
274
- }
275
-
276
- rb_ary_push(buf, rb_const_get(rb_mAttributeBuilder, id_space));
277
- rb_ary_push(buf, key);
278
- rb_ary_push(buf, rb_const_get(rb_mAttributeBuilder, id_equal));
279
- rb_ary_push(buf, attr_quote);
280
- rb_ary_push(buf, value);
281
- rb_ary_push(buf, attr_quote);
282
- }
283
-
284
- static void
285
- build_attribute(VALUE buf, VALUE attr_quote, int is_html, VALUE key, VALUE value)
286
- {
287
- Check_Type(key, T_STRING);
288
- if (RB_TYPE_P(value, T_TRUE)) {
289
- if (is_html) {
290
- rb_ary_push(buf, rb_const_get(rb_mAttributeBuilder, id_space));
291
- rb_ary_push(buf, key);
292
- } else {
293
- put_attribute(buf, attr_quote, key, key);
294
- }
295
- } else {
296
- put_attribute(buf, attr_quote, key, value);
297
- }
298
- }
299
-
300
- static VALUE
301
- m_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
302
- {
303
- VALUE attr_quote, object_ref, attributes, keys, buf;
304
- int is_html;
305
- long len, i;
306
-
307
- rb_check_arity(argc, 3, UNLIMITED_ARGUMENTS);
308
- attr_quote = argv[0];
309
- is_html = RTEST(argv[1]);
310
- object_ref = argv[2];
311
- attributes = merge(object_ref, argc-3, argv+3);
312
-
313
- keys = rb_funcall(attributes, id_keys, 0);
314
- rb_funcall(keys, id_sort_bang, 0);
315
- len = RARRAY_LEN(keys);
316
- buf = rb_ary_new();
317
- for (i = 0; i < len; i++) {
318
- VALUE k = RARRAY_AREF(keys, i);
319
- build_attribute(buf, attr_quote, is_html, k, rb_hash_lookup(attributes, k));
320
- }
321
-
322
- return rb_ary_join(buf, Qnil);
323
- }
324
-
325
- static VALUE
326
- m_normalize_data(RB_UNUSED_VAR(VALUE self), VALUE data)
327
- {
328
- return normalize_data(data);
329
- }
330
-
331
- static VALUE
332
- m_merge(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
333
- {
334
- rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
335
- return merge(argv[0], argc-1, argv+1);
336
- }
337
-
338
- void
339
- Init_attribute_builder(void)
340
- {
341
- VALUE mFaml = rb_define_module("Faml");
342
- rb_mAttributeBuilder = rb_define_module_under(mFaml, "AttributeBuilder");
343
- rb_define_singleton_method(rb_mAttributeBuilder, "build", RUBY_METHOD_FUNC(m_build), -1);
344
- rb_define_singleton_method(rb_mAttributeBuilder, "normalize_data", RUBY_METHOD_FUNC(m_normalize_data), 1);
345
- rb_define_singleton_method(rb_mAttributeBuilder, "merge", RUBY_METHOD_FUNC(m_merge), -1);
346
-
347
- id_keys = rb_intern("keys");
348
- id_sort_bang = rb_intern("sort!");
349
- id_uniq_bang = rb_intern("uniq!");
350
- id_merge_bang = rb_intern("merge!");
351
- id_flatten = rb_intern("flatten");
352
-
353
- id_id = rb_intern("ID");
354
- id_class = rb_intern("CLASS");
355
- id_underscore = rb_intern("UNDERSCORE");
356
- id_hyphen = rb_intern("HYPHEN");
357
- id_space = rb_intern("SPACE");
358
- id_equal = rb_intern("EQUAL");
359
-
360
- rb_const_set(rb_mAttributeBuilder, id_id, rb_obj_freeze(rb_str_new_cstr("id")));
361
- rb_const_set(rb_mAttributeBuilder, id_class, rb_obj_freeze(rb_str_new_cstr("class")));
362
- rb_const_set(rb_mAttributeBuilder, id_underscore, rb_obj_freeze(rb_str_new_cstr("_")));
363
- rb_const_set(rb_mAttributeBuilder, id_hyphen, rb_obj_freeze(rb_str_new_cstr("-")));
364
- rb_const_set(rb_mAttributeBuilder, id_space, rb_obj_freeze(rb_str_new_cstr(" ")));
365
- rb_const_set(rb_mAttributeBuilder, id_equal, rb_obj_freeze(rb_str_new_cstr("=")));
366
- }